╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ incr-check.zig ───────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
721 llvm, 722 /// Use the C backend. The output is compiled with `zig cc`. 723 /// Corresponds to `-ofmt=c`. 724 cbe, 725 }; 726 727 const Case = struct { 728 updates: []Update, 729 root_source_file: []const u8, 730 skip_targets: []const SkipTarget, 731 modules: []const Module, 732 733 const SkipTarget = struct { 734 query: std.Target.Query, 735 backend: Backend, 736 }; 737 738 const Module = struct { 739 name: []const u8, 740 file: []const u8, 741 }; 742 743 const Update = struct { 744 name: []const u8, 745 outcome: Outcome, 746 changes: []const FullContents = &.{}, 747 deletes: []const []const u8 = &.{}, 748 }; 749 750 const FullContents = struct { 751 name: []const u8, 752 bytes: []const u8, 753 }; 754 755 const Outcome = union(enum) { 756 unknown, 757 compile_errors: struct { 758 errors: []const ExpectedError, 759 compile_log_output: []const u8, 760 }, 761 stdout: []const u8, 762 exit_code: u8, 763 }; 764 765 const ExpectedError = struct { 766 is_note: bool, 767 msg: []const u8, 768 src: ?struct { 769 filename: []const u8, 770 line: u32, 771 column: u32, 772 }, 773 }; 774 775 fn parse(arena: Allocator, bytes: []const u8) !Case { 776 const fatal = std.process.fatal; 777 778 var skip_targets: std.ArrayList(SkipTarget) = .empty; 779 var modules: std.ArrayList(Module) = .empty; 780 var updates: std.ArrayList(Update) = .empty; 781 var changes: std.ArrayList(FullContents) = .empty; 782 var deletes: std.ArrayList([]const u8) = .empty; 783 var it = std.mem.splitScalar(u8, bytes, '\n'); 784 var line_n: usize = 1; 785 var root_source_file: ?[]const u8 = null; 786 while (it.next()) |line| : (line_n += 1) { 787 if (std.mem.startsWith(u8, line, "#")) { 788 var line_it = std.mem.splitScalar(u8, line, '='); 789 const key = line_it.first()[1..]; 790 const val = std.mem.trimEnd(u8, line_it.rest(), "\r"); // windows moment 791 if (val.len == 0) { 792 fatal("line {d}: missing value", .{line_n}); 793 } else if (std.mem.eql(u8, key, "skip_target")) { 794 const query, const backend = parseTargetQueryAndBackend( 795 val, 796 try std.fmt.allocPrint(arena, "line {d}: ", .{line_n}), 797 ); 798 try skip_targets.append(arena, .{ 799 .query = query, 800 .backend = backend, 801 }); 802 } else if (std.mem.eql(u8, key, "module")) { 803 const split_idx = std.mem.findScalar(u8, val, '=') orelse 804 fatal("line {d}: module does not include file", .{line_n}); 805 const name = val[0..split_idx]; 806 const file = val[split_idx + 1 ..]; 807 try modules.append(arena, .{ 808 .name = name, 809 .file = file, 810 }); 811 } else if (std.mem.eql(u8, key, "update")) { 812 if (updates.items.len > 0) { 813 const last_update = &updates.items[updates.items.len - 1]; 814 last_update.changes = try changes.toOwnedSlice(arena); 815 last_update.deletes = try deletes.toOwnedSlice(arena); 816 } 817 try updates.append(arena, .{ 818 .name = val, 819 .outcome = .unknown, 820 }); 821 } else if (std.mem.eql(u8, key, "file")) { 822 if (updates.items.len == 0) fatal("line {d}: file directive before update", .{line_n}); 823 824 if (root_source_file == null) 825 root_source_file = val; 826 827 // Because Windows is so excellent, we need to convert CRLF to LF, so 828 // can't just slice into the input here. How delightful! 829 var src: std.ArrayList(u8) = .empty; 830 831 while (true) { 832 const next_line_raw = it.peek() orelse fatal("line {d}: unexpected EOF", .{line_n}); 833 const next_line = std.mem.trimEnd(u8, next_line_raw, "\r"); 834 if (std.mem.startsWith(u8, next_line, "#")) break; 835 836 _ = it.next(); 837 line_n += 1; 838 839 try src.ensureUnusedCapacity(arena, next_line.len + 1); 840 src.appendSliceAssumeCapacity(next_line); 841 src.appendAssumeCapacity('\n'); 842 } 843 844 try changes.append(arena, .{ 845 .name = val, 846 .bytes = src.items, 847 }); 848 } else if (std.mem.eql(u8, key, "rm_file")) { 849 if (updates.items.len == 0) fatal("line {d}: rm_file directive before update", .{line_n}); 850 try deletes.append(arena, val); 851 } else if (std.mem.eql(u8, key, "expect_stdout")) { 852 if (updates.items.len == 0) fatal("line {d}: expect directive before update", .{line_n}); 853 const last_update = &updates.items[updates.items.len - 1]; 854 if (last_update.outcome != .unknown) fatal("line {d}: conflicting expect directive", .{line_n}); 855 last_update.outcome = .{ 856 .stdout = std.zig.string_literal.parseAlloc(arena, val) catch |err| { 857 fatal("line {d}: bad string literal: {t}", .{ line_n, err }); 858 }, 859 }; 860 } else if (std.mem.eql(u8, key, "expect_error")) { 861 if (updates.items.len == 0) fatal("line {d}: expect directive before update", .{line_n}); 862 const last_update = &updates.items[updates.items.len - 1]; 863 if (last_update.outcome != .unknown) fatal("line {d}: conflicting expect directive", .{line_n}); 864 865 var errors: std.ArrayList(ExpectedError) = .empty; 866 try errors.append(arena, parseExpectedError(val, line_n)); 867 while (true) { 868 const next_line = it.peek() orelse break; 869 if (!std.mem.startsWith(u8, next_line, "#")) break; 870 var new_line_it = std.mem.splitScalar(u8, next_line, '='); 871 const new_key = new_line_it.first()[1..]; 872 const new_val = std.mem.trimEnd(u8, new_line_it.rest(), "\r"); 873 if (new_val.len == 0) break; 874 if (!std.mem.eql(u8, new_key, "expect_error")) break; 875 876 _ = it.next(); 877 line_n += 1; 878 try errors.append(arena, parseExpectedError(new_val, line_n)); 879 } 880 881 var compile_log_output: std.ArrayList(u8) = .empty; 882 while (true) { 883 const next_line = it.peek() orelse break; 884 if (!std.mem.startsWith(u8, next_line, "#")) break; 885 var new_line_it = std.mem.splitScalar(u8, next_line, '='); 886 const new_key = new_line_it.first()[1..]; 887 const new_val = std.mem.trimEnd(u8, new_line_it.rest(), "\r"); 888 if (new_val.len == 0) break; 889 if (!std.mem.eql(u8, new_key, "expect_compile_log")) break; 890 891 _ = it.next(); 892 line_n += 1; 893 try compile_log_output.ensureUnusedCapacity(arena, new_val.len + 1); 894 compile_log_output.appendSliceAssumeCapacity(new_val); 895 compile_log_output.appendAssumeCapacity('\n'); 896 } 897 898 last_update.outcome = .{ .compile_errors = .{ 899 .errors = errors.items, 900 .compile_log_output = compile_log_output.items, 901 } }; 902 } else if (std.mem.eql(u8, key, "expect_compile_log")) { 903 fatal("line {d}: 'expect_compile_log' must immediately follow 'expect_error'", .{line_n}); 904 } else { 905 fatal("line {d}: unrecognized key '{s}'", .{ line_n, key }); 906 } 907 } 908 } 909 910 if (changes.items.len > 0) { 911 const last_update = &updates.items[updates.items.len - 1]; 912 last_update.changes = changes.items; // arena so no need for toOwnedSlice 913 last_update.deletes = deletes.items; 914 } 915 916 return .{ 917 .updates = updates.items, 918 .root_source_file = root_source_file orelse fatal("missing root source file", .{}), 919 .skip_targets = skip_targets.items, // arena so no need for toOwnedSlice 920 .modules = modules.items, 921 }; 922 } 923 }; 924 925 fn requestExit(child: *std.process.Child, eval: *Eval) void { 926 if (child.stdin == null) return; 927 const io = eval.io; 928 929 var w = eval.child.stdin.?.writerStreaming(io, &.{}); 930 var client: std.zig.Client = .{ 931 .in = undefined, 932 .out = &w.interface, 933 }; 934 client.serveBodylessMessage(.exit) catch |err| switch (err) { 935 error.WriteFailed => switch (w.err.?) { 936 error.BrokenPipe => {}, 937 else => |e| eval.fatal("failed to send exit: {t}", .{e}), 938 }, 939 }; 940 941 // Send EOF to stdin. 942 child.stdin.?.close(io); 943 child.stdin = null; 944 } 945 946 fn waitChild(child: *std.process.Child, eval: *Eval) void { 947 const io = eval.io; 948 requestExit(child, eval); 949 const term = child.wait(io) catch |err| eval.fatal("child process failed: {t}", .{err}); 950 switch (term) { 951 .exited => |code| if (code != 0) eval.fatal("compiler failed with code {d}", .{code}), 952 .signal => |sig| eval.fatal("compiler terminated with signal {t}", .{sig}), 953 .stopped => |sig| eval.fatal("compiler stopped unexpectedly with signal {t}", .{sig}), 954 .unknown => eval.fatal("compiler terminated unexpectedly", .{}), 955 } 956 } 957 958 fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError { 959 // #expect_error=foo.zig:1:2: error: the error message 960 // #expect_error=foo.zig:1:2: note: and a note 961 962 const fatal = std.process.fatal; 963 964 var it = std.mem.splitScalar(u8, str, ':'); 965 const filename = it.first(); 966 const line_str, const column_str = if (filename.len > 0) .{ 967 it.next() orelse fatal("line {d}: incomplete error specification", .{l}), 968 it.next() orelse fatal("line {d}: incomplete error specification", .{l}), 969 } else .{ undefined, undefined }; 970 const error_or_note_str = std.mem.trim( 971 u8, 972 it.next() orelse fatal("line {d}: incomplete error specification", .{l}), 973 " ", 974 ); 975 976 const is_note = if (std.mem.eql(u8, error_or_note_str, "error")) 977 false 978 else if (std.mem.eql(u8, error_or_note_str, "note")) 979 true 980 else 981 fatal("line {d}: expeted 'error' or 'note', found '{s}'", .{ l, error_or_note_str }); 982 983 const message = std.mem.trim(u8, it.rest(), " "); 984 if (message.len == 0) fatal("line {d}: empty error message", .{l}); 985 986 return .{ 987 .is_note = is_note, 988 .msg = message, 989 .src = if (filename.len == 0) null else .{ 990 .filename = filename, 991 .line = std.fmt.parseInt(u32, line_str, 10) catch 992 fatal("line {d}: invalid line number '{s}'", .{ l, line_str }), 993 .column = std.fmt.parseInt(u32, column_str, 10) catch 994 fatal("line {d}: invalid column number '{s}'", .{ l, column_str }), 995 }, 996 }; 997 } 998 999 fn rand64(io: Io) u64 { 1000 var x: u64 = undefined; 1001 io.random(@ptrCast(&x)); 1002 return x; 1003 } 1004 1005 /// Calls `std.process.fatal` on error. The error messages are prefixed with `err_prefix`. 1006 fn parseTargetQueryAndBackend(input_str: []const u8, err_prefix: []const u8) struct { std.Target.Query, Backend } { 1007 const fatal = std.process.fatal; 1008 1009 const split_idx = std.mem.findScalarLast(u8, input_str, '-') orelse 1010 fatal("{s}target does not include backend", .{err_prefix}); 1011 1012 const query = input_str[0..split_idx]; 1013 1014 const backend_str = input_str[split_idx + 1 ..]; 1015 const backend: Backend = std.meta.stringToEnum(Backend, backend_str) orelse 1016 fatal("{s}invalid backend '{s}'", .{ err_prefix, backend_str }); 1017 1018 const parsed_query = std.Build.parseTargetQuery(.{ 1019 .arch_os_abi = query, 1020 .object_format = switch (backend) { 1021 .sema, .selfhosted, .llvm => null, 1022 .cbe => "c", 1023 }, 1024 }) catch fatal("{s}invalid target query '{s}'", .{ err_prefix, query }); 1025 1026 return .{ parsed_query, backend }; 1027 } 1028 1029 fn badUsage(comptime fmt: []const u8, args: anytype) noreturn { 1030 std.log.err(fmt ++ "\n{s}", args ++ .{usage}); 1031 std.process.exit(1); 1032 } 1033