| author | |
| committer | |
| log | 5bf19f61ff5fe80a8f9aad5ce55812bd7e673fd0 |
| tree | 5cf57fe99e767cfbfa827263d308f23616271232 |
| parent | 5f74e4f3f8b909835ef794253a98a77868b3880e |
| parent | d697d97a95688e873d2677367e94010cdaa3ac73 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36147
Reviewed-by: Andrew Kelley <andrew@ziglang.org>16 files changed, 958 insertions(+), 375 deletions(-)
lib/compiler/Maker.zig+272-44| ... | @@ -11,6 +11,7 @@ const File = std.Io.File; | ... | @@ -11,6 +11,7 @@ const File = std.Io.File; |
| 11 | const Io = std.Io; | 11 | const Io = std.Io; |
| 12 | const Dir = std.Io.Dir; | 12 | const Dir = std.Io.Dir; |
| 13 | const Path = std.Build.Cache.Path; | 13 | const Path = std.Build.Cache.Path; |
| 14 | const Reader = std.Io.Reader; | ||
| 14 | const Writer = std.Io.Writer; | 15 | const Writer = std.Io.Writer; |
| 15 | const assert = std.debug.assert; | 16 | const assert = std.debug.assert; |
| 16 | const fatal = std.process.fatal; | 17 | const fatal = std.process.fatal; |
| ... | @@ -19,6 +20,8 @@ const log = std.log; | ... | @@ -19,6 +20,8 @@ const log = std.log; |
| 19 | const mem = std.mem; | 20 | const mem = std.mem; |
| 20 | const process = std.process; | 21 | const process = std.process; |
| 21 | const Color = std.zig.Color; | 22 | const Color = std.zig.Color; |
| 23 | const Client = std.zig.Client; | ||
| 24 | const Server = std.zig.Server; | ||
| 22 | const EnvVar = std.zig.EnvVar; | 25 | const EnvVar = std.zig.EnvVar; |
| 23 | const default_local_zig_cache_basename = std.zig.default_local_zig_cache_basename; | 26 | const default_local_zig_cache_basename = std.zig.default_local_zig_cache_basename; |
| 24 | const stringToEnum = std.meta.stringToEnum; | 27 | const stringToEnum = std.meta.stringToEnum; |
| ... | @@ -51,10 +54,14 @@ max_rss_mutex: Io.Mutex, | ... | @@ -51,10 +54,14 @@ max_rss_mutex: Io.Mutex, |
| 51 | skip_oom_steps: bool, | 54 | skip_oom_steps: bool, |
| 52 | unit_test_timeout_ns: ?u64, | 55 | unit_test_timeout_ns: ?u64, |
| 53 | watch: bool, | 56 | watch: bool, |
| 57 | protocol_server: ?*AvoidableServer, | ||
| 58 | protocol_server_mutex: Io.Mutex, | ||
| 54 | web_server: ?*AvoidableWebServer, | 59 | web_server: ?*AvoidableWebServer, |
| 55 | /// Allocated into `gpa`. | 60 | /// Allocated into `gpa`. |
| 56 | memory_blocked_steps: std.ArrayList(Configuration.Step.Index), | 61 | memory_blocked_steps: std.ArrayList(Configuration.Step.Index), |
| 57 | /// Allocated into `gpa`. | 62 | /// Allocated into `gpa`. |
| 63 | initial_steps: std.array_hash_map.Auto(Configuration.Step.Index, void), | ||
| 64 | /// Allocated into `gpa`. | ||
| 58 | step_stack: std.array_hash_map.Auto(Configuration.Step.Index, void), | 65 | step_stack: std.array_hash_map.Auto(Configuration.Step.Index, void), |
| 59 | pkg_config: PkgConfig, | 66 | pkg_config: PkgConfig, |
| 60 | 67 | ||
| ... | @@ -67,6 +74,7 @@ var stdio_buffer_allocation: [256]u8 = undefined; | ... | @@ -67,6 +74,7 @@ var stdio_buffer_allocation: [256]u8 = undefined; |
| 67 | var stdout_writer_allocation: Io.File.Writer = undefined; | 74 | var stdout_writer_allocation: Io.File.Writer = undefined; |
| 68 | var debug_maker_leaks: bool = false; | 75 | var debug_maker_leaks: bool = false; |
| 69 | 76 | ||
| 77 | const AvoidableServer = if (builtin.single_threaded) void else Server; | ||
| 70 | const AvoidableWebServer = if (builtin.single_threaded) void else WebServer; | 78 | const AvoidableWebServer = if (builtin.single_threaded) void else WebServer; |
| 71 | 79 | ||
| 72 | const is_debug_mode = builtin.mode == .debug; | 80 | const is_debug_mode = builtin.mode == .debug; |
| ... | @@ -216,6 +224,7 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -216,6 +224,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 216 | var watch = false; | 224 | var watch = false; |
| 217 | var fuzz: ?Fuzz.Mode = null; | 225 | var fuzz: ?Fuzz.Mode = null; |
| 218 | var debounce_interval_ms: u16 = 50; | 226 | var debounce_interval_ms: u16 = 50; |
| 227 | var listen: bool = false; | ||
| 219 | var webui_listen: ?Io.net.IpAddress = null; | 228 | var webui_listen: ?Io.net.IpAddress = null; |
| 220 | var debug_pkg_config = false; | 229 | var debug_pkg_config = false; |
| 221 | var run_args: ?[]const []const u8 = null; | 230 | var run_args: ?[]const []const u8 = null; |
| ... | @@ -422,6 +431,8 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -422,6 +431,8 @@ pub fn main(init: process.Init.Minimal) !void { |
| 422 | next_arg, err, | 431 | next_arg, err, |
| 423 | }); | 432 | }); |
| 424 | }; | 433 | }; |
| 434 | } else if (mem.eql(u8, arg, "--listen=-")) { | ||
| 435 | listen = true; | ||
| 425 | } else if (mem.eql(u8, arg, "--webui")) { | 436 | } else if (mem.eql(u8, arg, "--webui")) { |
| 426 | if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) }; | 437 | if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) }; |
| 427 | } else if (mem.startsWith(u8, arg, "--webui=")) { | 438 | } else if (mem.startsWith(u8, arg, "--webui=")) { |
| ... | @@ -559,7 +570,7 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -559,7 +570,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 559 | } | 570 | } |
| 560 | 571 | ||
| 561 | const early_exit_mode = fetch_only or help_menu or steps_menu or print_configuration != .none; | 572 | const early_exit_mode = fetch_only or help_menu or steps_menu or print_configuration != .none; |
| 562 | const server_mode = !early_exit_mode and (watch or webui_listen != null or fuzz != null); | 573 | const server_mode = !early_exit_mode and (watch or webui_listen != null or fuzz != null or listen); |
| 563 | 574 | ||
| 564 | process.raiseFileDescriptorLimit(); | 575 | process.raiseFileDescriptorLimit(); |
| 565 | 576 | ||
| ... | @@ -667,6 +678,25 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -667,6 +678,25 @@ pub fn main(init: process.Init.Minimal) !void { |
| 667 | break :ws &web_server_allocation; | 678 | break :ws &web_server_allocation; |
| 668 | } else null; | 679 | } else null; |
| 669 | 680 | ||
| 681 | var stdin_buffer: [256]u8 = undefined; | ||
| 682 | var stdout_buffer: [256]u8 = undefined; | ||
| 683 | var stdin_reader = Io.File.stdin().reader(io, &stdin_buffer); | ||
| 684 | var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); | ||
| 685 | |||
| 686 | var protocol_server_allocation: AvoidableServer = undefined; | ||
| 687 | const protocol_server: ?*AvoidableServer = if (listen) s: { | ||
| 688 | if (builtin.single_threaded) fatal("--listen is not yet supported on single-threaded hosts", .{}); | ||
| 689 | if (watch) fatal("using '--watch' and '--listen' together is not supported", .{}); | ||
| 690 | if (fuzz != null) fatal("using '--fuzz' and '--listen' together is not supported", .{}); | ||
| 691 | if (step_names.items.len > 0) fatal("build steps must be provided over the protocol instead of using CLI arguments", .{}); | ||
| 692 | protocol_server_allocation = .{ | ||
| 693 | .in = &stdin_reader.interface, | ||
| 694 | .out = &stdout_writer.interface, | ||
| 695 | }; | ||
| 696 | try serveBSPHandshake(&protocol_server_allocation); | ||
| 697 | break :s &protocol_server_allocation; | ||
| 698 | } else null; | ||
| 699 | |||
| 670 | while (true) { | 700 | while (true) { |
| 671 | // If this fails, we can still start the server and wait for user | 701 | // If this fails, we can still start the server and wait for user |
| 672 | // to request a rebuild. If it returns error.FailedButCacheIntact | 702 | // to request a rebuild. If it returns error.FailedButCacheIntact |
| ... | @@ -737,16 +767,25 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -737,16 +767,25 @@ pub fn main(init: process.Init.Minimal) !void { |
| 737 | 767 | ||
| 738 | .watch = watch, | 768 | .watch = watch, |
| 739 | .web_server = web_server, | 769 | .web_server = web_server, |
| 770 | .protocol_server = protocol_server, | ||
| 771 | .protocol_server_mutex = .init, | ||
| 740 | .memory_blocked_steps = .empty, | 772 | .memory_blocked_steps = .empty, |
| 773 | .initial_steps = .empty, | ||
| 741 | .step_stack = .empty, | 774 | .step_stack = .empty, |
| 742 | .pkg_config = .{ .debug = debug_pkg_config }, | 775 | .pkg_config = .{ .debug = debug_pkg_config }, |
| 743 | 776 | ||
| 744 | .error_style = error_style, | 777 | .error_style = error_style, |
| 745 | .multiline_errors = multiline_errors, | 778 | .multiline_errors = multiline_errors, |
| 746 | .summary = summary orelse if (watch or webui_listen != null) .new else .failures, | 779 | .summary = summary orelse if (listen) |
| 780 | .none | ||
| 781 | else if (watch or webui_listen != null) | ||
| 782 | .new | ||
| 783 | else | ||
| 784 | .failures, | ||
| 747 | }; | 785 | }; |
| 748 | defer { | 786 | defer { |
| 749 | maker.memory_blocked_steps.deinit(gpa); | 787 | maker.memory_blocked_steps.deinit(gpa); |
| 788 | maker.initial_steps.deinit(gpa); | ||
| 750 | maker.step_stack.deinit(gpa); | 789 | maker.step_stack.deinit(gpa); |
| 751 | } | 790 | } |
| 752 | 791 | ||
| ... | @@ -755,7 +794,91 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -755,7 +794,91 @@ pub fn main(init: process.Init.Minimal) !void { |
| 755 | maker.max_rss_is_default = true; | 794 | maker.max_rss_is_default = true; |
| 756 | } | 795 | } |
| 757 | 796 | ||
| 758 | maker.prepare(step_names.items) catch |err| switch (err) { | 797 | if (protocol_server) |s| { |
| 798 | try s.serveStringMessage(.bsp_configuration, try arena.print("{f}", .{scanned_config.path})); | ||
| 799 | |||
| 800 | var w: ?Watch = null; | ||
| 801 | |||
| 802 | const Event = union(enum) { | ||
| 803 | message: Reader.Error!Client.Message.Header, | ||
| 804 | fs_event: if (Watch.have_impl) @typeInfo(@TypeOf(Watch.wait)).@"fn".return_type.? else noreturn, | ||
| 805 | }; | ||
| 806 | |||
| 807 | var select_buffer: [2]Event = undefined; | ||
| 808 | var select: Io.Select(Event) = .init(io, &select_buffer); | ||
| 809 | defer select.cancelDiscard(); | ||
| 810 | |||
| 811 | try select.concurrent(.message, Server.receiveMessage, .{s}); | ||
| 812 | |||
| 813 | var in_debounce = false; | ||
| 814 | loop: switch (try select.await()) { | ||
| 815 | .message => |payload| { | ||
| 816 | const header: Client.Message.Header = try payload; | ||
| 817 | switch (header.tag) { | ||
| 818 | .exit => { | ||
| 819 | cleanExit(io, &scanned_config); | ||
| 820 | process.exit(0); | ||
| 821 | }, | ||
| 822 | .bsp_build_steps => { | ||
| 823 | // Cancel existing file watching | ||
| 824 | select.cancelDiscard(); | ||
| 825 | in_debounce = false; | ||
| 826 | |||
| 827 | const body = try s.in.takeStruct(Client.Message.BuildSteps, .little); | ||
| 828 | const steps = try s.in.readSliceEndianAlloc(gpa, Configuration.Step.Index, body.step_count, .little); | ||
| 829 | defer gpa.free(steps); | ||
| 830 | if (body.flags.watch and !Watch.have_impl) fatal("file watching is unavailable", .{}); | ||
| 831 | |||
| 832 | try select.concurrent(.message, Server.receiveMessage, .{s}); | ||
| 833 | |||
| 834 | maker.watch = body.flags.watch; | ||
| 835 | maker.prepare(steps) catch |err| switch (err) { | ||
| 836 | error.DependencyLoopDetected, error.InsufficientMemory => { | ||
| 837 | // TODO handle DependencyLoopDetected as error.FailedButCacheIntact | ||
| 838 | // and handle InsufficientMemory as error.AlreadyReported | ||
| 839 | _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; | ||
| 840 | process.exit(1); | ||
| 841 | }, | ||
| 842 | else => |e| return e, | ||
| 843 | }; | ||
| 844 | |||
| 845 | try maker.makeSteps(main_progress_node, null); | ||
| 846 | |||
| 847 | if (body.flags.watch) { | ||
| 848 | if (!Watch.have_impl) unreachable; | ||
| 849 | if (w == null) w = try .init(&maker); | ||
| 850 | |||
| 851 | try w.?.update(maker.step_stack.keys()); | ||
| 852 | try select.concurrent(.fs_event, Watch.wait, .{ &w.?, if (in_debounce) .{ .ms = debounce_interval_ms } else .none }); | ||
| 853 | } | ||
| 854 | |||
| 855 | continue :loop try select.await(); | ||
| 856 | }, | ||
| 857 | else => fatal("unsupported message: {t}", .{header.tag}), | ||
| 858 | } | ||
| 859 | }, | ||
| 860 | .fs_event => |payload| { | ||
| 861 | if (!Watch.have_impl) unreachable; | ||
| 862 | switch (try payload) { | ||
| 863 | .timeout => { | ||
| 864 | assert(in_debounce); | ||
| 865 | markFailedStepsDirty(&maker); | ||
| 866 | try maker.makeSteps(main_progress_node, null); | ||
| 867 | in_debounce = false; | ||
| 868 | }, | ||
| 869 | .dirty => in_debounce = true, | ||
| 870 | .clean => {}, | ||
| 871 | } | ||
| 872 | try select.concurrent(.fs_event, Watch.wait, .{ &w.?, if (in_debounce) .{ .ms = debounce_interval_ms } else .none }); | ||
| 873 | continue :loop try select.await(); | ||
| 874 | }, | ||
| 875 | } | ||
| 876 | } | ||
| 877 | |||
| 878 | const initial_steps = try maker.resolveTopLevelSteps(step_names.items); | ||
| 879 | defer gpa.free(initial_steps); | ||
| 880 | |||
| 881 | maker.prepare(initial_steps) catch |err| switch (err) { | ||
| 759 | error.DependencyLoopDetected, error.InsufficientMemory => { | 882 | error.DependencyLoopDetected, error.InsufficientMemory => { |
| 760 | // TODO handle DependencyLoopDetected as error.FailedButCacheIntact | 883 | // TODO handle DependencyLoopDetected as error.FailedButCacheIntact |
| 761 | // and handle InsufficientMemory as error.AlreadyReported | 884 | // and handle InsufficientMemory as error.AlreadyReported |
| ... | @@ -780,18 +903,7 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -780,18 +903,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 780 | error.WriteFailed => return stderr.file_writer.err.?, | 903 | error.WriteFailed => return stderr.file_writer.err.?, |
| 781 | }; | 904 | }; |
| 782 | }) { | 905 | }) { |
| 783 | if (web_server) |ws| ws.startBuild(); | 906 | try maker.makeSteps(main_progress_node, fuzz); |
| 784 | |||
| 785 | try maker.makeStepNames(step_names.items, main_progress_node, fuzz); | ||
| 786 | |||
| 787 | if (web_server) |ws| { | ||
| 788 | if (fuzz) |mode| if (mode != .forever) fatal( | ||
| 789 | "error: limited fuzzing is not implemented yet for --webui", | ||
| 790 | .{}, | ||
| 791 | ); | ||
| 792 | |||
| 793 | ws.finishBuild(.{ .fuzz = fuzz != null }); | ||
| 794 | } | ||
| 795 | 907 | ||
| 796 | if (web_server) |ws| { | 908 | if (web_server) |ws| { |
| 797 | const c = &scanned_config.configuration; | 909 | const c = &scanned_config.configuration; |
| ... | @@ -856,6 +968,9 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -856,6 +968,9 @@ pub fn main(init: process.Init.Minimal) !void { |
| 856 | _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; | 968 | _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; |
| 857 | process.exit(1); | 969 | process.exit(1); |
| 858 | } | 970 | } |
| 971 | if (protocol_server != null) { | ||
| 972 | fatal("(zig build system) TODO send error messages to client when build.zig compilation fails", .{}); | ||
| 973 | } | ||
| 859 | if (watch and can_fs_watch) { | 974 | if (watch and can_fs_watch) { |
| 860 | fatal("(zig build system) TODO set up fs watching even when build.zig compilation fails", .{}); | 975 | fatal("(zig build system) TODO set up fs watching even when build.zig compilation fails", .{}); |
| 861 | } else { | 976 | } else { |
| ... | @@ -2022,11 +2137,37 @@ pub fn stepByIndex(maker: *const Maker, i: Configuration.Step.Index) *Step { | ... | @@ -2022,11 +2137,37 @@ pub fn stepByIndex(maker: *const Maker, i: Configuration.Step.Index) *Step { |
| 2022 | return &maker.steps[@backingInt(i)]; | 2137 | return &maker.steps[@backingInt(i)]; |
| 2023 | } | 2138 | } |
| 2024 | 2139 | ||
| 2025 | fn prepare(maker: *Maker, step_names: []const []const u8) !void { | 2140 | fn resolveTopLevelSteps(maker: *Maker, step_names: []const []const u8) ![]const Configuration.Step.Index { |
| 2141 | const gpa = maker.gpa; | ||
| 2142 | const c = &maker.scanned_config.configuration; | ||
| 2143 | |||
| 2144 | if (step_names.len == 0) { | ||
| 2145 | return try gpa.dupe(Configuration.Step.Index, &.{c.default_step}); | ||
| 2146 | } | ||
| 2147 | |||
| 2148 | var result: std.array_hash_map.Auto(Configuration.Step.Index, void) = .empty; | ||
| 2149 | defer result.deinit(gpa); | ||
| 2150 | |||
| 2151 | try result.ensureTotalCapacity(gpa, step_names.len); | ||
| 2152 | |||
| 2153 | for (0..step_names.len) |i| { | ||
| 2154 | const step_name = step_names[step_names.len - i - 1]; | ||
| 2155 | const s = maker.scanned_config.top_level_steps.get(step_name) orelse { | ||
| 2156 | log.info("to list available steps: zig build -l", .{}); | ||
| 2157 | fatal("no such step: {s}", .{step_name}); | ||
| 2158 | }; | ||
| 2159 | result.putAssumeCapacity(s, {}); | ||
| 2160 | } | ||
| 2161 | |||
| 2162 | return try gpa.dupe(Configuration.Step.Index, result.keys()); | ||
| 2163 | } | ||
| 2164 | |||
| 2165 | fn prepare(maker: *Maker, step_indices: []const Configuration.Step.Index) !void { | ||
| 2026 | const gpa = maker.gpa; | 2166 | const gpa = maker.gpa; |
| 2027 | const graph = maker.graph; | 2167 | const graph = maker.graph; |
| 2028 | const arena = graph.arena; | 2168 | const arena = graph.arena; |
| 2029 | const seed: u32 = graph.random_seed; | 2169 | const seed: u32 = graph.random_seed; |
| 2170 | const initial_steps = &maker.initial_steps; | ||
| 2030 | const step_stack = &maker.step_stack; | 2171 | const step_stack = &maker.step_stack; |
| 2031 | const c = &maker.scanned_config.configuration; | 2172 | const c = &maker.scanned_config.configuration; |
| 2032 | 2173 | ||
| ... | @@ -2035,18 +2176,15 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void { | ... | @@ -2035,18 +2176,15 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void { |
| 2035 | step.* = .{ .extended = .init(step_index.ptr(c).flags(c).tag) }; | 2176 | step.* = .{ .extended = .init(step_index.ptr(c).flags(c).tag) }; |
| 2036 | } | 2177 | } |
| 2037 | 2178 | ||
| 2038 | if (step_names.len == 0) { | 2179 | try initial_steps.ensureUnusedCapacity(gpa, step_indices.len); |
| 2039 | try step_stack.put(gpa, c.default_step, {}); | 2180 | try step_stack.ensureUnusedCapacity(gpa, step_indices.len); |
| 2040 | } else { | 2181 | |
| 2041 | try step_stack.ensureUnusedCapacity(gpa, step_names.len); | 2182 | initial_steps.clearRetainingCapacity(); |
| 2042 | for (0..step_names.len) |i| { | 2183 | step_stack.clearRetainingCapacity(); |
| 2043 | const step_name = step_names[step_names.len - i - 1]; | 2184 | |
| 2044 | const s = maker.scanned_config.top_level_steps.get(step_name) orelse { | 2185 | for (step_indices) |step| { |
| 2045 | log.info("to list available steps: zig build -l", .{}); | 2186 | initial_steps.putAssumeCapacity(step, {}); |
| 2046 | fatal("no such step: {s}", .{step_name}); | 2187 | step_stack.putAssumeCapacity(step, {}); |
| 2047 | }; | ||
| 2048 | step_stack.putAssumeCapacity(s, {}); | ||
| 2049 | } | ||
| 2050 | } | 2188 | } |
| 2051 | 2189 | ||
| 2052 | const starting_steps = try arena.dupe(Configuration.Step.Index, step_stack.keys()); | 2190 | const starting_steps = try arena.dupe(Configuration.Step.Index, step_stack.keys()); |
| ... | @@ -2095,9 +2233,8 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void { | ... | @@ -2095,9 +2233,8 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void { |
| 2095 | } | 2233 | } |
| 2096 | } | 2234 | } |
| 2097 | 2235 | ||
| 2098 | fn makeStepNames( | 2236 | fn makeSteps( |
| 2099 | maker: *Maker, | 2237 | maker: *Maker, |
| 2100 | step_names: []const []const u8, | ||
| 2101 | parent_progress_node: std.Progress.Node, | 2238 | parent_progress_node: std.Progress.Node, |
| 2102 | fuzz: ?Fuzz.Mode, | 2239 | fuzz: ?Fuzz.Mode, |
| 2103 | ) !void { | 2240 | ) !void { |
| ... | @@ -2108,6 +2245,12 @@ fn makeStepNames( | ... | @@ -2108,6 +2245,12 @@ fn makeStepNames( |
| 2108 | const top_level_steps = &maker.scanned_config.top_level_steps; | 2245 | const top_level_steps = &maker.scanned_config.top_level_steps; |
| 2109 | const c = &maker.scanned_config.configuration; | 2246 | const c = &maker.scanned_config.configuration; |
| 2110 | 2247 | ||
| 2248 | if (maker.web_server) |ws| ws.startBuild(); | ||
| 2249 | |||
| 2250 | if (maker.protocol_server) |s| { | ||
| 2251 | try s.serveBodylessMessage(.bsp_build_started); | ||
| 2252 | } | ||
| 2253 | |||
| 2111 | { | 2254 | { |
| 2112 | // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer, | 2255 | // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer, |
| 2113 | // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking | 2256 | // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking |
| ... | @@ -2133,6 +2276,19 @@ fn makeStepNames( | ... | @@ -2133,6 +2276,19 @@ fn makeStepNames( |
| 2133 | try group.await(io); | 2276 | try group.await(io); |
| 2134 | } | 2277 | } |
| 2135 | 2278 | ||
| 2279 | if (maker.web_server) |ws| { | ||
| 2280 | if (fuzz) |mode| if (mode != .forever) fatal( | ||
| 2281 | "error: limited fuzzing is not implemented yet for --webui", | ||
| 2282 | .{}, | ||
| 2283 | ); | ||
| 2284 | |||
| 2285 | ws.finishBuild(.{ .fuzz = fuzz != null }); | ||
| 2286 | } | ||
| 2287 | |||
| 2288 | if (maker.protocol_server) |s| { | ||
| 2289 | try s.serveBodylessMessage(.bsp_build_completed); | ||
| 2290 | } | ||
| 2291 | |||
| 2136 | assert(maker.memory_blocked_steps.items.len == 0); | 2292 | assert(maker.memory_blocked_steps.items.len == 0); |
| 2137 | 2293 | ||
| 2138 | var test_pass_count: usize = 0; | 2294 | var test_pass_count: usize = 0; |
| ... | @@ -2285,7 +2441,7 @@ fn makeStepNames( | ... | @@ -2285,7 +2441,7 @@ fn makeStepNames( |
| 2285 | defer step_stack_copy.deinit(gpa); | 2441 | defer step_stack_copy.deinit(gpa); |
| 2286 | 2442 | ||
| 2287 | var print_node: PrintNode = .{ .parent = null }; | 2443 | var print_node: PrintNode = .{ .parent = null }; |
| 2288 | if (step_names.len == 0) { | 2444 | if (maker.initial_steps.count() == 0) { |
| 2289 | print_node.last = true; | 2445 | print_node.last = true; |
| 2290 | printTreeStep(maker, c.default_step, t, &print_node, &step_stack_copy) catch |err| switch (err) { | 2446 | printTreeStep(maker, c.default_step, t, &print_node, &step_stack_copy) catch |err| switch (err) { |
| 2291 | error.Canceled => |e| return e, | 2447 | error.Canceled => |e| return e, |
| ... | @@ -2293,10 +2449,10 @@ fn makeStepNames( | ... | @@ -2293,10 +2449,10 @@ fn makeStepNames( |
| 2293 | }; | 2449 | }; |
| 2294 | } else { | 2450 | } else { |
| 2295 | const last_index = if (maker.summary == .all) top_level_steps.count() else blk: { | 2451 | const last_index = if (maker.summary == .all) top_level_steps.count() else blk: { |
| 2296 | var i: usize = step_names.len; | 2452 | var i: usize = maker.initial_steps.count(); |
| 2297 | while (i > 0) { | 2453 | while (i > 0) { |
| 2298 | i -= 1; | 2454 | i -= 1; |
| 2299 | const step_index = top_level_steps.get(step_names[i]).?; | 2455 | const step_index = maker.initial_steps.keys()[i]; |
| 2300 | const step = maker.stepByIndex(step_index); | 2456 | const step = maker.stepByIndex(step_index); |
| 2301 | const found = switch (maker.summary) { | 2457 | const found = switch (maker.summary) { |
| 2302 | .all, .line, .none => unreachable, | 2458 | .all, .line, .none => unreachable, |
| ... | @@ -2307,8 +2463,7 @@ fn makeStepNames( | ... | @@ -2307,8 +2463,7 @@ fn makeStepNames( |
| 2307 | } | 2463 | } |
| 2308 | break :blk top_level_steps.count(); | 2464 | break :blk top_level_steps.count(); |
| 2309 | }; | 2465 | }; |
| 2310 | for (step_names, 0..) |step_name, i| { | 2466 | for (maker.initial_steps.keys(), 0..) |step_index, i| { |
| 2311 | const step_index = top_level_steps.get(step_name).?; | ||
| 2312 | print_node.last = i + 1 == last_index; | 2467 | print_node.last = i + 1 == last_index; |
| 2313 | printTreeStep(maker, step_index, t, &print_node, &step_stack_copy) catch |err| switch (err) { | 2468 | printTreeStep(maker, step_index, t, &print_node, &step_stack_copy) catch |err| switch (err) { |
| 2314 | error.Canceled => |e| return e, | 2469 | error.Canceled => |e| return e, |
| ... | @@ -2319,7 +2474,7 @@ fn makeStepNames( | ... | @@ -2319,7 +2474,7 @@ fn makeStepNames( |
| 2319 | w.writeByte('\n') catch {}; | 2474 | w.writeByte('\n') catch {}; |
| 2320 | } | 2475 | } |
| 2321 | 2476 | ||
| 2322 | if (maker.watch or maker.web_server != null) return; | 2477 | if (maker.watch or maker.web_server != null or maker.protocol_server != null) return; |
| 2323 | 2478 | ||
| 2324 | const code: u8 = code: { | 2479 | const code: u8 = code: { |
| 2325 | if (failure_count == 0) break :code 0; // success | 2480 | if (failure_count == 0) break :code 0; // success |
| ... | @@ -2394,6 +2549,15 @@ fn makeStep( | ... | @@ -2394,6 +2549,15 @@ fn makeStep( |
| 2394 | defer step_prog_node.end(); | 2549 | defer step_prog_node.end(); |
| 2395 | 2550 | ||
| 2396 | if (maker.web_server) |ws| ws.updateStepStatus(step_index, .wip); | 2551 | if (maker.web_server) |ws| ws.updateStepStatus(step_index, .wip); |
| 2552 | if (maker.protocol_server) |s| { | ||
| 2553 | maker.protocol_server_mutex.lockUncancelable(io); | ||
| 2554 | defer maker.protocol_server_mutex.unlock(io); | ||
| 2555 | |||
| 2556 | s.serveU32Message( | ||
| 2557 | .bsp_step_started, | ||
| 2558 | @backingInt(step_index), | ||
| 2559 | ) catch @panic("TODO propagate error when failing to send protocol message"); | ||
| 2560 | } | ||
| 2397 | 2561 | ||
| 2398 | const new_state: Step.State = for (deps) |dep_index| { | 2562 | const new_state: Step.State = for (deps) |dep_index| { |
| 2399 | const dep_make_step = maker.stepByIndex(dep_index); | 2563 | const dep_make_step = maker.stepByIndex(dep_index); |
| ... | @@ -2419,7 +2583,7 @@ fn makeStep( | ... | @@ -2419,7 +2583,7 @@ fn makeStep( |
| 2419 | 2583 | ||
| 2420 | @atomicStore(Step.State, &make_step.state, new_state, .monotonic); | 2584 | @atomicStore(Step.State, &make_step.state, new_state, .monotonic); |
| 2421 | 2585 | ||
| 2422 | switch (new_state) { | 2586 | const success = switch (new_state) { |
| 2423 | .precheck_unstarted => unreachable, | 2587 | .precheck_unstarted => unreachable, |
| 2424 | .precheck_started => unreachable, | 2588 | .precheck_started => unreachable, |
| 2425 | .precheck_done => unreachable, | 2589 | .precheck_done => unreachable, |
| ... | @@ -2427,17 +2591,37 @@ fn makeStep( | ... | @@ -2427,17 +2591,37 @@ fn makeStep( |
| 2427 | .failure, | 2591 | .failure, |
| 2428 | .dependency_failure, | 2592 | .dependency_failure, |
| 2429 | .skipped_oom, | 2593 | .skipped_oom, |
| 2430 | => { | 2594 | => false, |
| 2431 | if (maker.web_server) |ws| ws.updateStepStatus(step_index, .failure); | ||
| 2432 | std.Progress.setStatus(.failure_working); | ||
| 2433 | }, | ||
| 2434 | 2595 | ||
| 2435 | .success, | 2596 | .success, |
| 2436 | .skipped, | 2597 | .skipped, |
| 2437 | => { | 2598 | => true, |
| 2438 | if (maker.web_server) |ws| ws.updateStepStatus(step_index, .success); | 2599 | }; |
| 2439 | }, | 2600 | |
| 2601 | if (maker.web_server) |ws| { | ||
| 2602 | ws.updateStepStatus(step_index, if (success) .success else .failure); | ||
| 2440 | } | 2603 | } |
| 2604 | if (maker.protocol_server != null) { | ||
| 2605 | maker.protocol_server_mutex.lockUncancelable(io); | ||
| 2606 | defer maker.protocol_server_mutex.unlock(io); | ||
| 2607 | |||
| 2608 | const status: Server.Message.BuildStepCompleted.Status = switch (new_state) { | ||
| 2609 | .precheck_unstarted => unreachable, | ||
| 2610 | .precheck_started => unreachable, | ||
| 2611 | .precheck_done => unreachable, | ||
| 2612 | .success => .success, | ||
| 2613 | .failure, .dependency_failure => .failure, | ||
| 2614 | .skipped => .skipped, | ||
| 2615 | .skipped_oom => .skipped_oom, | ||
| 2616 | }; | ||
| 2617 | serveBuildStepCompleted( | ||
| 2618 | maker, | ||
| 2619 | step_index, | ||
| 2620 | status, | ||
| 2621 | ) catch |err| std.debug.panic("TODO propagate error when failing to send protocol message: {t}", .{err}); | ||
| 2622 | } | ||
| 2623 | |||
| 2624 | if (!success) std.Progress.setStatus(.failure_working); | ||
| 2441 | } | 2625 | } |
| 2442 | 2626 | ||
| 2443 | // No matter the result, we want to display error/warning messages. | 2627 | // No matter the result, we want to display error/warning messages. |
| ... | @@ -2992,6 +3176,50 @@ fn cleanTmpFiles(maker: *Maker, steps: []const Configuration.Step.Index) void { | ... | @@ -2992,6 +3176,50 @@ fn cleanTmpFiles(maker: *Maker, steps: []const Configuration.Step.Index) void { |
| 2992 | } | 3176 | } |
| 2993 | } | 3177 | } |
| 2994 | 3178 | ||
| 3179 | fn serveBSPHandshake(s: *const std.zig.Server) !void { | ||
| 3180 | const handshake_header: Server.Message.Handshake = .{ | ||
| 3181 | .version = Server.build_system_version, | ||
| 3182 | .flags = .{ | ||
| 3183 | .file_system_watch_supported = Watch.have_impl, | ||
| 3184 | }, | ||
| 3185 | }; | ||
| 3186 | try s.serveMessageHeader(.{ | ||
| 3187 | .tag = .bsp_handshake, | ||
| 3188 | .bytes_len = @sizeOf(Server.Message.Handshake), | ||
| 3189 | }); | ||
| 3190 | try s.out.writeStruct(handshake_header, .little); | ||
| 3191 | try s.out.flush(); | ||
| 3192 | } | ||
| 3193 | |||
| 3194 | fn serveBuildStepCompleted( | ||
| 3195 | maker: *Maker, | ||
| 3196 | step_index: Configuration.Step.Index, | ||
| 3197 | status: Server.Message.BuildStepCompleted.Status, | ||
| 3198 | ) !void { | ||
| 3199 | const s: *Server = maker.protocol_server.?; | ||
| 3200 | const step = maker.stepByIndex(step_index); | ||
| 3201 | const error_bundle = step.result_error_bundle; | ||
| 3202 | |||
| 3203 | const body: Server.Message.BuildStepCompleted = .{ | ||
| 3204 | .step_index = step_index, | ||
| 3205 | .status = status, | ||
| 3206 | .error_bundle = .{ | ||
| 3207 | .extra_len = @intCast(error_bundle.extra.len), | ||
| 3208 | .string_bytes_len = @intCast(error_bundle.string_bytes.len), | ||
| 3209 | }, | ||
| 3210 | }; | ||
| 3211 | const eb_bytes_len = @sizeOf(u32) * error_bundle.extra.len + error_bundle.string_bytes.len; | ||
| 3212 | const bytes_len = @sizeOf(Server.Message.BuildStepCompleted) + eb_bytes_len; | ||
| 3213 | try s.serveMessageHeader(.{ | ||
| 3214 | .tag = .bsp_step_completed, | ||
| 3215 | .bytes_len = @intCast(bytes_len), | ||
| 3216 | }); | ||
| 3217 | try s.out.writeStruct(body, .little); | ||
| 3218 | try s.out.writeSliceEndian(u32, error_bundle.extra, .little); | ||
| 3219 | try s.out.writeAll(error_bundle.string_bytes); | ||
| 3220 | try s.out.flush(); | ||
| 3221 | } | ||
| 3222 | |||
| 2995 | fn initStdoutWriter(io: Io) *Writer { | 3223 | fn initStdoutWriter(io: Io) *Writer { |
| 2996 | stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation); | 3224 | stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation); |
| 2997 | return &stdout_writer_allocation.interface; | 3225 | return &stdout_writer_allocation.interface; |
lib/compiler/Maker/Step.zig+10-8| ... | @@ -561,24 +561,26 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi | ... | @@ -561,24 +561,26 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi |
| 561 | var result: ?Path = null; | 561 | var result: ?Path = null; |
| 562 | var eos_err: error{EndOfStream}!void = {}; | 562 | var eos_err: error{EndOfStream}!void = {}; |
| 563 | 563 | ||
| 564 | const stdout = zp.multi_reader.fileReader(0); | 564 | var client: std.zig.Client = .{ |
| 565 | .in = zp.multi_reader.reader(0), | ||
| 566 | .out = undefined, | ||
| 567 | }; | ||
| 565 | 568 | ||
| 566 | while (true) { | 569 | while (true) { |
| 567 | const Header = std.zig.Server.Message.Header; | 570 | const header = client.receiveMessageWithMultiReader(&zp.multi_reader, .none) catch |err| switch (err) { |
| 568 | const header = stdout.interface.takeStruct(Header, .little) catch |err| switch (err) { | 571 | error.Timeout => unreachable, |
| 569 | error.EndOfStream => break, | ||
| 570 | error.ReadFailed => return stdout.err.?, | ||
| 571 | }; | ||
| 572 | const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) { | ||
| 573 | error.EndOfStream => |e| { | 572 | error.EndOfStream => |e| { |
| 573 | if (client.in.bufferedLen() == 0) break; | ||
| 574 | // Better to report the crash with stderr below, but we set | 574 | // Better to report the crash with stderr below, but we set |
| 575 | // this in case the child exits successfully while violating | 575 | // this in case the child exits successfully while violating |
| 576 | // this protocol. | 576 | // this protocol. |
| 577 | eos_err = e; | 577 | eos_err = e; |
| 578 | break; | 578 | break; |
| 579 | }, | 579 | }, |
| 580 | error.ReadFailed => return stdout.err.?, | 580 | else => |e| return e, |
| 581 | }; | 581 | }; |
| 582 | const body = client.in.take(header.bytes_len) catch unreachable; | ||
| 583 | |||
| 582 | switch (header.tag) { | 584 | switch (header.tag) { |
| 583 | .zig_version => { | 585 | .zig_version => { |
| 584 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { | 586 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { |
lib/compiler/Maker/Step/Run.zig+51-125| ... | @@ -384,13 +384,23 @@ fn waitZigTest( | ... | @@ -384,13 +384,23 @@ fn waitZigTest( |
| 384 | var sub_prog_node: ?std.Progress.Node = null; | 384 | var sub_prog_node: ?std.Progress.Node = null; |
| 385 | defer if (sub_prog_node) |n| n.end(); | 385 | defer if (sub_prog_node) |n| n.end(); |
| 386 | 386 | ||
| 387 | const stdout = multi_reader.reader(0); | ||
| 388 | const stderr = multi_reader.reader(1); | ||
| 389 | |||
| 390 | var stdin_writer = child.stdin.?.writerStreaming(io, &.{}); | ||
| 391 | |||
| 392 | var client: std.zig.Client = .{ | ||
| 393 | .in = stdout, | ||
| 394 | .out = &stdin_writer.interface, | ||
| 395 | }; | ||
| 396 | |||
| 387 | if (opt_metadata.*) |*md| { | 397 | if (opt_metadata.*) |*md| { |
| 388 | // Previous unit test process died or was killed; we're continuing where it left off | 398 | // Previous unit test process died or was killed; we're continuing where it left off |
| 389 | requestNextTest(io, child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; | 399 | requestNextTest(&client, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; |
| 390 | } else { | 400 | } else { |
| 391 | // Running unit tests normally | 401 | // Running unit tests normally |
| 392 | run.fuzz_tests.clearRetainingCapacity(); | 402 | run.fuzz_tests.clearRetainingCapacity(); |
| 393 | sendMessage(io, child.stdin.?, .query_test_metadata) catch |err| return .{ .write_failed = err }; | 403 | client.serveBodylessMessage(.query_test_metadata) catch |err| return .{ .write_failed = err }; |
| 394 | } | 404 | } |
| 395 | 405 | ||
| 396 | var active_test_index: ?u32 = null; | 406 | var active_test_index: ?u32 = null; |
| ... | @@ -410,10 +420,6 @@ fn waitZigTest( | ... | @@ -410,10 +420,6 @@ fn waitZigTest( |
| 410 | .raw = .fromNanoseconds(ns), | 420 | .raw = .fromNanoseconds(ns), |
| 411 | } else null; | 421 | } else null; |
| 412 | 422 | ||
| 413 | const stdout = multi_reader.reader(0); | ||
| 414 | const stderr = multi_reader.reader(1); | ||
| 415 | const Header = std.zig.Server.Message.Header; | ||
| 416 | |||
| 417 | while (true) { | 423 | while (true) { |
| 418 | const timeout: Io.Timeout = t: { | 424 | const timeout: Io.Timeout = t: { |
| 419 | const opt_duration = if (active_test_index == null) response_timeout else test_timeout; | 425 | const opt_duration = if (active_test_index == null) response_timeout else test_timeout; |
| ... | @@ -421,46 +427,20 @@ fn waitZigTest( | ... | @@ -421,46 +427,20 @@ fn waitZigTest( |
| 421 | break :t .{ .deadline = last_update.addDuration(duration) }; | 427 | break :t .{ .deadline = last_update.addDuration(duration) }; |
| 422 | }; | 428 | }; |
| 423 | 429 | ||
| 424 | // This block is exited when `stdout` contains enough bytes for a `Header`. | 430 | const header = client.receiveMessageWithMultiReader(multi_reader, timeout) catch |err| switch (err) { |
| 425 | header_ready: { | 431 | error.Timeout => return .{ .timeout = .{ |
| 426 | if (stdout.buffered().len >= @sizeOf(Header)) { | 432 | .active_test_index = active_test_index, |
| 427 | // We already have one, no need to poll! | 433 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), |
| 428 | break :header_ready; | 434 | } }, |
| 429 | } | 435 | error.EndOfStream => return .{ .no_poll = .{ |
| 430 | 436 | .active_test_index = active_test_index, | |
| 431 | multi_reader.fill(64, timeout) catch |err| switch (err) { | 437 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), |
| 432 | error.Timeout => return .{ .timeout = .{ | 438 | } }, |
| 433 | .active_test_index = active_test_index, | 439 | else => |e| return e, |
| 434 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | 440 | }; |
| 435 | } }, | 441 | const body = client.in.take(header.bytes_len) catch unreachable; |
| 436 | error.EndOfStream => return .{ .no_poll = .{ | ||
| 437 | .active_test_index = active_test_index, | ||
| 438 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | ||
| 439 | } }, | ||
| 440 | else => |e| return e, | ||
| 441 | }; | ||
| 442 | |||
| 443 | continue; | ||
| 444 | } | ||
| 445 | // There is definitely a header available now -- read it. | ||
| 446 | const header = stdout.takeStruct(Header, .little) catch unreachable; | ||
| 447 | |||
| 448 | while (stdout.buffered().len < header.bytes_len) { | ||
| 449 | multi_reader.fill(64, timeout) catch |err| switch (err) { | ||
| 450 | error.Timeout => return .{ .timeout = .{ | ||
| 451 | .active_test_index = active_test_index, | ||
| 452 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | ||
| 453 | } }, | ||
| 454 | error.EndOfStream => return .{ .no_poll = .{ | ||
| 455 | .active_test_index = active_test_index, | ||
| 456 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | ||
| 457 | } }, | ||
| 458 | else => |e| return e, | ||
| 459 | }; | ||
| 460 | } | ||
| 461 | |||
| 462 | const body = stdout.take(header.bytes_len) catch unreachable; | ||
| 463 | var body_r: std.Io.Reader = .fixed(body); | 442 | var body_r: std.Io.Reader = .fixed(body); |
| 443 | |||
| 464 | switch (header.tag) { | 444 | switch (header.tag) { |
| 465 | .zig_version => { | 445 | .zig_version => { |
| 466 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail( | 446 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail( |
| ... | @@ -500,7 +480,7 @@ fn waitZigTest( | ... | @@ -500,7 +480,7 @@ fn waitZigTest( |
| 500 | active_test_index = null; | 480 | active_test_index = null; |
| 501 | last_update = .now(io, .awake); | 481 | last_update = .now(io, .awake); |
| 502 | 482 | ||
| 503 | requestNextTest(io, child.stdin.?, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err }; | 483 | requestNextTest(&client, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err }; |
| 504 | }, | 484 | }, |
| 505 | .test_started => { | 485 | .test_started => { |
| 506 | active_test_index = opt_metadata.*.?.next_index - 1; | 486 | active_test_index = opt_metadata.*.?.next_index - 1; |
| ... | @@ -551,7 +531,7 @@ fn waitZigTest( | ... | @@ -551,7 +531,7 @@ fn waitZigTest( |
| 551 | md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds); | 531 | md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds); |
| 552 | last_update = now; | 532 | last_update = now; |
| 553 | 533 | ||
| 554 | requestNextTest(io, child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; | 534 | requestNextTest(&client, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; |
| 555 | }, | 535 | }, |
| 556 | else => {}, // ignore other messages | 536 | else => {}, // ignore other messages |
| 557 | } | 537 | } |
| ... | @@ -697,17 +677,18 @@ const FuzzTestRunner = struct { | ... | @@ -697,17 +677,18 @@ const FuzzTestRunner = struct { |
| 697 | 677 | ||
| 698 | for (0.., f.instances) |id, *instance| { | 678 | for (0.., f.instances) |id, *instance| { |
| 699 | const id32: u32 = @intCast(id); | 679 | const id32: u32 = @intCast(id); |
| 680 | var writer = instance.child.stdin.?.writerStreaming(io, &.{}); | ||
| 681 | const client: std.zig.Client = .{ | ||
| 682 | .in = undefined, | ||
| 683 | .out = &writer.interface, | ||
| 684 | }; | ||
| 700 | (switch (f.ctx.fuzz.mode) { | 685 | (switch (f.ctx.fuzz.mode) { |
| 701 | .forever => sendRunFuzzTestMessage( | 686 | .forever => client.serveRunFuzzTestMessage( |
| 702 | io, | ||
| 703 | instance.child.stdin.?, | ||
| 704 | run.fuzz_tests.items, | 687 | run.fuzz_tests.items, |
| 705 | .forever, | 688 | .forever, |
| 706 | id32, | 689 | id32, |
| 707 | ), | 690 | ), |
| 708 | .limit => |limit| sendRunFuzzTestMessage( | 691 | .limit => |limit| client.serveRunFuzzTestMessage( |
| 709 | io, | ||
| 710 | instance.child.stdin.?, | ||
| 711 | run.fuzz_tests.items, | 692 | run.fuzz_tests.items, |
| 712 | .iterations, | 693 | .iterations, |
| 713 | limit.amount, | 694 | limit.amount, |
| ... | @@ -1315,7 +1296,7 @@ pub const CachedTestMetadata = struct { | ... | @@ -1315,7 +1296,7 @@ pub const CachedTestMetadata = struct { |
| 1315 | } | 1296 | } |
| 1316 | }; | 1297 | }; |
| 1317 | 1298 | ||
| 1318 | fn requestNextTest(io: Io, in: Io.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { | 1299 | fn requestNextTest(client: *std.zig.Client, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { |
| 1319 | while (metadata.next_index < metadata.names.len) { | 1300 | while (metadata.next_index < metadata.names.len) { |
| 1320 | const i = metadata.next_index; | 1301 | const i = metadata.next_index; |
| 1321 | metadata.next_index += 1; | 1302 | metadata.next_index += 1; |
| ... | @@ -1326,76 +1307,11 @@ fn requestNextTest(io: Io, in: Io.File, metadata: *TestMetadata, sub_prog_node: | ... | @@ -1326,76 +1307,11 @@ fn requestNextTest(io: Io, in: Io.File, metadata: *TestMetadata, sub_prog_node: |
| 1326 | if (sub_prog_node.*) |n| n.end(); | 1307 | if (sub_prog_node.*) |n| n.end(); |
| 1327 | sub_prog_node.* = metadata.prog_node.start(name, 0); | 1308 | sub_prog_node.* = metadata.prog_node.start(name, 0); |
| 1328 | 1309 | ||
| 1329 | try sendRunTestMessage(io, in, .run_test, i); | 1310 | try client.serveRunTest(i); |
| 1330 | return; | 1311 | return; |
| 1331 | } else { | 1312 | } else { |
| 1332 | metadata.next_index = std.math.maxInt(u32); // indicate that all tests are done | 1313 | metadata.next_index = std.math.maxInt(u32); // indicate that all tests are done |
| 1333 | try sendMessage(io, in, .exit); | 1314 | try client.serveBodylessMessage(.exit); |
| 1334 | } | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void { | ||
| 1338 | const header: std.zig.Client.Message.Header = .{ | ||
| 1339 | .tag = tag, | ||
| 1340 | .bytes_len = 0, | ||
| 1341 | }; | ||
| 1342 | var w = file.writerStreaming(io, &.{}); | ||
| 1343 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | ||
| 1344 | error.WriteFailed => return w.err.?, | ||
| 1345 | }; | ||
| 1346 | } | ||
| 1347 | |||
| 1348 | fn sendRunTestMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag, index: u32) !void { | ||
| 1349 | const header: std.zig.Client.Message.Header = .{ | ||
| 1350 | .tag = tag, | ||
| 1351 | .bytes_len = 4, | ||
| 1352 | }; | ||
| 1353 | var w = file.writerStreaming(io, &.{}); | ||
| 1354 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | ||
| 1355 | error.WriteFailed => return w.err.?, | ||
| 1356 | }; | ||
| 1357 | w.interface.writeInt(u32, index, .little) catch |err| switch (err) { | ||
| 1358 | error.WriteFailed => return w.err.?, | ||
| 1359 | }; | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | fn sendRunFuzzTestMessage( | ||
| 1363 | io: Io, | ||
| 1364 | file: Io.File, | ||
| 1365 | test_names: []const []const u8, | ||
| 1366 | kind: std.Build.abi.fuzz.LimitKind, | ||
| 1367 | amount_or_instance: u64, | ||
| 1368 | ) !void { | ||
| 1369 | const header: std.zig.Client.Message.Header = .{ | ||
| 1370 | .tag = .start_fuzzing, | ||
| 1371 | .bytes_len = 1 + 8 + 4 + count: { | ||
| 1372 | var c: u32 = @intCast(test_names.len * 4); | ||
| 1373 | for (test_names) |name| { | ||
| 1374 | c += @intCast(name.len); | ||
| 1375 | } | ||
| 1376 | break :count c; | ||
| 1377 | }, | ||
| 1378 | }; | ||
| 1379 | var w = file.writerStreaming(io, &.{}); | ||
| 1380 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | ||
| 1381 | error.WriteFailed => return w.err.?, | ||
| 1382 | }; | ||
| 1383 | w.interface.writeByte(@backingInt(kind)) catch |err| switch (err) { | ||
| 1384 | error.WriteFailed => return w.err.?, | ||
| 1385 | }; | ||
| 1386 | w.interface.writeInt(u64, amount_or_instance, .little) catch |err| switch (err) { | ||
| 1387 | error.WriteFailed => return w.err.?, | ||
| 1388 | }; | ||
| 1389 | w.interface.writeInt(u32, @intCast(test_names.len), .little) catch |err| switch (err) { | ||
| 1390 | error.WriteFailed => return w.err.?, | ||
| 1391 | }; | ||
| 1392 | for (test_names) |test_name| { | ||
| 1393 | w.interface.writeInt(u32, @intCast(test_name.len), .little) catch |err| switch (err) { | ||
| 1394 | error.WriteFailed => return w.err.?, | ||
| 1395 | }; | ||
| 1396 | w.interface.writeAll(test_name) catch |err| switch (err) { | ||
| 1397 | error.WriteFailed => return w.err.?, | ||
| 1398 | }; | ||
| 1399 | } | 1315 | } |
| 1400 | } | 1316 | } |
| 1401 | 1317 | ||
| ... | @@ -2285,25 +2201,35 @@ fn spawnChildAndCollect( | ... | @@ -2285,25 +2201,35 @@ fn spawnChildAndCollect( |
| 2285 | assert(conf_run.flags.stdio != .inherit); | 2201 | assert(conf_run.flags.stdio != .inherit); |
| 2286 | break :s .pipe; | 2202 | break :s .pipe; |
| 2287 | } else switch (conf_run.flags.stdio) { | 2203 | } else switch (conf_run.flags.stdio) { |
| 2288 | .infer_from_args => if (has_side_effects) .inherit else .ignore, | 2204 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .ignore, |
| 2289 | .inherit => .inherit, | 2205 | .inherit => .inherit, |
| 2290 | .check => .ignore, | 2206 | .check => .ignore, |
| 2291 | .zig_test => .pipe, | 2207 | .zig_test => .pipe, |
| 2292 | }, | 2208 | }, |
| 2293 | .stdout = if (conf_run.captured_stdout.value != null) .pipe else switch (conf_run.flags.stdio) { | 2209 | .stdout = if (conf_run.captured_stdout.value != null) .pipe else switch (conf_run.flags.stdio) { |
| 2294 | .infer_from_args => if (has_side_effects) .inherit else .ignore, | 2210 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .ignore, |
| 2295 | .inherit => .inherit, | 2211 | .inherit => .inherit, |
| 2296 | .check => if (checksContainStdout(&conf_run)) .pipe else .ignore, | 2212 | .check => if (checksContainStdout(&conf_run)) .pipe else .ignore, |
| 2297 | .zig_test => .pipe, | 2213 | .zig_test => .pipe, |
| 2298 | }, | 2214 | }, |
| 2299 | .stderr = if (conf_run.captured_stderr.value != null) .pipe else switch (conf_run.flags.stdio) { | 2215 | .stderr = if (conf_run.captured_stderr.value != null) .pipe else switch (conf_run.flags.stdio) { |
| 2300 | .infer_from_args => if (has_side_effects) .inherit else .pipe, | 2216 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .pipe, |
| 2301 | .inherit => .inherit, | 2217 | .inherit => if (maker.protocol_server == null) .inherit else .pipe, |
| 2302 | .check => .pipe, | 2218 | .check => .pipe, |
| 2303 | .zig_test => .pipe, | 2219 | .zig_test => .pipe, |
| 2304 | }, | 2220 | }, |
| 2305 | }; | 2221 | }; |
| 2306 | 2222 | ||
| 2223 | if (maker.protocol_server != null) { | ||
| 2224 | if (spawn_options.stdin == .inherit) { | ||
| 2225 | return step.fail(maker, "Cannot inherit stdin when running through over the build system protocol", .{}); | ||
| 2226 | } | ||
| 2227 | if (spawn_options.stdout == .inherit) { | ||
| 2228 | return step.fail(maker, "Cannot inherit stdout when running through over the build system protocol", .{}); | ||
| 2229 | } | ||
| 2230 | assert(spawn_options.stderr != .inherit); | ||
| 2231 | } | ||
| 2232 | |||
| 2307 | if (conf_run.flags.stdio == .zig_test) { | 2233 | if (conf_run.flags.stdio == .zig_test) { |
| 2308 | try setColorEnvironmentVariables(&conf_run, environ_map, graph.stderr_mode.?); | 2234 | try setColorEnvironmentVariables(&conf_run, environ_map, graph.stderr_mode.?); |
| 2309 | const started: Io.Clock.Timestamp = .now(io, .awake); | 2235 | const started: Io.Clock.Timestamp = .now(io, .awake); |
lib/compiler/objcopy.zig+3-3| ... | @@ -214,11 +214,11 @@ fn cmdObjCopy(arena: Allocator, io: Io, args: []const []const u8) !void { | ... | @@ -214,11 +214,11 @@ fn cmdObjCopy(arena: Allocator, io: Io, args: []const []const u8) !void { |
| 214 | if (listen) { | 214 | if (listen) { |
| 215 | var stdin_reader = Io.File.stdin().reader(io, &stdin_buffer); | 215 | var stdin_reader = Io.File.stdin().reader(io, &stdin_buffer); |
| 216 | var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); | 216 | var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); |
| 217 | var server = try Server.init(.{ | 217 | var server: Server = .{ |
| 218 | .in = &stdin_reader.interface, | 218 | .in = &stdin_reader.interface, |
| 219 | .out = &stdout_writer.interface, | 219 | .out = &stdout_writer.interface, |
| 220 | .zig_version = builtin.zig_version_string, | 220 | }; |
| 221 | }); | 221 | try server.serveStringMessage(.zig_version, builtin.zig_version_string); |
| 222 | 222 | ||
| 223 | var seen_update = false; | 223 | var seen_update = false; |
| 224 | while (true) { | 224 | while (true) { |
lib/compiler/std-docs.zig+21-22| ... | @@ -346,29 +346,39 @@ fn buildWasmBinary( | ... | @@ -346,29 +346,39 @@ fn buildWasmBinary( |
| 346 | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); | 346 | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); |
| 347 | defer multi_reader.deinit(); | 347 | defer multi_reader.deinit(); |
| 348 | 348 | ||
| 349 | try sendMessage(io, child.stdin.?, .update); | 349 | const stdout = multi_reader.reader(0); |
| 350 | try sendMessage(io, child.stdin.?, .exit); | 350 | |
| 351 | var stdin_buffer: [256]u8 = undefined; | ||
| 352 | var stdin_writer = child.stdin.?.writerStreaming(io, &stdin_buffer); | ||
| 353 | |||
| 354 | var client: std.zig.Client = .{ | ||
| 355 | .in = stdout, | ||
| 356 | .out = &stdin_writer.interface, | ||
| 357 | }; | ||
| 358 | |||
| 359 | try client.serveMessageHeader(.{ .tag = .update, .bytes_len = 0 }); | ||
| 360 | try client.serveMessageHeader(.{ .tag = .exit, .bytes_len = 0 }); | ||
| 361 | try client.out.flush(); | ||
| 351 | 362 | ||
| 352 | var result: ?Cache.Path = null; | 363 | var result: ?Cache.Path = null; |
| 353 | var result_error_bundle = std.zig.ErrorBundle.empty; | 364 | var result_error_bundle = std.zig.ErrorBundle.empty; |
| 354 | 365 | ||
| 355 | const stdout = multi_reader.fileReader(0); | ||
| 356 | const MessageHeader = std.zig.Server.Message.Header; | ||
| 357 | |||
| 358 | var eos_err: error{EndOfStream}!void = {}; | 366 | var eos_err: error{EndOfStream}!void = {}; |
| 359 | 367 | ||
| 360 | while (true) { | 368 | while (true) { |
| 361 | const header = stdout.interface.takeStruct(MessageHeader, .little) catch |err| switch (err) { | 369 | const header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) { |
| 362 | error.EndOfStream => break, | 370 | error.Timeout => unreachable, |
| 363 | error.ReadFailed => return stdout.err.?, | ||
| 364 | }; | ||
| 365 | const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) { | ||
| 366 | error.EndOfStream => |e| { | 371 | error.EndOfStream => |e| { |
| 372 | if (client.in.bufferedLen() == 0) break; | ||
| 373 | // Better to report the crash with stderr below, but we set | ||
| 374 | // this in case the child exits successfully while violating | ||
| 375 | // this protocol. | ||
| 367 | eos_err = e; | 376 | eos_err = e; |
| 368 | break; | 377 | break; |
| 369 | }, | 378 | }, |
| 370 | error.ReadFailed => return stdout.err.?, | 379 | else => |e| return e, |
| 371 | }; | 380 | }; |
| 381 | const body = client.in.take(header.bytes_len) catch unreachable; | ||
| 372 | 382 | ||
| 373 | switch (header.tag) { | 383 | switch (header.tag) { |
| 374 | .zig_version => { | 384 | .zig_version => { |
| ... | @@ -435,17 +445,6 @@ fn buildWasmBinary( | ... | @@ -435,17 +445,6 @@ fn buildWasmBinary( |
| 435 | }; | 445 | }; |
| 436 | } | 446 | } |
| 437 | 447 | ||
| 438 | fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void { | ||
| 439 | const header: std.zig.Client.Message.Header = .{ | ||
| 440 | .tag = tag, | ||
| 441 | .bytes_len = 0, | ||
| 442 | }; | ||
| 443 | var w = file.writer(io, &.{}); | ||
| 444 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | ||
| 445 | error.WriteFailed => return w.err.?, | ||
| 446 | }; | ||
| 447 | } | ||
| 448 | |||
| 449 | fn openBrowserTab(io: Io, url: []const u8) !void { | 448 | fn openBrowserTab(io: Io, url: []const u8) !void { |
| 450 | // Until https://github.com/ziglang/zig/issues/19205 is implemented, we | 449 | // Until https://github.com/ziglang/zig/issues/19205 is implemented, we |
| 451 | // spawn and then leak a concurrent task for this child process. | 450 | // spawn and then leak a concurrent task for this child process. |
lib/compiler/test_runner.zig+3-3| ... | @@ -78,11 +78,11 @@ fn mainServer(init: std.process.Init.Minimal) !void { | ... | @@ -78,11 +78,11 @@ fn mainServer(init: std.process.Init.Minimal) !void { |
| 78 | @disableInstrumentation(); | 78 | @disableInstrumentation(); |
| 79 | stdin_reader = .initStreaming(.stdin(), runner_threaded_io, &stdin_buffer); | 79 | stdin_reader = .initStreaming(.stdin(), runner_threaded_io, &stdin_buffer); |
| 80 | stdout_writer = .initStreaming(.stdout(), runner_threaded_io, &stdout_buffer); | 80 | stdout_writer = .initStreaming(.stdout(), runner_threaded_io, &stdout_buffer); |
| 81 | var server = try std.zig.Server.init(.{ | 81 | var server: std.zig.Server = .{ |
| 82 | .in = &stdin_reader.interface, | 82 | .in = &stdin_reader.interface, |
| 83 | .out = &stdout_writer.interface, | 83 | .out = &stdout_writer.interface, |
| 84 | .zig_version = builtin.zig_version_string, | 84 | }; |
| 85 | }); | 85 | try server.serveStringMessage(.zig_version, builtin.zig_version_string); |
| 86 | 86 | ||
| 87 | while (true) { | 87 | while (true) { |
| 88 | const hdr = try server.receiveMessage(); | 88 | const hdr = try server.receiveMessage(); |
lib/std/Io/Reader.zig+3-5| ... | @@ -718,7 +718,7 @@ pub inline fn readSliceEndian( | ... | @@ -718,7 +718,7 @@ pub inline fn readSliceEndian( |
| 718 | endian: std.builtin.Endian, | 718 | endian: std.builtin.Endian, |
| 719 | ) Error!void { | 719 | ) Error!void { |
| 720 | try readSliceAll(r, @ptrCast(buffer)); | 720 | try readSliceAll(r, @ptrCast(buffer)); |
| 721 | if (native_endian != endian) for (buffer) |*elem| std.mem.byteSwapAllFields(Elem, elem); | 721 | if (native_endian != endian) std.mem.byteSwapAllElements(Elem, buffer); |
| 722 | } | 722 | } |
| 723 | 723 | ||
| 724 | pub const ReadAllocError = Error || Allocator.Error; | 724 | pub const ReadAllocError = Error || Allocator.Error; |
| ... | @@ -734,8 +734,7 @@ pub inline fn readSliceEndianAlloc( | ... | @@ -734,8 +734,7 @@ pub inline fn readSliceEndianAlloc( |
| 734 | ) ReadAllocError![]Elem { | 734 | ) ReadAllocError![]Elem { |
| 735 | const dest = try allocator.alloc(Elem, len); | 735 | const dest = try allocator.alloc(Elem, len); |
| 736 | errdefer allocator.free(dest); | 736 | errdefer allocator.free(dest); |
| 737 | try readSliceAll(r, @ptrCast(dest)); | 737 | try r.readSliceEndian(Elem, dest, endian); |
| 738 | if (native_endian != endian) for (dest) |*elem| std.mem.byteSwapAllFields(Elem, elem); | ||
| 739 | return dest; | 738 | return dest; |
| 740 | } | 739 | } |
| 741 | 740 | ||
| ... | @@ -1227,8 +1226,7 @@ pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endia | ... | @@ -1227,8 +1226,7 @@ pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endia |
| 1227 | .auto => @compileError("ill-defined memory layout"), | 1226 | .auto => @compileError("ill-defined memory layout"), |
| 1228 | .@"extern" => { | 1227 | .@"extern" => { |
| 1229 | var res: T = undefined; | 1228 | var res: T = undefined; |
| 1230 | try r.readSliceAll(std.mem.asBytes(&res)); | 1229 | try r.readSliceEndian(T, (&res)[0..1], endian); |
| 1231 | if (native_endian != endian) std.mem.byteSwapAllFields(T, &res); | ||
| 1232 | return res; | 1230 | return res; |
| 1233 | }, | 1231 | }, |
| 1234 | .@"packed" => { | 1232 | .@"packed" => { |
lib/std/mem.zig+66-43| ... | @@ -2215,33 +2215,54 @@ test writeVarPackedInt { | ... | @@ -2215,33 +2215,54 @@ test writeVarPackedInt { |
| 2215 | try testing.expectEqual(T{ .a = 1, .b = value, .c = 4 }, st); | 2215 | try testing.expectEqual(T{ .a = 1, .b = value, .c = 4 }, st); |
| 2216 | } | 2216 | } |
| 2217 | 2217 | ||
| 2218 | /// Swap the byte order of all the members of the fields of a struct | 2218 | /// Deprecated: use `byteSwap` instead. |
| 2219 | /// (Changing their endianness) | 2219 | pub const byteSwapAllFields = byteSwap; |
| 2220 | pub fn byteSwapAllFields(comptime S: type, ptr: *S) void { | 2220 | |
| 2221 | byteSwapAllFieldsAligned(S, .of(S), ptr); | 2221 | /// Deprecated: use `byteSwapAligned` instead. |
| 2222 | pub const byteSwapAllFieldsAligned = byteSwapAligned; | ||
| 2223 | |||
| 2224 | /// Reverses the byte order. | ||
| 2225 | /// Handles structs, unions, arrays, enums, floats, and integers recursively. | ||
| 2226 | /// The order of extern struct fields and array elements remains unchanged and | ||
| 2227 | /// will be byte swapped recursively. | ||
| 2228 | /// Useful for converting between little-endian and big-endian representations. | ||
| 2229 | pub fn byteSwap(comptime S: type, ptr: *S) void { | ||
| 2230 | byteSwapAligned(S, .of(S), ptr); | ||
| 2222 | } | 2231 | } |
| 2223 | 2232 | ||
| 2224 | /// Swap the byte order of all the members of the fields of a struct | 2233 | /// Reverses the byte order. |
| 2225 | /// (Changing their endianness) | 2234 | /// Handles structs, unions, arrays, enums, floats, and integers recursively. |
| 2226 | pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *align(a.toByteUnits()) S) void { | 2235 | /// The order of extern struct fields and array elements remains unchanged and |
| 2236 | /// will be byte swapped recursively. | ||
| 2237 | /// Useful for converting between little-endian and big-endian representations. | ||
| 2238 | pub fn byteSwapAligned( | ||
| 2239 | comptime S: type, | ||
| 2240 | comptime a: Alignment, | ||
| 2241 | ptr: *align(a.toByteUnits()) S, | ||
| 2242 | ) void { | ||
| 2227 | switch (@typeInfo(S)) { | 2243 | switch (@typeInfo(S)) { |
| 2228 | .@"struct" => |@"struct"| { | 2244 | .@"struct" => |@"struct"| { |
| 2229 | if (@"struct".backing_integer) |Int| { | 2245 | if (@"struct".backing_integer) |Int| { |
| 2230 | ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*)))); | 2246 | ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*)))); |
| 2231 | } else inline for (@"struct".field_types, @"struct".field_names, @"struct".field_attrs) |f_type, f_name, f_attr| { | 2247 | } else { |
| 2232 | switch (@typeInfo(f_type)) { | 2248 | if (@"struct".layout != .@"extern") { |
| 2233 | .@"struct" => byteSwapAllFieldsAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)), | 2249 | @compileError("byteSwapAligned expects a packed or extern struct"); |
| 2234 | .@"union", .array => byteSwapAllFieldsAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)), | 2250 | } |
| 2235 | .@"enum" => { | 2251 | inline for (@"struct".field_types, @"struct".field_names, @"struct".field_attrs) |f_type, f_name, f_attr| { |
| 2236 | @field(ptr, f_name) = @fromBackingInt(@intCast(@byteSwap(@backingInt(@field(ptr, f_name))))); | 2252 | switch (@typeInfo(f_type)) { |
| 2237 | }, | 2253 | .@"struct" => byteSwapAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)), |
| 2238 | .bool => {}, | 2254 | .@"union", .array => byteSwapAligned(f_type, .fromByteUnits(f_attr.@"align" orelse @alignOf(f_type)), &@field(ptr, f_name)), |
| 2239 | .float => |float| { | 2255 | .@"enum" => { |
| 2240 | @field(ptr, f_name) = @bitCast(@byteSwap(@as(@Int(.unsigned, float.bits), @bitCast(@field(ptr, f_name))))); | 2256 | @field(ptr, f_name) = @fromBackingInt(@byteSwap(@backingInt(@field(ptr, f_name)))); |
| 2241 | }, | 2257 | }, |
| 2242 | else => { | 2258 | .bool => {}, |
| 2243 | @field(ptr, f_name) = @byteSwap(@field(ptr, f_name)); | 2259 | .float => |float| { |
| 2244 | }, | 2260 | @field(ptr, f_name) = @bitCast(@byteSwap(@as(@Int(.unsigned, float.bits), @bitCast(@field(ptr, f_name))))); |
| 2261 | }, | ||
| 2262 | else => { | ||
| 2263 | @field(ptr, f_name) = @byteSwap(@field(ptr, f_name)); | ||
| 2264 | }, | ||
| 2265 | } | ||
| 2245 | } | 2266 | } |
| 2246 | } | 2267 | } |
| 2247 | }, | 2268 | }, |
| ... | @@ -2249,7 +2270,7 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a | ... | @@ -2249,7 +2270,7 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a |
| 2249 | ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*)))); | 2270 | ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*)))); |
| 2250 | } else { | 2271 | } else { |
| 2251 | if (@"union".layout != .@"extern") { | 2272 | if (@"union".layout != .@"extern") { |
| 2252 | @compileError("byteSwapAllFields expects a packed or extern union"); | 2273 | @compileError("byteSwapAligned expects a packed or extern union"); |
| 2253 | } | 2274 | } |
| 2254 | 2275 | ||
| 2255 | const first_size = @bitSizeOf(@"union".field_types[0]); | 2276 | const first_size = @bitSizeOf(@"union".field_types[0]); |
| ... | @@ -2266,13 +2287,21 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a | ... | @@ -2266,13 +2287,21 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a |
| 2266 | .array => |array| { | 2287 | .array => |array| { |
| 2267 | byteSwapAllElements(array.child, ptr); | 2288 | byteSwapAllElements(array.child, ptr); |
| 2268 | }, | 2289 | }, |
| 2290 | .@"enum" => { | ||
| 2291 | ptr.* = @fromBackingInt(@byteSwap(@backingInt(ptr.*))); | ||
| 2292 | }, | ||
| 2293 | .bool => {}, | ||
| 2294 | .float => |float| { | ||
| 2295 | const int_repr: @Int(.unsigned, float.bits) = @bitCast(ptr.*); | ||
| 2296 | ptr.* = @bitCast(@byteSwap(int_repr)); | ||
| 2297 | }, | ||
| 2269 | else => { | 2298 | else => { |
| 2270 | ptr.* = @byteSwap(ptr.*); | 2299 | ptr.* = @byteSwap(ptr.*); |
| 2271 | }, | 2300 | }, |
| 2272 | } | 2301 | } |
| 2273 | } | 2302 | } |
| 2274 | 2303 | ||
| 2275 | test byteSwapAllFields { | 2304 | test byteSwap { |
| 2276 | const T = extern struct { | 2305 | const T = extern struct { |
| 2277 | f0: u8, | 2306 | f0: u8, |
| 2278 | f1: u16, | 2307 | f1: u16, |
| ... | @@ -2304,6 +2333,9 @@ test byteSwapAllFields { | ... | @@ -2304,6 +2333,9 @@ test byteSwapAllFields { |
| 2304 | } align(4), | 2333 | } align(4), |
| 2305 | f2: u32, | 2334 | f2: u32, |
| 2306 | }; | 2335 | }; |
| 2336 | const E = enum(u32) { | ||
| 2337 | _, | ||
| 2338 | }; | ||
| 2307 | var s = T{ | 2339 | var s = T{ |
| 2308 | .f0 = 0x12, | 2340 | .f0 = 0x12, |
| 2309 | .f1 = 0x1234, | 2341 | .f1 = 0x1234, |
| ... | @@ -2327,10 +2359,14 @@ test byteSwapAllFields { | ... | @@ -2327,10 +2359,14 @@ test byteSwapAllFields { |
| 2327 | .f1 = .{ .f0 = 0x123456789ABCDEF0 }, | 2359 | .f1 = .{ .f0 = 0x123456789ABCDEF0 }, |
| 2328 | .f2 = 0x87654321, | 2360 | .f2 = 0x87654321, |
| 2329 | }; | 2361 | }; |
| 2330 | byteSwapAllFields(T, &s); | 2362 | var e: E = @fromBackingInt(0x12345678); |
| 2331 | byteSwapAllFields(K, &k); | 2363 | var f: f32 = @bitCast(@as(u32, 0x4640e400)); |
| 2332 | byteSwapAllFields(P, &p); | 2364 | byteSwap(T, &s); |
| 2333 | byteSwapAllFields(A, &a); | 2365 | byteSwap(K, &k); |
| 2366 | byteSwap(P, &p); | ||
| 2367 | byteSwap(A, &a); | ||
| 2368 | byteSwap(E, &e); | ||
| 2369 | byteSwap(f32, &f); | ||
| 2334 | try std.testing.expectEqual(T{ | 2370 | try std.testing.expectEqual(T{ |
| 2335 | .f0 = 0x12, | 2371 | .f0 = 0x12, |
| 2336 | .f1 = 0x3412, | 2372 | .f1 = 0x3412, |
| ... | @@ -2354,28 +2390,15 @@ test byteSwapAllFields { | ... | @@ -2354,28 +2390,15 @@ test byteSwapAllFields { |
| 2354 | .f1 = .{ .f0 = 0xF0DEBC9A78563412 }, | 2390 | .f1 = .{ .f0 = 0xF0DEBC9A78563412 }, |
| 2355 | .f2 = 0x21436587, | 2391 | .f2 = 0x21436587, |
| 2356 | }, a); | 2392 | }, a); |
| 2393 | try std.testing.expectEqual(@as(E, @fromBackingInt(0x78563412)), e); | ||
| 2394 | try std.testing.expectEqual(@as(f32, @bitCast(@as(u32, 0x00e44046))), f); | ||
| 2357 | } | 2395 | } |
| 2358 | 2396 | ||
| 2359 | /// Reverses the byte order of all elements in a slice. | 2397 | /// Reverses the byte order of all elements in a slice. |
| 2360 | /// Handles structs, unions, arrays, enums, floats, and integers recursively. | 2398 | /// Handles structs, unions, arrays, enums, floats, and integers recursively. |
| 2361 | /// Useful for converting between little-endian and big-endian representations. | 2399 | /// Useful for converting between little-endian and big-endian representations. |
| 2362 | pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void { | 2400 | pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void { |
| 2363 | for (slice) |*elem| { | 2401 | for (slice) |*elem| byteSwap(Elem, elem); |
| 2364 | switch (@typeInfo(@TypeOf(elem.*))) { | ||
| 2365 | .@"struct", .@"union", .array => byteSwapAllFields(@TypeOf(elem.*), elem), | ||
| 2366 | .@"enum" => { | ||
| 2367 | elem.* = @fromBackingInt(@intCast(@byteSwap(@backingInt(elem.*)))); | ||
| 2368 | }, | ||
| 2369 | .bool => {}, | ||
| 2370 | .float => |float| { | ||
| 2371 | const int_repr: @Int(.unsigned, float.bits) = @bitCast(elem.*); | ||
| 2372 | elem.* = @bitCast(@byteSwap(int_repr)); | ||
| 2373 | }, | ||
| 2374 | else => { | ||
| 2375 | elem.* = @byteSwap(elem.*); | ||
| 2376 | }, | ||
| 2377 | } | ||
| 2378 | } | ||
| 2379 | } | 2402 | } |
| 2380 | 2403 | ||
| 2381 | /// Returns an iterator that iterates over the slices of `buffer` that are not | 2404 | /// Returns an iterator that iterates over the slices of `buffer` that are not |
lib/std/zig.zig+46-58| ... | @@ -1658,31 +1658,32 @@ pub fn buildExeSubprocess( | ... | @@ -1658,31 +1658,32 @@ pub fn buildExeSubprocess( |
| 1658 | }; | 1658 | }; |
| 1659 | defer child.kill(io); | 1659 | defer child.kill(io); |
| 1660 | 1660 | ||
| 1661 | var stderr_task = io.concurrent(readStreamAlloc, .{ gpa, io, child.stderr.?, .unlimited }) catch | 1661 | var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined; |
| 1662 | @panic("TODO use multireader instead"); | 1662 | var multi_reader: Io.File.MultiReader = undefined; |
| 1663 | defer if (stderr_task.cancel(io)) |slice| gpa.free(slice) else |_| {}; | 1663 | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); |
| 1664 | 1664 | defer multi_reader.deinit(); | |
| 1665 | var stdout_buffer: [512]u8 = undefined; | 1665 | const stdout = multi_reader.reader(0); |
| 1666 | var stdout_reader: Io.File.Reader = .initStreaming(child.stdout.?, io, &stdout_buffer); | 1666 | const stderr = multi_reader.reader(1); |
| 1667 | const stdout = &stdout_reader.interface; | 1667 | |
| 1668 | 1668 | var stdin_buffer: [8]u8 = undefined; | |
| 1669 | { | 1669 | var stdin_writer = child.stdin.?.writerStreaming(io, &stdin_buffer); |
| 1670 | var w = child.stdin.?.writer(io, &.{}); | 1670 | |
| 1671 | w.interface.writeStruct(Client.Message.Header{ .tag = .update, .bytes_len = 0 }, .little) catch |err| switch (err) { | 1671 | var client: Client = .{ |
| 1672 | error.WriteFailed => { | 1672 | .in = stdout, |
| 1673 | log.err("{t} writing to command: {f}", .{ w.err.?, cmd }); | 1673 | .out = &stdin_writer.interface, |
| 1674 | return error.AlreadyReported; | 1674 | }; |
| 1675 | }, | ||
| 1676 | }; | ||
| 1677 | w.interface.writeStruct(Client.Message.Header{ .tag = .exit, .bytes_len = 0 }, .little) catch |err| switch (err) { | ||
| 1678 | error.WriteFailed => { | ||
| 1679 | log.err("{t} writing to command: {f}", .{ w.err.?, cmd }); | ||
| 1680 | return error.AlreadyReported; | ||
| 1681 | }, | ||
| 1682 | }; | ||
| 1683 | } | ||
| 1684 | 1675 | ||
| 1685 | const Header = Server.Message.Header; | 1676 | (blk: { |
| 1677 | client.serveMessageHeader(.{ .tag = .update, .bytes_len = 0 }) catch |err| break :blk err; | ||
| 1678 | client.serveMessageHeader(.{ .tag = .exit, .bytes_len = 0 }) catch |err| break :blk err; | ||
| 1679 | client.out.flush() catch |err| break :blk err; | ||
| 1680 | }) catch |err| switch (err) { | ||
| 1681 | error.WriteFailed => { | ||
| 1682 | if (stdin_writer.err.? == error.Canceled) return error.Canceled; | ||
| 1683 | log.err("{t} writing to command: {f}", .{ stdin_writer.err.?, cmd }); | ||
| 1684 | return error.AlreadyReported; | ||
| 1685 | }, | ||
| 1686 | }; | ||
| 1686 | 1687 | ||
| 1687 | var result: ?Cache.Path = null; | 1688 | var result: ?Cache.Path = null; |
| 1688 | defer if (result) |r| gpa.free(r.sub_path); | 1689 | defer if (result) |r| gpa.free(r.sub_path); |
| ... | @@ -1690,33 +1691,29 @@ pub fn buildExeSubprocess( | ... | @@ -1690,33 +1691,29 @@ pub fn buildExeSubprocess( |
| 1690 | var result_error_bundle: ErrorBundle = .empty; | 1691 | var result_error_bundle: ErrorBundle = .empty; |
| 1691 | defer result_error_bundle.deinit(gpa); | 1692 | defer result_error_bundle.deinit(gpa); |
| 1692 | 1693 | ||
| 1693 | var body_buffer: std.ArrayList(u8) = .empty; | ||
| 1694 | defer body_buffer.deinit(gpa); | ||
| 1695 | |||
| 1696 | var received_fs_inputs = false; | 1694 | var received_fs_inputs = false; |
| 1697 | var cache_hit = false; | 1695 | var cache_hit = false; |
| 1698 | 1696 | ||
| 1697 | var eos_err: error{EndOfStream}!void = {}; | ||
| 1698 | |||
| 1699 | while (true) { | 1699 | while (true) { |
| 1700 | const header = stdout.takeStruct(Header, .little) catch |err| switch (err) { | 1700 | const header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) { |
| 1701 | error.ReadFailed => { | 1701 | error.Timeout => unreachable, |
| 1702 | log.err("{t} reading from command: {f}", .{ stdout_reader.err.?, cmd }); | 1702 | error.EndOfStream => |e| { |
| 1703 | return error.AlreadyReported; | 1703 | if (client.in.bufferedLen() == 0) break; |
| 1704 | }, | 1704 | // Better to report the crash with stderr below, but we set |
| 1705 | error.EndOfStream => break, | 1705 | // this in case the child exits successfully while violating |
| 1706 | }; | 1706 | // this protocol. |
| 1707 | body_buffer.clearRetainingCapacity(); | 1707 | eos_err = e; |
| 1708 | stdout.appendExact(gpa, &body_buffer, header.bytes_len) catch |err| switch (err) { | 1708 | break; |
| 1709 | error.ReadFailed => { | ||
| 1710 | log.err("{t} reading from command: {f}", .{ stdout_reader.err.?, cmd }); | ||
| 1711 | return error.AlreadyReported; | ||
| 1712 | }, | 1709 | }, |
| 1713 | error.OutOfMemory => |e| return e, | 1710 | error.Canceled, error.OutOfMemory => |e| return e, |
| 1714 | error.EndOfStream => { | 1711 | else => |e| { |
| 1715 | log.err("unexpected end of stream from command: {f}", .{cmd}); | 1712 | log.err("{t} reading from command: {f}", .{ e, cmd }); |
| 1716 | return error.AlreadyReported; | 1713 | return error.AlreadyReported; |
| 1717 | }, | 1714 | }, |
| 1718 | }; | 1715 | }; |
| 1719 | const body = body_buffer.items; | 1716 | const body = stdout.take(header.bytes_len) catch unreachable; |
| 1720 | 1717 | ||
| 1721 | switch (header.tag) { | 1718 | switch (header.tag) { |
| 1722 | .zig_version => { | 1719 | .zig_version => { |
| ... | @@ -1767,16 +1764,15 @@ pub fn buildExeSubprocess( | ... | @@ -1767,16 +1764,15 @@ pub fn buildExeSubprocess( |
| 1767 | } | 1764 | } |
| 1768 | } | 1765 | } |
| 1769 | 1766 | ||
| 1770 | const stderr_contents = stderr_task.await(io) catch |err| switch (err) { | 1767 | const stderr_contents = stderr.buffered(); |
| 1771 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1772 | else => |e| c: { | ||
| 1773 | log.warn("{t} reading stderr from command: {f}", .{ e, cmd }); | ||
| 1774 | break :c ""; | ||
| 1775 | }, | ||
| 1776 | }; | ||
| 1777 | if (stderr_contents.len > 0) | 1768 | if (stderr_contents.len > 0) |
| 1778 | log.warn("unexpected stderr from {s} command:\n{s}", .{ options.argv[0], stderr_contents }); | 1769 | log.warn("unexpected stderr from {s} command:\n{s}", .{ options.argv[0], stderr_contents }); |
| 1779 | 1770 | ||
| 1771 | eos_err catch { | ||
| 1772 | log.err("unexpected end of stream from command: {f}", .{cmd}); | ||
| 1773 | return error.AlreadyReported; | ||
| 1774 | }; | ||
| 1775 | |||
| 1780 | // Send EOF to stdin. | 1776 | // Send EOF to stdin. |
| 1781 | child.stdin.?.close(io); | 1777 | child.stdin.?.close(io); |
| 1782 | child.stdin = null; | 1778 | child.stdin = null; |
| ... | @@ -1834,14 +1830,6 @@ pub fn buildExeSubprocess( | ... | @@ -1834,14 +1830,6 @@ pub fn buildExeSubprocess( |
| 1834 | }; | 1830 | }; |
| 1835 | } | 1831 | } |
| 1836 | 1832 | ||
| 1837 | fn readStreamAlloc(gpa: Allocator, io: Io, file: Io.File, limit: Io.Limit) ![]u8 { | ||
| 1838 | var file_reader: Io.File.Reader = .initStreaming(file, io, &.{}); | ||
| 1839 | return file_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) { | ||
| 1840 | error.ReadFailed => return file_reader.err.?, | ||
| 1841 | else => |e| return e, | ||
| 1842 | }; | ||
| 1843 | } | ||
| 1844 | |||
| 1845 | test { | 1833 | test { |
| 1846 | _ = Ast; | 1834 | _ = Ast; |
| 1847 | _ = AstRlAnnotate; | 1835 | _ = AstRlAnnotate; |
lib/std/zig/Client.zig+126-2| ... | @@ -1,3 +1,18 @@ | ... | @@ -1,3 +1,18 @@ |
| 1 | const Client = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const Io = std.Io; | ||
| 5 | const Allocator = std.mem.Allocator; | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const Configuration = std.Build.Configuration; | ||
| 8 | const OutMessage = std.zig.Client.Message; | ||
| 9 | const InMessage = std.zig.Server.Message; | ||
| 10 | const Reader = Io.Reader; | ||
| 11 | const Writer = Io.Writer; | ||
| 12 | |||
| 13 | in: *Reader, | ||
| 14 | out: *Writer, | ||
| 15 | |||
| 1 | pub const Message = struct { | 16 | pub const Message = struct { |
| 2 | pub const Header = extern struct { | 17 | pub const Header = extern struct { |
| 3 | tag: Tag, | 18 | tag: Tag, |
| ... | @@ -46,11 +61,120 @@ pub const Message = struct { | ... | @@ -46,11 +61,120 @@ pub const Message = struct { |
| 46 | /// The message body has the same format as in Server. | 61 | /// The message body has the same format as in Server. |
| 47 | new_fuzz_input, | 62 | new_fuzz_input, |
| 48 | 63 | ||
| 64 | /// Asks the server to run a list of steps. | ||
| 65 | /// Body is a `BuildSteps`. | ||
| 66 | /// This message only applies to the build system protocol. | ||
| 67 | bsp_build_steps = 0x80000000, | ||
| 68 | |||
| 49 | _, | 69 | _, |
| 50 | }; | 70 | }; |
| 51 | 71 | ||
| 72 | /// Trailing: | ||
| 73 | /// * step_indices: [step_count]std.Build.Configuration.Step.Index, | ||
| 74 | pub const BuildSteps = extern struct { | ||
| 75 | step_count: u32, | ||
| 76 | flags: Flags, | ||
| 77 | |||
| 78 | pub const Flags = packed struct(u32) { | ||
| 79 | /// Can only be enabled when the server declared support for file | ||
| 80 | /// watching. | ||
| 81 | watch: bool, | ||
| 82 | reserved: u31 = 0, | ||
| 83 | }; | ||
| 84 | }; | ||
| 85 | |||
| 52 | comptime { | 86 | comptime { |
| 53 | const std = @import("std"); | 87 | assert(@sizeOf(std.Build.abi.fuzz.LimitKind) == 1); |
| 54 | std.debug.assert(@sizeOf(std.Build.abi.fuzz.LimitKind) == 1); | ||
| 55 | } | 88 | } |
| 56 | }; | 89 | }; |
| 90 | |||
| 91 | pub fn receiveMessage(c: *const Client) Reader.Error!InMessage.Header { | ||
| 92 | return c.in.takeStruct(InMessage.Header, .little); | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Assumes that `c.in` is a reader in `multi_reader`. | ||
| 96 | /// Guarantees that the response body will be buffered in `c.in` on success. | ||
| 97 | pub fn receiveMessageWithMultiReader( | ||
| 98 | c: *Client, | ||
| 99 | multi_reader: *Io.File.MultiReader, | ||
| 100 | timeout: Io.Timeout, | ||
| 101 | ) (Io.File.MultiReader.Error || Io.Timeout.Error)!InMessage.Header { | ||
| 102 | while (c.in.bufferedLen() < @sizeOf(InMessage.Header)) { | ||
| 103 | multi_reader.fill(64, timeout) catch |err| switch (err) { | ||
| 104 | error.Canceled, | ||
| 105 | error.Timeout, | ||
| 106 | error.ConcurrencyUnavailable, | ||
| 107 | error.EndOfStream, | ||
| 108 | => |e| return e, | ||
| 109 | }; | ||
| 110 | } | ||
| 111 | const header = c.in.takeStruct(InMessage.Header, .little) catch unreachable; | ||
| 112 | while (c.in.bufferedLen() < header.bytes_len) { | ||
| 113 | try multi_reader.fill(header.bytes_len - c.in.bufferedLen(), timeout); | ||
| 114 | } | ||
| 115 | try multi_reader.checkAnyError(); | ||
| 116 | return header; | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Don't forget to flush! | ||
| 120 | pub fn serveMessageHeader(c: *const Client, header: OutMessage.Header) Writer.Error!void { | ||
| 121 | try c.out.writeStruct(header, .little); | ||
| 122 | } | ||
| 123 | |||
| 124 | pub fn serveBodylessMessage(c: *const Client, tag: OutMessage.Tag) Writer.Error!void { | ||
| 125 | try c.serveMessageHeader(.{ .tag = tag, .bytes_len = 0 }); | ||
| 126 | try c.out.flush(); | ||
| 127 | } | ||
| 128 | |||
| 129 | pub fn serveRunTest(c: *const Client, index: u32) !void { | ||
| 130 | try c.serveMessageHeader(.{ | ||
| 131 | .tag = .run_test, | ||
| 132 | .bytes_len = @sizeOf(u32), | ||
| 133 | }); | ||
| 134 | try c.out.writeInt(u32, index, .little); | ||
| 135 | try c.out.flush(); | ||
| 136 | } | ||
| 137 | |||
| 138 | pub fn serveRunFuzzTestMessage( | ||
| 139 | c: *const Client, | ||
| 140 | test_names: []const []const u8, | ||
| 141 | kind: std.Build.abi.fuzz.LimitKind, | ||
| 142 | amount_or_instance: u64, | ||
| 143 | ) !void { | ||
| 144 | try c.serveMessageHeader(.{ | ||
| 145 | .tag = .start_fuzzing, | ||
| 146 | .bytes_len = 1 + 8 + 4 + count: { | ||
| 147 | var bytes_len: u32 = @intCast(test_names.len * 4); | ||
| 148 | for (test_names) |name| { | ||
| 149 | bytes_len += @intCast(name.len); | ||
| 150 | } | ||
| 151 | break :count bytes_len; | ||
| 152 | }, | ||
| 153 | }); | ||
| 154 | try c.out.writeByte(@backingInt(kind)); | ||
| 155 | try c.out.writeInt(u64, amount_or_instance, .little); | ||
| 156 | try c.out.writeInt(u32, @intCast(test_names.len), .little); | ||
| 157 | for (test_names) |test_name| { | ||
| 158 | try c.out.writeInt(u32, @intCast(test_name.len), .little); | ||
| 159 | try c.out.writeAll(test_name); | ||
| 160 | } | ||
| 161 | try c.out.flush(); | ||
| 162 | } | ||
| 163 | |||
| 164 | pub fn serveBuildSteps( | ||
| 165 | c: *const Client, | ||
| 166 | steps: []const Configuration.Step.Index, | ||
| 167 | flags: OutMessage.BuildSteps.Flags, | ||
| 168 | ) !void { | ||
| 169 | try c.serveMessageHeader(.{ | ||
| 170 | .tag = .bsp_build_steps, | ||
| 171 | .bytes_len = @intCast(@sizeOf(OutMessage.BuildSteps) + steps.len * @sizeOf(Configuration.Step.Index)), | ||
| 172 | }); | ||
| 173 | const body: OutMessage.BuildSteps = .{ | ||
| 174 | .step_count = @intCast(steps.len), | ||
| 175 | .flags = flags, | ||
| 176 | }; | ||
| 177 | try c.out.writeStruct(body, .little); | ||
| 178 | try c.out.writeSliceEndian(Configuration.Step.Index, steps, .little); | ||
| 179 | try c.out.flush(); | ||
| 180 | } |
lib/std/zig/Server.zig+66-19| ... | @@ -1,12 +1,8 @@ | ... | @@ -1,12 +1,8 @@ |
| 1 | const Server = @This(); | 1 | const Server = @This(); |
| 2 | 2 | ||
| 3 | const builtin = @import("builtin"); | ||
| 4 | |||
| 5 | const std = @import("std"); | 3 | const std = @import("std"); |
| 6 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 7 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 8 | const native_endian = builtin.target.cpu.arch.endian(); | ||
| 9 | const need_bswap = native_endian != .little; | ||
| 10 | const Cache = std.Build.Cache; | 6 | const Cache = std.Build.Cache; |
| 11 | const OutMessage = std.zig.Server.Message; | 7 | const OutMessage = std.zig.Server.Message; |
| 12 | const InMessage = std.zig.Client.Message; | 8 | const InMessage = std.zig.Client.Message; |
| ... | @@ -16,6 +12,14 @@ const Writer = std.Io.Writer; | ... | @@ -16,6 +12,14 @@ const Writer = std.Io.Writer; |
| 16 | in: *Reader, | 12 | in: *Reader, |
| 17 | out: *Writer, | 13 | out: *Writer, |
| 18 | 14 | ||
| 15 | /// The ABI version of the build system protocol. Will be bumped whenever a | ||
| 16 | /// backwards incompatible changes to the protocol is made. | ||
| 17 | /// | ||
| 18 | /// Does not apply to the internal compiler protocol or test runner. | ||
| 19 | /// | ||
| 20 | /// See `version` in `Message.Handshake`. | ||
| 21 | pub const build_system_version: u32 = 1; | ||
| 22 | |||
| 19 | pub const Message = struct { | 23 | pub const Message = struct { |
| 20 | pub const Header = extern struct { | 24 | pub const Header = extern struct { |
| 21 | tag: Tag, | 25 | tag: Tag, |
| ... | @@ -70,9 +74,62 @@ pub const Message = struct { | ... | @@ -70,9 +74,62 @@ pub const Message = struct { |
| 70 | /// Body is a TimeReport. | 74 | /// Body is a TimeReport. |
| 71 | time_report, | 75 | time_report, |
| 72 | 76 | ||
| 77 | /// The first message sent by the server over the build system protocol. | ||
| 78 | /// Body is a `Handshake`. | ||
| 79 | /// This message only applies to the build system protocol. | ||
| 80 | bsp_handshake = 0x80000000, | ||
| 81 | /// Notifies that a new configuration file is available. | ||
| 82 | /// Body is a cwd relative path to the configuration file. | ||
| 83 | /// This message only applies to the build system protocol. | ||
| 84 | bsp_configuration, | ||
| 85 | /// Does not have a body. | ||
| 86 | /// This message only applies to the build system protocol. | ||
| 87 | bsp_build_started, | ||
| 88 | /// Does not have a body. | ||
| 89 | /// This message only applies to the build system protocol. | ||
| 90 | bsp_build_completed, | ||
| 91 | /// Body is a `Configuration.Step.Index`. | ||
| 92 | /// This message only applies to the build system protocol. | ||
| 93 | bsp_step_started, | ||
| 94 | /// Body is a `BuildStepCompleted`. | ||
| 95 | /// This message only applies to the build system protocol. | ||
| 96 | bsp_step_completed, | ||
| 97 | |||
| 73 | _, | 98 | _, |
| 74 | }; | 99 | }; |
| 75 | 100 | ||
| 101 | /// Trailing: | ||
| 102 | /// * base_paths: BasePaths, | ||
| 103 | pub const Handshake = extern struct { | ||
| 104 | /// See `build_system_version`. | ||
| 105 | version: u32, | ||
| 106 | flags: Flags, | ||
| 107 | |||
| 108 | pub const Flags = packed struct(u32) { | ||
| 109 | file_system_watch_supported: bool, | ||
| 110 | _: u31 = 0, | ||
| 111 | }; | ||
| 112 | }; | ||
| 113 | |||
| 114 | /// Trailing: | ||
| 115 | /// * error_bundle: ErrorBundle, | ||
| 116 | pub const BuildStepCompleted = extern struct { | ||
| 117 | step_index: std.Build.Configuration.Step.Index, | ||
| 118 | status: Status, | ||
| 119 | error_bundle: ErrorBundle, | ||
| 120 | // TODO result_error_msgs | ||
| 121 | // TODO result_stderr | ||
| 122 | // TODO result_peak_rss | ||
| 123 | // TODO result_duration_ns | ||
| 124 | |||
| 125 | pub const Status = enum(u32) { | ||
| 126 | success, | ||
| 127 | failure, | ||
| 128 | skipped, | ||
| 129 | skipped_oom, | ||
| 130 | }; | ||
| 131 | }; | ||
| 132 | |||
| 76 | pub const PathPrefix = enum(u8) { | 133 | pub const PathPrefix = enum(u8) { |
| 77 | cwd, | 134 | cwd, |
| 78 | zig_lib, | 135 | zig_lib, |
| ... | @@ -140,21 +197,6 @@ pub const Message = struct { | ... | @@ -140,21 +197,6 @@ pub const Message = struct { |
| 140 | }; | 197 | }; |
| 141 | }; | 198 | }; |
| 142 | 199 | ||
| 143 | pub const Options = struct { | ||
| 144 | in: *Reader, | ||
| 145 | out: *Writer, | ||
| 146 | zig_version: []const u8, | ||
| 147 | }; | ||
| 148 | |||
| 149 | pub fn init(options: Options) !Server { | ||
| 150 | var s: Server = .{ | ||
| 151 | .in = options.in, | ||
| 152 | .out = options.out, | ||
| 153 | }; | ||
| 154 | try s.serveStringMessage(.zig_version, options.zig_version); | ||
| 155 | return s; | ||
| 156 | } | ||
| 157 | |||
| 158 | pub fn receiveMessage(s: *Server) !InMessage.Header { | 200 | pub fn receiveMessage(s: *Server) !InMessage.Header { |
| 159 | return s.in.takeStruct(InMessage.Header, .little); | 201 | return s.in.takeStruct(InMessage.Header, .little); |
| 160 | } | 202 | } |
| ... | @@ -183,6 +225,11 @@ pub fn serveMessageHeader(s: *const Server, header: OutMessage.Header) !void { | ... | @@ -183,6 +225,11 @@ pub fn serveMessageHeader(s: *const Server, header: OutMessage.Header) !void { |
| 183 | try s.out.writeStruct(header, .little); | 225 | try s.out.writeStruct(header, .little); |
| 184 | } | 226 | } |
| 185 | 227 | ||
| 228 | pub fn serveBodylessMessage(s: *const Server, tag: OutMessage.Tag) Writer.Error!void { | ||
| 229 | try s.serveMessageHeader(.{ .tag = tag, .bytes_len = 0 }); | ||
| 230 | try s.out.flush(); | ||
| 231 | } | ||
| 232 | |||
| 186 | pub fn serveU32Message(s: *const Server, tag: OutMessage.Tag, int: u32) !void { | 233 | pub fn serveU32Message(s: *const Server, tag: OutMessage.Tag, int: u32) !void { |
| 187 | try serveMessageHeader(s, .{ | 234 | try serveMessageHeader(s, .{ |
| 188 | .tag = tag, | 235 | .tag = tag, |
src/Compilation.zig+12-8| ... | @@ -6028,26 +6028,30 @@ fn spawnZigRc( | ... | @@ -6028,26 +6028,30 @@ fn spawnZigRc( |
| 6028 | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); | 6028 | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); |
| 6029 | defer multi_reader.deinit(); | 6029 | defer multi_reader.deinit(); |
| 6030 | 6030 | ||
| 6031 | const stdout = multi_reader.fileReader(0); | 6031 | const stdout = multi_reader.reader(0); |
| 6032 | const MessageHeader = std.zig.Server.Message.Header; | ||
| 6033 | 6032 | ||
| 6034 | var eos_err: error{EndOfStream}!void = {}; | 6033 | var eos_err: error{EndOfStream}!void = {}; |
| 6035 | 6034 | ||
| 6035 | var client: std.zig.Client = .{ | ||
| 6036 | .in = stdout, | ||
| 6037 | .out = undefined, | ||
| 6038 | }; | ||
| 6039 | |||
| 6036 | while (true) { | 6040 | while (true) { |
| 6037 | const header = stdout.interface.takeStruct(MessageHeader, .little) catch |err| switch (err) { | 6041 | const header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) { |
| 6038 | error.EndOfStream => break, | 6042 | error.Timeout => unreachable, |
| 6039 | error.ReadFailed => return stdout.err.?, | ||
| 6040 | }; | ||
| 6041 | const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) { | ||
| 6042 | error.EndOfStream => |e| { | 6043 | error.EndOfStream => |e| { |
| 6044 | if (client.in.bufferedLen() == 0) break; | ||
| 6043 | // Better to report the crash with stderr below, but we set | 6045 | // Better to report the crash with stderr below, but we set |
| 6044 | // this in case the child exits successfully while violating | 6046 | // this in case the child exits successfully while violating |
| 6045 | // this protocol. | 6047 | // this protocol. |
| 6046 | eos_err = e; | 6048 | eos_err = e; |
| 6047 | break; | 6049 | break; |
| 6048 | }, | 6050 | }, |
| 6049 | error.ReadFailed => return stdout.err.?, | 6051 | else => |e| return e, |
| 6050 | }; | 6052 | }; |
| 6053 | const body = client.in.take(header.bytes_len) catch unreachable; | ||
| 6054 | |||
| 6051 | switch (header.tag) { | 6055 | switch (header.tag) { |
| 6052 | // We expect exactly one ErrorBundle, and if any error_bundle header is | 6056 | // We expect exactly one ErrorBundle, and if any error_bundle header is |
| 6053 | // sent then it's a fatal error. | 6057 | // sent then it's a fatal error. |
src/main.zig+2-5| ... | @@ -4297,11 +4297,8 @@ fn serve( | ... | @@ -4297,11 +4297,8 @@ fn serve( |
| 4297 | const gpa = comp.gpa; | 4297 | const gpa = comp.gpa; |
| 4298 | const io = comp.io; | 4298 | const io = comp.io; |
| 4299 | 4299 | ||
| 4300 | var server = try Server.init(.{ | 4300 | var server: Server = .{ .in = in, .out = out }; |
| 4301 | .in = in, | 4301 | try server.serveStringMessage(.zig_version, build_options.version); |
| 4302 | .out = out, | ||
| 4303 | .zig_version = build_options.version, | ||
| 4304 | }); | ||
| 4305 | 4302 | ||
| 4306 | var child_pid: ?std.process.Child.Id = null; | 4303 | var child_pid: ?std.process.Child.Id = null; |
| 4307 | 4304 |
test/standalone/build.zig+1| ... | @@ -31,6 +31,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -31,6 +31,7 @@ pub fn build(b: *std.Build) void { |
| 31 | const tools_target = b.resolveTargetQuery(.{}); | 31 | const tools_target = b.resolveTargetQuery(.{}); |
| 32 | for ([_][]const u8{ | 32 | for ([_][]const u8{ |
| 33 | // Alphabetically sorted. No need to build `tools/spirv/grammar.zig`. | 33 | // Alphabetically sorted. No need to build `tools/spirv/grammar.zig`. |
| 34 | "../../tools/bsp.zig", | ||
| 34 | "../../tools/check_mingw.zig", | 35 | "../../tools/check_mingw.zig", |
| 35 | "../../tools/dump-cov.zig", | 36 | "../../tools/dump-cov.zig", |
| 36 | "../../tools/fetch_them_macos_headers.zig", | 37 | "../../tools/fetch_them_macos_headers.zig", |
tools/bsp.zig created+242| ... | @@ -0,0 +1,242 @@ | ||
| 1 | //! CLI tool to interface with the build system protocol (zig build --listen=-) | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const Io = std.Io; | ||
| 5 | const Allocator = std.mem.Allocator; | ||
| 6 | const Configuration = std.Build.Configuration; | ||
| 7 | const Client = std.zig.Client; | ||
| 8 | const Server = std.zig.Server; | ||
| 9 | const log = std.log.scoped(.bsp); | ||
| 10 | |||
| 11 | pub fn main(init: std.process.Init) !void { | ||
| 12 | const io = init.io; | ||
| 13 | const gpa = init.gpa; | ||
| 14 | const arena = init.arena.allocator(); | ||
| 15 | |||
| 16 | var maker_args: std.ArrayList([]const u8) = .empty; | ||
| 17 | |||
| 18 | const args = try init.minimal.args.toSlice(arena); | ||
| 19 | for (args[1..]) |arg| { | ||
| 20 | try maker_args.append(arena, try arena.dupe(u8, arg)); | ||
| 21 | } | ||
| 22 | if (maker_args.items.len < 1) try maker_args.append(arena, "zig"); | ||
| 23 | if (maker_args.items.len < 2) try maker_args.append(arena, "build"); | ||
| 24 | if (!std.mem.eql(u8, maker_args.last().?.*, "--listen=-")) try maker_args.append(arena, "--listen=-"); | ||
| 25 | |||
| 26 | log.debug("cmd: {f}", .{std.zig.SubprocessCommand{ | ||
| 27 | .argv = maker_args.items, | ||
| 28 | }}); | ||
| 29 | |||
| 30 | var child_process = std.process.spawn(io, .{ | ||
| 31 | .argv = maker_args.items, | ||
| 32 | .stdin = .pipe, | ||
| 33 | .stdout = .pipe, | ||
| 34 | .stderr = .pipe, | ||
| 35 | }) catch |err| std.debug.panic("failed to spawn process: {}", .{err}); | ||
| 36 | errdefer child_process.kill(io); | ||
| 37 | |||
| 38 | var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined; | ||
| 39 | var multi_reader: Io.File.MultiReader = undefined; | ||
| 40 | defer multi_reader.deinit(); | ||
| 41 | multi_reader.init( | ||
| 42 | gpa, | ||
| 43 | io, | ||
| 44 | multi_reader_buffer.toStreams(), | ||
| 45 | &.{ child_process.stdout.?, child_process.stderr.? }, | ||
| 46 | ); | ||
| 47 | const client_stdout = multi_reader.reader(0); | ||
| 48 | const client_stderr = multi_reader.reader(1); | ||
| 49 | |||
| 50 | var client_stdout_buffer: [256]u8 = undefined; | ||
| 51 | var client_stdout_writer = child_process.stdin.?.writerStreaming(io, &client_stdout_buffer); | ||
| 52 | |||
| 53 | var client: Client = .{ | ||
| 54 | .in = client_stdout, | ||
| 55 | .out = &client_stdout_writer.interface, | ||
| 56 | }; | ||
| 57 | |||
| 58 | const err = blk: { | ||
| 59 | const handshake: Server.Message.Handshake = handshake: { | ||
| 60 | const header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) { | ||
| 61 | error.Canceled, error.ConcurrencyUnavailable => |e| return e, | ||
| 62 | error.Timeout => unreachable, | ||
| 63 | else => |e| { | ||
| 64 | log.err("failed to receive message: {t}", .{err}); | ||
| 65 | break :blk e; | ||
| 66 | }, | ||
| 67 | }; | ||
| 68 | const body = client_stdout.take(header.bytes_len) catch unreachable; | ||
| 69 | log.debug("received {f} ({d} bytes)", .{ fmtEnum(header.tag), body.len }); | ||
| 70 | |||
| 71 | if (header.tag != .bsp_handshake) { | ||
| 72 | log.err("received unexpected message: {f}", .{fmtEnum(header.tag)}); | ||
| 73 | return error.UnexpectedMessage; | ||
| 74 | } | ||
| 75 | |||
| 76 | var r: Io.Reader = .fixed(body); | ||
| 77 | break :handshake try r.takeStruct(Server.Message.Handshake, .little); | ||
| 78 | }; | ||
| 79 | _ = handshake; | ||
| 80 | |||
| 81 | var conf_arena_allocator: std.heap.ArenaAllocator = .init(gpa); | ||
| 82 | defer conf_arena_allocator.deinit(); | ||
| 83 | const conf_arena = conf_arena_allocator.allocator(); | ||
| 84 | |||
| 85 | const configuration = configuration: { | ||
| 86 | const header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) { | ||
| 87 | error.Canceled, error.ConcurrencyUnavailable => |e| return e, | ||
| 88 | error.Timeout => unreachable, | ||
| 89 | else => |e| { | ||
| 90 | log.err("failed to receive message: {t}", .{err}); | ||
| 91 | break :blk e; | ||
| 92 | }, | ||
| 93 | }; | ||
| 94 | const body = client_stdout.take(header.bytes_len) catch unreachable; | ||
| 95 | log.debug("received {t} ({d} bytes)", .{ header.tag, body.len }); | ||
| 96 | |||
| 97 | if (header.tag != .bsp_configuration) { | ||
| 98 | log.err("received unexpected message: {f}", .{fmtEnum(header.tag)}); | ||
| 99 | return error.UnexpectedMessage; | ||
| 100 | } | ||
| 101 | |||
| 102 | const configuration_path = body; | ||
| 103 | var file = Io.Dir.cwd().openFile(io, configuration_path, .{}) catch |err| | ||
| 104 | std.debug.panic("failed to open configuration file {q}: {t}", .{ configuration_path, err }); | ||
| 105 | defer file.close(io); | ||
| 106 | break :configuration Configuration.loadFile(conf_arena, io, file) catch |err| | ||
| 107 | std.debug.panic("failed to load configuration file {q}: {t}", .{ configuration_path, err }); | ||
| 108 | }; | ||
| 109 | const c = &configuration; | ||
| 110 | |||
| 111 | var top_level_steps: std.array_hash_map.String(Configuration.Step.Index) = .empty; | ||
| 112 | defer top_level_steps.deinit(gpa); | ||
| 113 | |||
| 114 | for (c.steps, 0..) |*conf_step, step_index_usize| { | ||
| 115 | if (conf_step.owner != .root) continue; | ||
| 116 | const step_index: Configuration.Step.Index = @fromBackingInt(@intCast(step_index_usize)); | ||
| 117 | const flags = conf_step.flags(c); | ||
| 118 | if (flags.tag != .top_level) continue; | ||
| 119 | const name = step_index.ptr(c).name.slice(c); | ||
| 120 | try top_level_steps.putNoClobber(gpa, name, step_index); | ||
| 121 | } | ||
| 122 | |||
| 123 | std.debug.print("Steps:\n", .{}); | ||
| 124 | for (top_level_steps.keys()) |name| { | ||
| 125 | std.debug.print(" - {q}\n", .{name}); | ||
| 126 | } | ||
| 127 | std.debug.print( | ||
| 128 | \\Available Commands: | ||
| 129 | \\ - build [step names / step indices] | ||
| 130 | \\ - watch [step names / step indices] | ||
| 131 | \\ - exit | ||
| 132 | \\ | ||
| 133 | , .{}); | ||
| 134 | |||
| 135 | var stdin_reader_buffer: [256]u8 = undefined; | ||
| 136 | var stdin_reader = Io.File.stdin().reader(io, &stdin_reader_buffer); | ||
| 137 | const stdin = &stdin_reader.interface; | ||
| 138 | |||
| 139 | while (true) { | ||
| 140 | try Io.File.stdout().writeStreamingAll(io, "> "); | ||
| 141 | const command = try stdin.takeDelimiterExclusive('\n'); | ||
| 142 | stdin.toss(1); | ||
| 143 | if (std.mem.startsWith(u8, command, "build") or | ||
| 144 | std.mem.startsWith(u8, command, "watch")) | ||
| 145 | { | ||
| 146 | var steps: std.ArrayList(Configuration.Step.Index) = .empty; | ||
| 147 | defer steps.deinit(gpa); | ||
| 148 | |||
| 149 | const watch = std.mem.startsWith(u8, command, "watch"); | ||
| 150 | |||
| 151 | if (std.mem.cutPrefix(u8, command, "build ") orelse | ||
| 152 | std.mem.cutPrefix(u8, command, "watch ")) |command_args| | ||
| 153 | { | ||
| 154 | var it = std.mem.tokenizeScalar(u8, command_args, ' '); | ||
| 155 | while (it.next()) |arg| { | ||
| 156 | const step: Configuration.Step.Index = | ||
| 157 | if (std.fmt.parseInt(u32, arg, 10)) |i| | ||
| 158 | @fromBackingInt(i) | ||
| 159 | else |_| | ||
| 160 | top_level_steps.get(arg) orelse std.debug.panic("unexpected step name or index", .{}); | ||
| 161 | try steps.append(gpa, step); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | if (steps.items.len < 1) { | ||
| 166 | try steps.append(gpa, c.default_step); | ||
| 167 | } | ||
| 168 | |||
| 169 | try client.serveBuildSteps(steps.items, .{ .watch = watch }); | ||
| 170 | |||
| 171 | while (true) { | ||
| 172 | const header: Server.Message.Header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) { | ||
| 173 | error.Canceled, error.ConcurrencyUnavailable => |e| return e, | ||
| 174 | error.Timeout => unreachable, | ||
| 175 | else => |e| { | ||
| 176 | log.err("failed to receive message: {t}", .{err}); | ||
| 177 | break :blk e; | ||
| 178 | }, | ||
| 179 | }; | ||
| 180 | const body = client_stdout.take(header.bytes_len) catch unreachable; | ||
| 181 | log.debug("received {f} ({d} bytes)", .{ fmtEnum(header.tag), body.len }); | ||
| 182 | |||
| 183 | switch (header.tag) { | ||
| 184 | .bsp_build_started => {}, | ||
| 185 | .bsp_build_completed => if (!watch) break, | ||
| 186 | .bsp_step_started => {}, | ||
| 187 | .bsp_step_completed => {}, | ||
| 188 | .bsp_configuration => @panic("TODO"), | ||
| 189 | else => std.debug.panic("received unexpected message: {f}", .{fmtEnum(header.tag)}), | ||
| 190 | } | ||
| 191 | } | ||
| 192 | continue; | ||
| 193 | } else if (std.mem.eql(u8, command, "exit")) { | ||
| 194 | try client.serveBodylessMessage(.exit); | ||
| 195 | break; | ||
| 196 | } else { | ||
| 197 | log.err("unknown command: {q}", .{command}); | ||
| 198 | continue; | ||
| 199 | } | ||
| 200 | } | ||
| 201 | }; | ||
| 202 | |||
| 203 | try multi_reader.fillRemaining(.none); | ||
| 204 | |||
| 205 | if (client_stderr.bufferedLen() > 0) { | ||
| 206 | log.err("stderr:\n{s}\n", .{client_stderr.buffered()}); | ||
| 207 | } | ||
| 208 | |||
| 209 | try err; | ||
| 210 | |||
| 211 | const term = try child_process.wait(io); | ||
| 212 | |||
| 213 | if (!term.success()) { | ||
| 214 | log.err("maker {f}", .{term}); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | const FormatEnum = union(enum) { | ||
| 219 | named: []const u8, | ||
| 220 | unnamed: usize, | ||
| 221 | |||
| 222 | pub fn format( | ||
| 223 | e: FormatEnum, | ||
| 224 | writer: *std.Io.Writer, | ||
| 225 | ) std.Io.Writer.Error!void { | ||
| 226 | switch (e) { | ||
| 227 | .named => |name| { | ||
| 228 | try writer.writeByte('.'); | ||
| 229 | try writer.writeAll(name); | ||
| 230 | }, | ||
| 231 | .unnamed => |number| try writer.print("0x{x}", .{number}), | ||
| 232 | } | ||
| 233 | } | ||
| 234 | }; | ||
| 235 | |||
| 236 | fn fmtEnum(e: anytype) FormatEnum { | ||
| 237 | if (std.enums.tagName(@TypeOf(e), e)) |name| { | ||
| 238 | return .{ .named = name }; | ||
| 239 | } else { | ||
| 240 | return .{ .unnamed = @backingInt(e) }; | ||
| 241 | } | ||
| 242 | } | ||
tools/incr-check.zig+34-30| ... | @@ -305,21 +305,23 @@ const Eval = struct { | ... | @@ -305,21 +305,23 @@ const Eval = struct { |
| 305 | 305 | ||
| 306 | fn check(eval: *Eval, mr: *Io.File.MultiReader, update: Case.Update, prog_node: std.Progress.Node) !void { | 306 | fn check(eval: *Eval, mr: *Io.File.MultiReader, update: Case.Update, prog_node: std.Progress.Node) !void { |
| 307 | const arena = eval.arena; | 307 | const arena = eval.arena; |
| 308 | const stdout = mr.fileReader(0); | 308 | const stdout = mr.reader(0); |
| 309 | const stderr = &mr.fileReader(1).interface; | 309 | const stderr = mr.reader(1); |
| 310 | const Header = std.zig.Server.Message.Header; | 310 | |
| 311 | var client: std.zig.Client = .{ | ||
| 312 | .in = stdout, | ||
| 313 | .out = undefined, | ||
| 314 | }; | ||
| 311 | 315 | ||
| 312 | while (true) { | 316 | while (true) { |
| 313 | const header = stdout.interface.takeStruct(Header, .little) catch |err| switch (err) { | 317 | const header = client.receiveMessageWithMultiReader(mr, .none) catch |err| switch (err) { |
| 314 | error.EndOfStream => break, | 318 | error.Timeout => unreachable, |
| 315 | error.ReadFailed => return stdout.err.?, | ||
| 316 | }; | ||
| 317 | const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) { | ||
| 318 | // If this panic triggers it might be helpful to rework this | 319 | // If this panic triggers it might be helpful to rework this |
| 319 | // code to print the stderr from the abnormally terminated child. | 320 | // code to print the stderr from the abnormally terminated child. |
| 320 | error.EndOfStream => @panic("unexpected mid-message end of stream"), | 321 | error.EndOfStream => @panic("unexpected mid-message end of stream"), |
| 321 | error.ReadFailed => return stdout.err.?, | 322 | else => |e| return e, |
| 322 | }; | 323 | }; |
| 324 | const body = client.in.take(header.bytes_len) catch unreachable; | ||
| 323 | 325 | ||
| 324 | switch (header.tag) { | 326 | switch (header.tag) { |
| 325 | .error_bundle => { | 327 | .error_bundle => { |
| ... | @@ -605,12 +607,13 @@ const Eval = struct { | ... | @@ -605,12 +607,13 @@ const Eval = struct { |
| 605 | 607 | ||
| 606 | fn requestUpdate(eval: *Eval) !void { | 608 | fn requestUpdate(eval: *Eval) !void { |
| 607 | const io = eval.io; | 609 | const io = eval.io; |
| 608 | const header: std.zig.Client.Message.Header = .{ | 610 | |
| 609 | .tag = .update, | 611 | var w = eval.child.stdin.?.writerStreaming(io, &.{}); |
| 610 | .bytes_len = 0, | 612 | var client: std.zig.Client = .{ |
| 613 | .in = undefined, | ||
| 614 | .out = &w.interface, | ||
| 611 | }; | 615 | }; |
| 612 | var w = eval.child.stdin.?.writer(io, &.{}); | 616 | client.serveBodylessMessage(.update) catch |err| switch (err) { |
| 613 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | ||
| 614 | error.WriteFailed => return w.err.?, | 617 | error.WriteFailed => return w.err.?, |
| 615 | }; | 618 | }; |
| 616 | } | 619 | } |
| ... | @@ -618,22 +621,23 @@ const Eval = struct { | ... | @@ -618,22 +621,23 @@ const Eval = struct { |
| 618 | fn end(eval: *Eval, mr: *Io.File.MultiReader) !void { | 621 | fn end(eval: *Eval, mr: *Io.File.MultiReader) !void { |
| 619 | requestExit(eval.child, eval); | 622 | requestExit(eval.child, eval); |
| 620 | 623 | ||
| 621 | const stdout = mr.fileReader(0); | 624 | var client: std.zig.Client = .{ |
| 622 | const Header = std.zig.Server.Message.Header; | 625 | .in = mr.reader(0), |
| 626 | .out = undefined, | ||
| 627 | }; | ||
| 623 | 628 | ||
| 624 | while (true) { | 629 | while (true) { |
| 625 | const header = stdout.interface.takeStruct(Header, .little) catch |err| switch (err) { | 630 | const header = client.receiveMessageWithMultiReader(mr, .none) catch |err| switch (err) { |
| 626 | error.EndOfStream => break, | 631 | error.Timeout => unreachable, |
| 627 | error.ReadFailed => return stdout.err.?, | 632 | error.EndOfStream => |e| { |
| 628 | }; | 633 | if (client.in.bufferedLen() == 0) break; |
| 629 | stdout.interface.discardAll(header.bytes_len) catch |err| switch (err) { | 634 | return e; |
| 630 | error.ReadFailed => return stdout.err.?, | 635 | }, |
| 631 | error.EndOfStream => |e| return e, | 636 | else => |e| return e, |
| 632 | }; | 637 | }; |
| 638 | try client.in.discardAll(header.bytes_len); | ||
| 633 | } | 639 | } |
| 634 | 640 | ||
| 635 | try mr.fillRemaining(.none); | ||
| 636 | |||
| 637 | const stderr = mr.reader(1).buffered(); | 641 | const stderr = mr.reader(1).buffered(); |
| 638 | if (stderr.len > 0) eval.fatal("unexpected stderr:\n{s}", .{stderr}); | 642 | if (stderr.len > 0) eval.fatal("unexpected stderr:\n{s}", .{stderr}); |
| 639 | } | 643 | } |
| ... | @@ -899,12 +903,12 @@ fn requestExit(child: *std.process.Child, eval: *Eval) void { | ... | @@ -899,12 +903,12 @@ fn requestExit(child: *std.process.Child, eval: *Eval) void { |
| 899 | if (child.stdin == null) return; | 903 | if (child.stdin == null) return; |
| 900 | const io = eval.io; | 904 | const io = eval.io; |
| 901 | 905 | ||
| 902 | const header: std.zig.Client.Message.Header = .{ | 906 | var w = eval.child.stdin.?.writerStreaming(io, &.{}); |
| 903 | .tag = .exit, | 907 | var client: std.zig.Client = .{ |
| 904 | .bytes_len = 0, | 908 | .in = undefined, |
| 909 | .out = &w.interface, | ||
| 905 | }; | 910 | }; |
| 906 | var w = eval.child.stdin.?.writer(io, &.{}); | 911 | client.serveBodylessMessage(.exit) catch |err| switch (err) { |
| 907 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | ||
| 908 | error.WriteFailed => switch (w.err.?) { | 912 | error.WriteFailed => switch (w.err.?) { |
| 909 | error.BrokenPipe => {}, | 913 | error.BrokenPipe => {}, |
| 910 | else => |e| eval.fatal("failed to send exit: {t}", .{e}), | 914 | else => |e| eval.fatal("failed to send exit: {t}", .{e}), |