╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ incr-check.zig ───────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
451 ) Allocator.Error!void { 452 const io = eval.io; 453 const err = eb.getErrorMessage(err_idx); 454 if (err.count != 1) @panic("TODO error message with count>1"); 455 const msg = eb.nullTerminatedString(err.msg); 456 const matches = matches: { 457 if (expected.is_note != is_note) break :matches false; 458 if (!std.mem.eql(u8, expected.msg, msg)) break :matches false; 459 if (err.src_loc == .none) { 460 break :matches expected.src == null; 461 } 462 const expected_src = expected.src orelse break :matches false; 463 const src = eb.getSourceLocation(err.src_loc); 464 const raw_filename = eb.nullTerminatedString(src.src_path); 465 // We need to replace backslashes for consistency between platforms. 466 const filename = name: { 467 if (std.mem.findScalar(u8, raw_filename, '\\') == null) break :name raw_filename; 468 const copied = try eval.arena.dupe(u8, raw_filename); 469 std.mem.replaceScalar(u8, copied, '\\', '/'); 470 break :name copied; 471 }; 472 if (!std.mem.eql(u8, expected_src.filename, filename)) break :matches false; 473 if (expected_src.line != src.line + 1) break :matches false; 474 if (expected_src.column != src.column + 1) break :matches false; 475 break :matches true; 476 }; 477 if (!matches) { 478 eb.renderToStderr(io, .{}, .auto) catch {}; 479 eval.fatal("compile error did not match expected error", .{}); 480 } 481 } 482 483 fn checkSuccessOutcome(eval: *Eval, update: Case.Update, opt_emitted_path: ?[]const u8, prog_node: std.Progress.Node) !void { 484 switch (update.outcome) { 485 .unknown => return, 486 .compile_errors => eval.fatal("expected compile errors but compilation incorrectly succeeded", .{}), 487 .stdout, .exit_code => {}, 488 } 489 const emitted_path = opt_emitted_path orelse { 490 std.debug.assert(eval.backend == .sema); 491 return; 492 }; 493 const io = eval.io; 494 495 const binary_path = switch (eval.backend) { 496 .sema => unreachable, 497 .selfhosted, .llvm => emitted_path, 498 .cbe => bin: { 499 const rand_int = rand64(io); 500 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int); 501 try eval.buildCOutput(emitted_path, out_bin_name, prog_node); 502 break :bin out_bin_name; 503 }, 504 }; 505 506 var argv_buf: [2][]const u8 = undefined; 507 const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor( 508 io, 509 &eval.target, 510 .{ 511 .link_libc = eval.backend == .cbe, 512 .host_cpu_arch = eval.host.cpu.arch, 513 .host_os_tag = eval.host.os.tag, 514 }, 515 )) { 516 .bad_dl, .bad_os_or_cpu => { 517 // This binary cannot be executed on this host. 518 if (!eval.quiet) { 519 std.log.warn("skipping execution because host '{s}' cannot execute binaries for foreign target '{s}'", .{ 520 try eval.host.zigTriple(eval.arena), 521 try eval.target.zigTriple(eval.arena), 522 }); 523 } 524 return; 525 }, 526 .native, .rosetta => argv: { 527 argv_buf[0] = binary_path; 528 break :argv .{ argv_buf[0..1], false }; 529 }, 530 .qemu => |executor_cmd| argv: { 531 if (eval.enable_qemu) { 532 argv_buf[0] = executor_cmd; 533 argv_buf[1] = binary_path; 534 break :argv .{ argv_buf[0..2], true }; 535 } else { 536 continue :sw .bad_os_or_cpu; 537 } 538 }, 539 .wine => |executor_cmd| argv: { 540 if (eval.enable_wine) { 541 argv_buf[0] = executor_cmd; 542 argv_buf[1] = binary_path; 543 break :argv .{ argv_buf[0..2], true }; 544 } else { 545 continue :sw .bad_os_or_cpu; 546 } 547 }, 548 .wasmtime => |executor_cmd| argv: { 549 if (eval.enable_wasmtime) { 550 argv_buf[0] = executor_cmd; 551 argv_buf[1] = binary_path; 552 break :argv .{ argv_buf[0..2], true }; 553 } else { 554 continue :sw .bad_os_or_cpu; 555 } 556 }, 557 .darling => |executor_cmd| argv: { 558 if (eval.enable_darling) { 559 argv_buf[0] = executor_cmd; 560 argv_buf[1] = binary_path; 561 break :argv .{ argv_buf[0..2], true }; 562 } else { 563 continue :sw .bad_os_or_cpu; 564 } 565 }, 566 }; 567 568 const run_prog_node = prog_node.start("run generated executable", 0); 569 defer run_prog_node.end(); 570 571 const result = std.process.run(eval.arena, io, .{ 572 .argv = argv, 573 .cwd = .{ .path = eval.tmp_dir_path }, 574 }) catch |err| { 575 if (is_foreign) { 576 // Chances are the foreign executor isn't available. Skip this evaluation. 577 if (!eval.quiet) { 578 std.log.warn("skipping execution of '{s}' via executor for foreign target '{s}': {t}", .{ 579 binary_path, 580 try eval.target.zigTriple(eval.arena), 581 err, 582 }); 583 } 584 return; 585 } 586 eval.fatal("failed to run the generated executable '{s}': {t}", .{ binary_path, err }); 587 }; 588 589 // Some executors (looking at you, Wine) like throwing some stderr in, just for fun. 590 // Therefore, we'll ignore stderr when using a foreign executor. 591 if (!is_foreign and result.stderr.len != 0) { 592 std.log.err("generated executable '{s}' had unexpected stderr:\n{s}", .{ 593 binary_path, result.stderr, 594 }); 595 } 596 597 switch (result.term) { 598 .exited => |code| switch (update.outcome) { 599 .unknown, .compile_errors => unreachable, 600 .stdout => |expected_stdout| { 601 if (code != 0) { 602 eval.fatal("generated executable '{s}' failed with code {d}", .{ binary_path, code }); 603 } 604 try std.testing.expectEqualStrings(expected_stdout, result.stdout); 605 }, 606 .exit_code => |expected_code| try std.testing.expectEqual(expected_code, code), 607 }, 608 .signal => |sig| { 609 eval.fatal("generated executable '{s}' terminated with signal {t}", .{ binary_path, sig }); 610 }, 611 .stopped => |sig| { 612 eval.fatal("generated executable '{s}' stopped with signal {t}", .{ binary_path, sig }); 613 }, 614 .unknown => { 615 eval.fatal("generated executable '{s}' terminated unexpectedly", .{binary_path}); 616 }, 617 } 618 619 if (!is_foreign and result.stderr.len != 0) std.process.exit(1); 620 } 621 622 fn requestUpdate(eval: *Eval) !void { 623 const io = eval.io; 624 625 var w = eval.child.stdin.?.writerStreaming(io, &.{}); 626 var client: std.zig.Client = .{ 627 .in = undefined, 628 .out = &w.interface, 629 }; 630 client.serveBodylessMessage(.update) catch |err| switch (err) { 631 error.WriteFailed => return w.err.?, 632 }; 633 } 634 635 fn end(eval: *Eval, mr: *Io.File.MultiReader) !void { 636 requestExit(eval.child, eval); 637 638 var client: std.zig.Client = .{ 639 .in = mr.reader(0), 640 .out = undefined, 641 }; 642 643 while (true) { 644 const header = client.receiveMessageWithMultiReader(mr, .none) catch |err| switch (err) { 645 error.Timeout => unreachable, 646 error.EndOfStream => |e| { 647 if (client.in.bufferedLen() == 0) break; 648 return e; 649 }, 650 else => |e| return e, 651 }; 652 try client.in.discardAll(header.bytes_len); 653 } 654 655 const stderr = mr.reader(1).buffered(); 656 if (stderr.len > 0) eval.fatal("unexpected stderr:\n{s}", .{stderr}); 657 } 658 659 fn buildCOutput(eval: *Eval, c_path: []const u8, out_path: []const u8, prog_node: std.Progress.Node) !void { 660 std.debug.assert(eval.cc_child_args.items.len > 0); 661 662 const child_prog_node = prog_node.start("build cbe output", 0); 663 defer child_prog_node.end(); 664 665 try eval.cc_child_args.append(eval.arena, try std.fmt.allocPrint(eval.arena, "-femit-bin={s}", .{out_path})); 666 try eval.cc_child_args.append(eval.arena, c_path); 667 defer eval.cc_child_args.items.len -= 2; 668 669 if (eval.allow_compiler_stderr) { 670 const cmd: std.zig.SubprocessCommand = .{ 671 .argv = eval.cc_child_args.items, 672 .cwd = eval.tmp_dir_path, 673 }; 674 std.log.scoped(.spawn).info("{f}", .{cmd}); 675 } 676 677 const result = std.process.run(eval.arena, eval.io, .{ 678 .argv = eval.cc_child_args.items, 679 .cwd = .{ .path = eval.tmp_dir_path }, 680 .progress_node = child_prog_node, 681 }) catch |err| { 682 eval.fatal("failed to spawn zig cc for '{s}': {t}", .{ c_path, err }); 683 }; 684 685 if (result.term == .exited and result.term.exited == 0) return; 686 687 if (result.stderr.len != 0) { 688 std.log.err("zig cc stderr:\n{s}", .{result.stderr}); 689 } 690 switch (result.term) { 691 .exited => |code| eval.fatal("zig cc for '{s}' failed with code {d}", .{ c_path, code }), 692 .signal => |sig| eval.fatal("zig cc for '{s}' terminated unexpectedly with signal {t}", .{ c_path, sig }), 693 .stopped => |sig| eval.fatal("zig cc for '{s}' stopped unexpectedly with signal {t}", .{ c_path, sig }), 694 .unknown => eval.fatal("zig cc for '{s}' terminated unexpectedly", .{c_path}), 695 } 696 } 697 698 fn fatal(eval: *Eval, comptime fmt: []const u8, args: anytype) noreturn { 699 const io = eval.io; 700 eval.tmp_dir.close(io); 701 if (!eval.preserve_tmp_on_fatal) { 702 // Kill the child since it holds an open handle to its CWD which is the tmp dir path 703 eval.child.kill(io); 704 Dir.cwd().deleteTree(io, eval.tmp_dir_path) catch |err| { 705 std.log.warn("failed to delete tree '{s}': {t}", .{ eval.tmp_dir_path, err }); 706 }; 707 } 708 std.process.fatal(fmt, args); 709 } 710 }; 711 712 const Backend = enum { 713 /// Run semantic analysis only. Runtime output will not be tested, but we still verify 714 /// that compilation succeeds. Corresponds to `-fno-emit-bin`. 715 sema, 716 /// Use the self-hosted code generation backend for this target. 717 /// Corresponds to `-fno-llvm -fno-lld`. 718 selfhosted, 719 /// Use the LLVM backend. 720 /// Corresponds to `-fllvm -flld`. 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