| author | |
| committer | |
| log | d8aab4878d471222daf448c63c8ae33cbc461bb3 |
| tree | 0b065775a36f54ad1b838698ebd23284da0d48fd |
| parent | da561521dd78dcde03fc51de8fd64551b368f56d |
| parent | 36a308ce42b8dc8c33c733b587152ee533b4bbd4 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36967
Reviewed-by: Andrew Kelley <andrew@ziglang.org>118 files changed, 5873 insertions(+), 2989 deletions(-)
build.zig+17-13| ... | @@ -125,7 +125,6 @@ pub fn build(b: *std.Build) !void { | ... | @@ -125,7 +125,6 @@ pub fn build(b: *std.Build) !void { |
| 125 | const skip_darwin = b.option(bool, "skip-darwin", "Main test suite skips targets with darwin OSs") orelse false; | 125 | const skip_darwin = b.option(bool, "skip-darwin", "Main test suite skips targets with darwin OSs") orelse false; |
| 126 | const skip_linux = b.option(bool, "skip-linux", "Main test suite skips targets with linux OS") orelse false; | 126 | const skip_linux = b.option(bool, "skip-linux", "Main test suite skips targets with linux OS") orelse false; |
| 127 | const skip_llvm = b.option(bool, "skip-llvm", "Main test suite skips targets that use LLVM backend") orelse false; | 127 | const skip_llvm = b.option(bool, "skip-llvm", "Main test suite skips targets that use LLVM backend") orelse false; |
| 128 | const skip_test_incremental = b.option(bool, "skip-test-incremental", "Main test step omits dependency on test-incremental step") orelse false; | ||
| 129 | 128 | ||
| 130 | const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; | 129 | const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; |
| 131 | 130 | ||
| ... | @@ -781,8 +780,22 @@ pub fn build(b: *std.Build) !void { | ... | @@ -781,8 +780,22 @@ pub fn build(b: *std.Build) !void { |
| 781 | test_step.dependOn(check_oracle_step); | 780 | test_step.dependOn(check_oracle_step); |
| 782 | } | 781 | } |
| 783 | 782 | ||
| 784 | const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases"); | 783 | if (tests.addLibcTestNszTests(b, .{ |
| 785 | try tests.addIncrementalTests(b, test_incremental_step, .{ | 784 | .optimize_modes = optimize_modes, |
| 785 | .test_filters = test_filters, | ||
| 786 | .test_target_filters = test_target_filters, | ||
| 787 | .skip_wasm = skip_wasm, | ||
| 788 | .max_rss = 4_300_000_000, | ||
| 789 | })) |test_libc_nsz_step| test_step.dependOn(test_libc_nsz_step); | ||
| 790 | |||
| 791 | const runner = b.addExecutable(.{ | ||
| 792 | .name = "runner", | ||
| 793 | .root_module = b.createModule(.{ | ||
| 794 | .root_source_file = b.path("test/src/Runner.zig"), | ||
| 795 | .target = b.graph.host, | ||
| 796 | }), | ||
| 797 | }); | ||
| 798 | test_step.dependOn(try tests.addIncrementalTests(b, runner, .{ | ||
| 786 | .test_filters = test_filters, | 799 | .test_filters = test_filters, |
| 787 | .test_target_filters = test_target_filters, | 800 | .test_target_filters = test_target_filters, |
| 788 | .skip_non_native = skip_non_native, | 801 | .skip_non_native = skip_non_native, |
| ... | @@ -794,16 +807,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -794,16 +807,7 @@ pub fn build(b: *std.Build) !void { |
| 794 | .skip_darwin = skip_darwin, | 807 | .skip_darwin = skip_darwin, |
| 795 | .skip_linux = skip_linux, | 808 | .skip_linux = skip_linux, |
| 796 | .skip_llvm = skip_llvm, | 809 | .skip_llvm = skip_llvm, |
| 797 | }); | 810 | })); |
| 798 | if (!skip_test_incremental) test_step.dependOn(test_incremental_step); | ||
| 799 | |||
| 800 | if (tests.addLibcTestNszTests(b, .{ | ||
| 801 | .optimize_modes = optimize_modes, | ||
| 802 | .test_filters = test_filters, | ||
| 803 | .test_target_filters = test_target_filters, | ||
| 804 | .skip_wasm = skip_wasm, | ||
| 805 | .max_rss = 4_300_000_000, | ||
| 806 | })) |test_libc_nsz_step| test_step.dependOn(test_libc_nsz_step); | ||
| 807 | } | 811 | } |
| 808 | 812 | ||
| 809 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { | 813 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { |
lib/compiler/Maker.zig+27-13| ... | @@ -3631,7 +3631,7 @@ pub fn resolveLazyPath( | ... | @@ -3631,7 +3631,7 @@ pub fn resolveLazyPath( |
| 3631 | const c = &maker.scanned_config.configuration; | 3631 | const c = &maker.scanned_config.configuration; |
| 3632 | return switch (lazy_path) { | 3632 | return switch (lazy_path) { |
| 3633 | .source_path => |sp| try packagePath(maker, arena, sp.owner, sp.sub_path.slice(c)), | 3633 | .source_path => |sp| try packagePath(maker, arena, sp.owner, sp.sub_path.slice(c)), |
| 3634 | .relative => |relative| relativePath(maker, arena, relative), | 3634 | .relative => |relative| try relativePath(maker, arena, relative, stepByIndex(maker, asking_step_index)), |
| 3635 | .generated => |gen| { | 3635 | .generated => |gen| { |
| 3636 | const base = generatedPath(maker, gen.index); | 3636 | const base = generatedPath(maker, gen.index); |
| 3637 | var file_path = base; | 3637 | var file_path = base; |
| ... | @@ -3768,7 +3768,12 @@ pub fn packagePath( | ... | @@ -3768,7 +3768,12 @@ pub fn packagePath( |
| 3768 | }; | 3768 | }; |
| 3769 | } | 3769 | } |
| 3770 | 3770 | ||
| 3771 | pub fn relativePath(maker: *const Maker, arena: Allocator, relative: Configuration.LazyPath.Relative) Allocator.Error!Path { | 3771 | pub fn relativePath( |
| 3772 | maker: *const Maker, | ||
| 3773 | arena: Allocator, | ||
| 3774 | relative: Configuration.LazyPath.Relative, | ||
| 3775 | asking_step: *Step, | ||
| 3776 | ) error{ OutOfMemory, MakeFailed }!Path { | ||
| 3772 | const graph = maker.graph; | 3777 | const graph = maker.graph; |
| 3773 | const c = &maker.scanned_config.configuration; | 3778 | const c = &maker.scanned_config.configuration; |
| 3774 | const sub_path = relative.sub_path.slice(c); | 3779 | const sub_path = relative.sub_path.slice(c); |
| ... | @@ -3789,12 +3794,12 @@ pub fn relativePath(maker: *const Maker, arena: Allocator, relative: Configurati | ... | @@ -3789,12 +3794,12 @@ pub fn relativePath(maker: *const Maker, arena: Allocator, relative: Configurati |
| 3789 | .root_dir = graph.build_root_directory, | 3794 | .root_dir = graph.build_root_directory, |
| 3790 | .sub_path = sub_path, | 3795 | .sub_path = sub_path, |
| 3791 | }, | 3796 | }, |
| 3792 | .zig_exe => .{ | 3797 | .zig_exe => { |
| 3793 | .root_dir = .cwd(), | 3798 | assert(sub_path.len == 0); |
| 3794 | .sub_path = if (sub_path.len == 0) | 3799 | return .{ |
| 3795 | graph.zig_exe | 3800 | .root_dir = .cwd(), |
| 3796 | else | 3801 | .sub_path = graph.zig_exe, |
| 3797 | try Io.Dir.path.join(arena, &.{ graph.zig_exe, sub_path }), | 3802 | }; |
| 3798 | }, | 3803 | }, |
| 3799 | .zig_lib => .{ | 3804 | .zig_lib => .{ |
| 3800 | .root_dir = graph.zig_lib_directory, | 3805 | .root_dir = graph.zig_lib_directory, |
| ... | @@ -3804,6 +3809,10 @@ pub fn relativePath(maker: *const Maker, arena: Allocator, relative: Configurati | ... | @@ -3804,6 +3809,10 @@ pub fn relativePath(maker: *const Maker, arena: Allocator, relative: Configurati |
| 3804 | .install_lib => try maker.install_paths.lib.join(arena, sub_path), | 3809 | .install_lib => try maker.install_paths.lib.join(arena, sub_path), |
| 3805 | .install_bin => try maker.install_paths.bin.join(arena, sub_path), | 3810 | .install_bin => try maker.install_paths.bin.join(arena, sub_path), |
| 3806 | .install_include => try maker.install_paths.include.join(arena, sub_path), | 3811 | .install_include => try maker.install_paths.include.join(arena, sub_path), |
| 3812 | .libc_runtimes => if (maker.graph.libc_runtimes_dir) |libc_runtimes_dir| .{ | ||
| 3813 | .root_dir = .cwd(), | ||
| 3814 | .sub_path = try Dir.path.join(arena, &.{ libc_runtimes_dir, sub_path }), | ||
| 3815 | } else asking_step.fail(maker, "unknown LazyPath: \"--libc-runtimes\" not specified", .{}), | ||
| 3807 | }; | 3816 | }; |
| 3808 | } | 3817 | } |
| 3809 | 3818 | ||
| ... | @@ -4125,8 +4134,7 @@ const Fork = struct { | ... | @@ -4125,8 +4134,7 @@ const Fork = struct { |
| 4125 | fork.arena_allocator = .init(gpa); | 4134 | fork.arena_allocator = .init(gpa); |
| 4126 | const arena = fork.arena_allocator.allocator(); | 4135 | const arena = fork.arena_allocator.allocator(); |
| 4127 | 4136 | ||
| 4128 | var error_bundle: std.zig.ErrorBundle.Wip = undefined; | 4137 | var error_bundle: std.zig.ErrorBundle.Wip = try .init(gpa); |
| 4129 | try error_bundle.init(gpa); | ||
| 4130 | defer error_bundle.deinit(); | 4138 | defer error_bundle.deinit(); |
| 4131 | 4139 | ||
| 4132 | const manifest_path = try fork.path.join(arena, Package.Manifest.basename); | 4140 | const manifest_path = try fork.path.join(arena, Package.Manifest.basename); |
| ... | @@ -4227,8 +4235,7 @@ fn loadManifest( | ... | @@ -4227,8 +4235,7 @@ fn loadManifest( |
| 4227 | errdefer manifest.deinit(gpa); | 4235 | errdefer manifest.deinit(gpa); |
| 4228 | 4236 | ||
| 4229 | if (manifest.errors.len > 0) { | 4237 | if (manifest.errors.len > 0) { |
| 4230 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 4238 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa); |
| 4231 | try wip_errors.init(gpa); | ||
| 4232 | defer wip_errors.deinit(); | 4239 | defer wip_errors.deinit(); |
| 4233 | 4240 | ||
| 4234 | const src_path = try wip_errors.addString(Package.Manifest.basename); | 4241 | const src_path = try wip_errors.addString(Package.Manifest.basename); |
| ... | @@ -4393,15 +4400,22 @@ fn confPathDepToCachePath( | ... | @@ -4393,15 +4400,22 @@ fn confPathDepToCachePath( |
| 4393 | else => |index| try Dir.path.join(arena, &.{ index.get(c).?.root_path.slice(c), sub_path }), | 4400 | else => |index| try Dir.path.join(arena, &.{ index.get(c).?.root_path.slice(c), sub_path }), |
| 4394 | }, | 4401 | }, |
| 4395 | }, | 4402 | }, |
| 4403 | .zig_exe => { | ||
| 4404 | assert(sub_path.len == 0); | ||
| 4405 | return .{ | ||
| 4406 | .root_dir = .cwd(), | ||
| 4407 | .sub_path = graph.zig_exe, | ||
| 4408 | }; | ||
| 4409 | }, | ||
| 4396 | .zig_lib => .{ | 4410 | .zig_lib => .{ |
| 4397 | .root_dir = graph.zig_lib_directory, | 4411 | .root_dir = graph.zig_lib_directory, |
| 4398 | .sub_path = sub_path, | 4412 | .sub_path = sub_path, |
| 4399 | }, | 4413 | }, |
| 4400 | .zig_exe => @panic("TODO"), | ||
| 4401 | .install_prefix => @panic("TODO"), | 4414 | .install_prefix => @panic("TODO"), |
| 4402 | .install_lib => @panic("TODO"), | 4415 | .install_lib => @panic("TODO"), |
| 4403 | .install_bin => @panic("TODO"), | 4416 | .install_bin => @panic("TODO"), |
| 4404 | .install_include => @panic("TODO"), | 4417 | .install_include => @panic("TODO"), |
| 4418 | .libc_runtimes => @panic("TODO"), | ||
| 4405 | }; | 4419 | }; |
| 4406 | } | 4420 | } |
| 4407 | 4421 |
lib/compiler/Maker/Fetch.zig+1-1| ... | @@ -527,7 +527,7 @@ pub fn run(f: *Fetch) RunError!void { | ... | @@ -527,7 +527,7 @@ pub fn run(f: *Fetch) RunError!void { |
| 527 | const arena = f.arena.allocator(); | 527 | const arena = f.arena.allocator(); |
| 528 | const gpa = f.arena.child_allocator; | 528 | const gpa = f.arena.child_allocator; |
| 529 | 529 | ||
| 530 | try eb.init(gpa); | 530 | eb.* = try .init(gpa); |
| 531 | 531 | ||
| 532 | // Check the global zig package cache to see if the hash already exists. If | 532 | // Check the global zig package cache to see if the hash already exists. If |
| 533 | // so, load, parse, and validate the build.zig.zon file therein, and skip | 533 | // so, load, parse, and validate the build.zig.zon file therein, and skip |
lib/compiler/Maker/Step.zig+21-12| ... | @@ -270,7 +270,10 @@ pub fn make( | ... | @@ -270,7 +270,10 @@ pub fn make( |
| 270 | .compile => break :t null, | 270 | .compile => break :t null, |
| 271 | .run => { | 271 | .run => { |
| 272 | const run_flags: Configuration.Step.Run.Flags = @bitCast(flags); | 272 | const run_flags: Configuration.Step.Run.Flags = @bitCast(flags); |
| 273 | if (run_flags.stdio == .zig_test) break :t null; | 273 | switch (run_flags.stdio) { |
| 274 | .infer_from_args, .inherit, .check => {}, | ||
| 275 | .zig_test, .protocol => break :t null, | ||
| 276 | } | ||
| 274 | }, | 277 | }, |
| 275 | else => {}, | 278 | else => {}, |
| 276 | } | 279 | } |
| ... | @@ -621,6 +624,7 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi | ... | @@ -621,6 +624,7 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi |
| 621 | else => |e| return e, | 624 | else => |e| return e, |
| 622 | }; | 625 | }; |
| 623 | const body = client.in.take(header.bytes_len) catch unreachable; | 626 | const body = client.in.take(header.bytes_len) catch unreachable; |
| 627 | var body_r: std.Io.Reader = .fixed(body); | ||
| 624 | 628 | ||
| 625 | switch (header.tag) { | 629 | switch (header.tag) { |
| 626 | .zig_version => { | 630 | .zig_version => { |
| ... | @@ -632,16 +636,22 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi | ... | @@ -632,16 +636,22 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi |
| 632 | ); | 636 | ); |
| 633 | } | 637 | } |
| 634 | }, | 638 | }, |
| 639 | .config => switch (s.extended) { | ||
| 640 | else => unreachable, | ||
| 641 | .compile => |*compile| compile.config = | ||
| 642 | body_r.takeStruct(std.zig.Server.Message.Config, .little) catch unreachable, | ||
| 643 | .translate_c => {}, | ||
| 644 | }, | ||
| 635 | .error_bundle => { | 645 | .error_bundle => { |
| 636 | s.result_error_bundle = try std.zig.Server.allocErrorBundle(gpa, body); | 646 | s.result_error_bundle = try std.zig.Server.allocErrorBundle(gpa, body); |
| 637 | // This message indicates the end of the update. | 647 | // This message indicates the end of the update. |
| 638 | if (watch) break; | 648 | if (watch) break; |
| 639 | }, | 649 | }, |
| 640 | .emit_digest => { | 650 | .emit_digest => { |
| 641 | const EmitDigest = std.zig.Server.Message.EmitDigest; | 651 | const emit_digest = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; |
| 642 | const emit_digest: *align(1) const EmitDigest = @ptrCast(body); | 652 | const digest = body_r.takeArray(Cache.bin_digest_len) catch unreachable; |
| 643 | s.result_cached = emit_digest.flags.cache_hit; | 653 | s.result_cached = emit_digest.flags.cache_hit; |
| 644 | result = .{ .bin = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len].* }; | 654 | result = .{ .bin = digest.* }; |
| 645 | }, | 655 | }, |
| 646 | .file_system_inputs => { | 656 | .file_system_inputs => { |
| 647 | clearWatchInputs(s, maker); | 657 | clearWatchInputs(s, maker); |
| ... | @@ -701,8 +711,7 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi | ... | @@ -701,8 +711,7 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi |
| 701 | } | 711 | } |
| 702 | }, | 712 | }, |
| 703 | .time_report => if (maker.web_server) |ws| { | 713 | .time_report => if (maker.web_server) |ws| { |
| 704 | const TimeReport = std.zig.Server.Message.TimeReport; | 714 | const tr = body_r.takeStruct(std.zig.Server.Message.TimeReport, .little) catch unreachable; |
| 705 | const tr: *align(1) const TimeReport = @ptrCast(body[0..@sizeOf(TimeReport)]); | ||
| 706 | ws.updateTimeReportCompile(.{ | 715 | ws.updateTimeReportCompile(.{ |
| 707 | .compile_step = step_index, | 716 | .compile_step = step_index, |
| 708 | .use_llvm = tr.flags.use_llvm, | 717 | .use_llvm = tr.flags.use_llvm, |
| ... | @@ -711,7 +720,7 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi | ... | @@ -711,7 +720,7 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi |
| 711 | .llvm_pass_timings_len = tr.llvm_pass_timings_len, | 720 | .llvm_pass_timings_len = tr.llvm_pass_timings_len, |
| 712 | .files_len = tr.files_len, | 721 | .files_len = tr.files_len, |
| 713 | .decls_len = tr.decls_len, | 722 | .decls_len = tr.decls_len, |
| 714 | .trailing = body[@sizeOf(TimeReport)..], | 723 | .trailing = body_r.buffered(), |
| 715 | }); | 724 | }); |
| 716 | }, | 725 | }, |
| 717 | else => {}, // ignore other messages | 726 | else => {}, // ignore other messages |
| ... | @@ -848,7 +857,7 @@ pub fn setWatchInputsFromManifestFiles( | ... | @@ -848,7 +857,7 @@ pub fn setWatchInputsFromManifestFiles( |
| 848 | } | 857 | } |
| 849 | 858 | ||
| 850 | /// For steps that have a single input that never changes when re-running `make`. | 859 | /// For steps that have a single input that never changes when re-running `make`. |
| 851 | pub fn singleUnchangingWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_path: LazyPath) Allocator.Error!void { | 860 | pub fn singleUnchangingWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_path: LazyPath) FailError!void { |
| 852 | if (!step.inputs.populated()) try step.addWatchInput(maker, arena, lazy_path); | 861 | if (!step.inputs.populated()) try step.addWatchInput(maker, arena, lazy_path); |
| 853 | } | 862 | } |
| 854 | 863 | ||
| ... | @@ -857,7 +866,7 @@ pub fn clearWatchInputs(step: *Step, maker: *Maker) void { | ... | @@ -857,7 +866,7 @@ pub fn clearWatchInputs(step: *Step, maker: *Maker) void { |
| 857 | } | 866 | } |
| 858 | 867 | ||
| 859 | /// Places a *file* dependency on the path. | 868 | /// Places a *file* dependency on the path. |
| 860 | pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: LazyPath) Allocator.Error!void { | 869 | pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: LazyPath) FailError!void { |
| 861 | const conf = &maker.scanned_config.configuration; | 870 | const conf = &maker.scanned_config.configuration; |
| 862 | switch (lazy_file) { | 871 | switch (lazy_file) { |
| 863 | .source_path => |source_path| { | 872 | .source_path => |source_path| { |
| ... | @@ -866,7 +875,7 @@ pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: La | ... | @@ -866,7 +875,7 @@ pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: La |
| 866 | try addWatchInputPath(step, maker, pkg_path); | 875 | try addWatchInputPath(step, maker, pkg_path); |
| 867 | }, | 876 | }, |
| 868 | .relative => |relative| { | 877 | .relative => |relative| { |
| 869 | const resolved_path = try maker.relativePath(arena, relative); | 878 | const resolved_path = try maker.relativePath(arena, relative, step); |
| 870 | try addWatchInputPath(step, maker, resolved_path); | 879 | try addWatchInputPath(step, maker, resolved_path); |
| 871 | }, | 880 | }, |
| 872 | // Nothing to watch because this dependency edge is modeled instead via `dependants`. | 881 | // Nothing to watch because this dependency edge is modeled instead via `dependants`. |
| ... | @@ -881,7 +890,7 @@ pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: La | ... | @@ -881,7 +890,7 @@ pub fn addWatchInput(step: *Step, maker: *Maker, arena: Allocator, lazy_file: La |
| 881 | /// Paths derived from this directory should also be manually added via | 890 | /// Paths derived from this directory should also be manually added via |
| 882 | /// `addDirectoryWatchInputFromPath` if and only if this function returns | 891 | /// `addDirectoryWatchInputFromPath` if and only if this function returns |
| 883 | /// `true`. | 892 | /// `true`. |
| 884 | pub fn addDirectoryWatchInput(step: *Step, maker: *Maker, lazy_directory: LazyPath) Allocator.Error!bool { | 893 | pub fn addDirectoryWatchInput(step: *Step, maker: *Maker, lazy_directory: LazyPath) FailError!bool { |
| 885 | const graph = maker.graph; | 894 | const graph = maker.graph; |
| 886 | const arena = graph.arena; // TODO don't leak into the process arena | 895 | const arena = graph.arena; // TODO don't leak into the process arena |
| 887 | switch (lazy_directory) { | 896 | switch (lazy_directory) { |
| ... | @@ -892,7 +901,7 @@ pub fn addDirectoryWatchInput(step: *Step, maker: *Maker, lazy_directory: LazyPa | ... | @@ -892,7 +901,7 @@ pub fn addDirectoryWatchInput(step: *Step, maker: *Maker, lazy_directory: LazyPa |
| 892 | try addDirectoryWatchInputFromPath(step, maker, pkg_path); | 901 | try addDirectoryWatchInputFromPath(step, maker, pkg_path); |
| 893 | }, | 902 | }, |
| 894 | .relative => |relative| { | 903 | .relative => |relative| { |
| 895 | const resolved_path = try maker.relativePath(arena, relative); | 904 | const resolved_path = try maker.relativePath(arena, relative, step); |
| 896 | try addDirectoryWatchInputFromPath(step, maker, resolved_path); | 905 | try addDirectoryWatchInputFromPath(step, maker, resolved_path); |
| 897 | }, | 906 | }, |
| 898 | // Nothing to watch because this dependency edge is modeled instead via `dependants`. | 907 | // Nothing to watch because this dependency edge is modeled instead via `dependants`. |
lib/compiler/Maker/Step/Compile.zig+5-6| ... | @@ -21,7 +21,7 @@ zig_process: ?*Step.ZigProcess = null, | ... | @@ -21,7 +21,7 @@ zig_process: ?*Step.ZigProcess = null, |
| 21 | /// Populated by InstallArtifact. | 21 | /// Populated by InstallArtifact. |
| 22 | installed_path: ?Path = null, | 22 | installed_path: ?Path = null, |
| 23 | /// Populated by `make`, used by `Run`. | 23 | /// Populated by `make`, used by `Run`. |
| 24 | is_linking_libc: bool = false, | 24 | config: ?std.zig.Server.Message.Config = null, |
| 25 | 25 | ||
| 26 | pub fn make( | 26 | pub fn make( |
| 27 | compile: *Compile, | 27 | compile: *Compile, |
| ... | @@ -29,6 +29,7 @@ pub fn make( | ... | @@ -29,6 +29,7 @@ pub fn make( |
| 29 | maker: *Maker, | 29 | maker: *Maker, |
| 30 | progress_node: std.Progress.Node, | 30 | progress_node: std.Progress.Node, |
| 31 | ) Step.ExtendedMakeError!void { | 31 | ) Step.ExtendedMakeError!void { |
| 32 | _ = compile; // only accessed by `Step.evalZigProcess`. | ||
| 32 | const graph = maker.graph; | 33 | const graph = maker.graph; |
| 33 | const gpa = maker.gpa; | 34 | const gpa = maker.gpa; |
| 34 | const conf = &maker.scanned_config.configuration; | 35 | const conf = &maker.scanned_config.configuration; |
| ... | @@ -42,7 +43,7 @@ pub fn make( | ... | @@ -42,7 +43,7 @@ pub fn make( |
| 42 | var argv: std.ArrayList([]const u8) = .empty; | 43 | var argv: std.ArrayList([]const u8) = .empty; |
| 43 | defer argv.deinit(gpa); | 44 | defer argv.deinit(gpa); |
| 44 | 45 | ||
| 45 | try lowerZigArgs(arena, compile, compile_index, maker, progress_node, &argv, false); | 46 | try lowerZigArgs(arena, compile_index, maker, progress_node, &argv, false); |
| 46 | 47 | ||
| 47 | const incremental = conf_comp.flags4.incremental.toBool() orelse graph.incremental == true; | 48 | const incremental = conf_comp.flags4.incremental.toBool() orelse graph.incremental == true; |
| 48 | 49 | ||
| ... | @@ -158,7 +159,6 @@ const ModuleListContext = struct { | ... | @@ -158,7 +159,6 @@ const ModuleListContext = struct { |
| 158 | 159 | ||
| 159 | fn lowerZigArgs( | 160 | fn lowerZigArgs( |
| 160 | arena: Allocator, | 161 | arena: Allocator, |
| 161 | compile: *Compile, | ||
| 162 | compile_index: Configuration.Step.Index, | 162 | compile_index: Configuration.Step.Index, |
| 163 | maker: *Maker, | 163 | maker: *Maker, |
| 164 | progress_node: std.Progress.Node, | 164 | progress_node: std.Progress.Node, |
| ... | @@ -575,8 +575,6 @@ fn lowerZigArgs( | ... | @@ -575,8 +575,6 @@ fn lowerZigArgs( |
| 575 | try zig_args.ensureUnusedCapacity(gpa, 2); | 575 | try zig_args.ensureUnusedCapacity(gpa, 2); |
| 576 | if (is_linking_libcpp) zig_args.appendAssumeCapacity("-lc++"); | 576 | if (is_linking_libcpp) zig_args.appendAssumeCapacity("-lc++"); |
| 577 | if (is_linking_libc) zig_args.appendAssumeCapacity("-lc"); | 577 | if (is_linking_libc) zig_args.appendAssumeCapacity("-lc"); |
| 578 | |||
| 579 | compile.is_linking_libc = is_linking_libc; | ||
| 580 | } | 578 | } |
| 581 | 579 | ||
| 582 | if (conf_comp.win32_manifest.value) |manifest_file| { | 580 | if (conf_comp.win32_manifest.value) |manifest_file| { |
| ... | @@ -944,6 +942,7 @@ pub fn rebuildInFuzzMode( | ... | @@ -944,6 +942,7 @@ pub fn rebuildInFuzzMode( |
| 944 | compile_index: Configuration.Step.Index, | 942 | compile_index: Configuration.Step.Index, |
| 945 | progress_node: std.Progress.Node, | 943 | progress_node: std.Progress.Node, |
| 946 | ) !Step.OptCacheDigest { | 944 | ) !Step.OptCacheDigest { |
| 945 | _ = compile; | ||
| 947 | const gpa = maker.gpa; | 946 | const gpa = maker.gpa; |
| 948 | const step = maker.stepByIndex(compile_index); | 947 | const step = maker.stepByIndex(compile_index); |
| 949 | 948 | ||
| ... | @@ -962,7 +961,7 @@ pub fn rebuildInFuzzMode( | ... | @@ -962,7 +961,7 @@ pub fn rebuildInFuzzMode( |
| 962 | var argv: std.ArrayList([]const u8) = .empty; | 961 | var argv: std.ArrayList([]const u8) = .empty; |
| 963 | defer argv.deinit(gpa); | 962 | defer argv.deinit(gpa); |
| 964 | 963 | ||
| 965 | try lowerZigArgs(arena, compile, compile_index, maker, progress_node, &argv, true); | 964 | try lowerZigArgs(arena, compile_index, maker, progress_node, &argv, true); |
| 966 | return Step.evalZigProcess(compile_index, maker, argv.items, progress_node, false); | 965 | return Step.evalZigProcess(compile_index, maker, argv.items, progress_node, false); |
| 967 | } | 966 | } |
| 968 | 967 |
lib/compiler/Maker/Step/Run.zig+821-326| ... | @@ -55,237 +55,598 @@ pub fn make( | ... | @@ -55,237 +55,598 @@ pub fn make( |
| 55 | var man = graph.cache.obtain(); | 55 | var man = graph.cache.obtain(); |
| 56 | defer man.deinit(); | 56 | defer man.deinit(); |
| 57 | 57 | ||
| 58 | if (conf_run.environ_map.value) |environ_map_index| { | 58 | var tmp_dir_path: ["tmp".len + Dir.path.sep_str.len + std.fmt.hex(@as(u64, 0)).len]u8 = undefined; |
| 59 | const environ_map = environ_map_index.get(conf); | 59 | |
| 60 | for (environ_map.keys.slice(conf), environ_map.values.slice(conf)) |key, value| { | 60 | const has_side_effects = has_side_effects: { |
| 61 | man.hash.addBytesZ(key.slice(conf)); | 61 | if (conf_run.environ_map.value) |environ_map_index| { |
| 62 | man.hash.addBytesZ(value.slice(conf)); | 62 | const environ_map = environ_map_index.get(conf); |
| 63 | for (environ_map.keys.slice(conf), environ_map.values.slice(conf)) |key, value| { | ||
| 64 | man.hash.addBytesZ(key.slice(conf)); | ||
| 65 | man.hash.addBytesZ(value.slice(conf)); | ||
| 66 | } | ||
| 63 | } | 67 | } |
| 64 | } | ||
| 65 | 68 | ||
| 66 | for (conf_run.preopens.slice) |preopen| { | 69 | for (conf_run.preopens.slice) |preopen| { |
| 67 | man.hash.addBytesZ(preopen.name.slice(conf)); | 70 | man.hash.addBytesZ(preopen.name.slice(conf)); |
| 68 | const cwd_path = try maker.resolveLazyPathIndex(arena, preopen.path, run_index); | 71 | const cwd_path = try maker.resolveLazyPathIndex(arena, preopen.path, run_index); |
| 69 | man.hash.addBytes(try cwd_path.toString(arena)); | 72 | man.hash.addBytes(try cwd_path.toString(arena)); |
| 70 | } | 73 | } |
| 71 | 74 | ||
| 72 | man.hash.add(graph.fuzzing); | 75 | man.hash.add(graph.fuzzing); |
| 73 | man.hash.add(conf_run.flags.color); | 76 | man.hash.add(conf_run.flags.color); |
| 74 | man.hash.add(conf_run.flags.disable_zig_progress); | 77 | man.hash.add(conf_run.flags.disable_zig_progress); |
| 75 | 78 | ||
| 76 | var any_dep_files = false; | 79 | var any_discovered_inputs = switch (conf_run.flags.stdio) { |
| 77 | var any_output_args = false; | 80 | .infer_from_args, .inherit, .check, .zig_test => false, |
| 78 | var any_cli_positionals = false; | 81 | .protocol => true, |
| 82 | }; | ||
| 83 | var any_output_args = false; | ||
| 84 | var any_cli_positionals = false; | ||
| 85 | |||
| 86 | for (switch (conf_run.flags.stdio) { | ||
| 87 | .infer_from_args, .inherit, .check, .zig_test => conf_run.args.slice, | ||
| 88 | .protocol => conf_run.args.slice[0..1], // arguments communicated over protocol | ||
| 89 | }) |arg_index| { | ||
| 90 | const arg = arg_index.get(conf); | ||
| 91 | try argv_list.ensureUnusedCapacity(gpa, 1); | ||
| 92 | switch (arg.flags.tag) { | ||
| 93 | .string => { | ||
| 94 | const string = arg.prefix.value.?.slice(conf); | ||
| 95 | argv_list.appendAssumeCapacity(string); | ||
| 96 | man.hash.addBytesZ(string); | ||
| 97 | }, | ||
| 98 | .path_file => { | ||
| 99 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | ||
| 100 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | ||
| 101 | const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index); | ||
| 102 | argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{ | ||
| 103 | prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix, | ||
| 104 | })); | ||
| 105 | man.hash.add(arg.flags.make_absolute); | ||
| 106 | man.hash.addBytesZ(prefix); | ||
| 107 | man.hash.addBytesZ(suffix); | ||
| 108 | _ = try man.addInputPath(file_path, .{}); | ||
| 109 | }, | ||
| 110 | .path_directory => { | ||
| 111 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | ||
| 112 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | ||
| 113 | const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index); | ||
| 114 | const resolved_arg = try mem.concat(arena, u8, &.{ | ||
| 115 | prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix, | ||
| 116 | }); | ||
| 117 | argv_list.appendAssumeCapacity(resolved_arg); | ||
| 118 | man.hash.add(arg.flags.make_absolute); | ||
| 119 | man.hash.addBytes(resolved_arg); | ||
| 120 | }, | ||
| 121 | .file_content => { | ||
| 122 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | ||
| 123 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | ||
| 124 | const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index); | ||
| 125 | |||
| 126 | var result: std.Io.Writer.Allocating = .init(arena); | ||
| 127 | result.writer.writeAll(prefix) catch return error.OutOfMemory; | ||
| 128 | |||
| 129 | const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err| | ||
| 130 | return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err }); | ||
| 131 | defer file.close(io); | ||
| 132 | |||
| 133 | var file_reader = file.reader(io, &.{}); | ||
| 134 | _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) { | ||
| 135 | error.ReadFailed => switch (file_reader.err.?) { | ||
| 136 | error.Canceled => |e| return e, | ||
| 137 | else => |e| return step.fail(maker, "failed to read from {qf}: {t}", .{ file_path, e }), | ||
| 138 | }, | ||
| 139 | error.WriteFailed => return error.OutOfMemory, | ||
| 140 | }; | ||
| 141 | result.writer.writeAll(suffix) catch return error.OutOfMemory; | ||
| 142 | |||
| 143 | argv_list.appendAssumeCapacity(result.written()); | ||
| 144 | man.hash.addBytesZ(prefix); | ||
| 145 | _ = try man.addInputPath(file_path, .{}); | ||
| 146 | man.hash.addBytesZ(suffix); | ||
| 147 | }, | ||
| 148 | .artifact => { | ||
| 149 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | ||
| 150 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | ||
| 151 | const producer_index = arg.producer.value.?; | ||
| 152 | const producer_step = producer_index.ptr(conf); | ||
| 153 | const producer = producer_step.extended.get(conf.extra).compile; | ||
| 154 | const producer_make_comp_step = maker.stepByIndex(producer_index); | ||
| 155 | const producer_make_comp = &producer_make_comp_step.extended.compile; | ||
| 156 | |||
| 157 | const file_path = producer_make_comp.installed_path orelse | ||
| 158 | maker.generatedPath(producer.generated_bin.value.?); | ||
| 159 | |||
| 160 | argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{ | ||
| 161 | prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix, | ||
| 162 | })); | ||
| 163 | |||
| 164 | man.hash.add(arg.flags.make_absolute); | ||
| 165 | man.hash.addBytesZ(prefix); | ||
| 166 | man.hash.addBytesZ(suffix); | ||
| 167 | _ = try man.addInputPath(file_path, .{}); | ||
| 168 | }, | ||
| 169 | .output_file, .output_directory => { | ||
| 170 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | ||
| 171 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | ||
| 172 | const basename = arg.basename.value.?.slice(conf); | ||
| 173 | |||
| 174 | man.hash.add(arg.flags.make_absolute); | ||
| 175 | man.hash.addBytesZ(prefix); | ||
| 176 | man.hash.addBytesZ(basename); | ||
| 177 | man.hash.addBytesZ(suffix); | ||
| 178 | man.hash.add(arg.flags.dep_file); | ||
| 179 | |||
| 180 | if (arg.flags.dep_file) any_discovered_inputs = true; | ||
| 181 | any_output_args = true; | ||
| 182 | |||
| 183 | // Add a placeholder into the argument list because we need the | ||
| 184 | // manifest hash to be updated with all arguments before the | ||
| 185 | // object directory is computed. | ||
| 186 | try output_placeholders.append(gpa, .{ | ||
| 187 | .index = @intCast(argv_list.items.len), | ||
| 188 | .offset = 0, | ||
| 189 | .arg_index = arg_index, | ||
| 190 | }); | ||
| 191 | _ = argv_list.addOneAssumeCapacity(); | ||
| 192 | }, | ||
| 193 | .passthru => { | ||
| 194 | any_cli_positionals = true; | ||
| 195 | if (maker.run_args) |run_args| { | ||
| 196 | try argv_list.appendSlice(gpa, run_args); | ||
| 197 | man.hash.addListOfBytes(run_args); | ||
| 198 | } | ||
| 199 | }, | ||
| 200 | .enable_darling => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_darling, arg.prefix.value, arg.suffix.value), | ||
| 201 | .enable_qemu => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_qemu, arg.prefix.value, arg.suffix.value), | ||
| 202 | .enable_rosetta => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_rosetta, arg.prefix.value, arg.suffix.value), | ||
| 203 | .enable_wasmtime => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_wasmtime, arg.prefix.value, arg.suffix.value), | ||
| 204 | .enable_wine => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value), | ||
| 205 | } | ||
| 206 | } | ||
| 79 | 207 | ||
| 80 | for (conf_run.args.slice) |arg_index| { | 208 | var owned_dirs: std.bit_set.Dynamic = .{}; |
| 81 | const arg = arg_index.get(conf); | 209 | var inherit_dirs: std.ArrayList(Io.Dir) = .empty; |
| 82 | try argv_list.ensureUnusedCapacity(gpa, 1); | 210 | var owned_files: std.bit_set.Dynamic = .{}; |
| 83 | switch (arg.flags.tag) { | 211 | var inherit_files: std.ArrayList(Io.File) = .empty; |
| 84 | .string => { | 212 | var protocol_args: std.ArrayList(u8) = .empty; |
| 85 | const prefix = arg.prefix.value.?.slice(conf); | 213 | var input_dirs: std.ArrayList(Cache.Path) = .empty; |
| 86 | argv_list.appendAssumeCapacity(prefix); | 214 | defer { |
| 87 | man.hash.addBytesZ(prefix); | 215 | for (inherit_dirs.items, 0..) |inherit_dir, inherit_dir_index| |
| 88 | }, | 216 | if (owned_dirs.isSet(inherit_dir_index)) inherit_dir.close(io); |
| 89 | .path_file => { | 217 | for (inherit_files.items, 0..) |inherit_file, inherit_file_index| |
| 90 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | 218 | if (owned_files.isSet(inherit_file_index)) inherit_file.close(io); |
| 91 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | 219 | owned_dirs.deinit(gpa); |
| 92 | const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index); | 220 | inherit_dirs.deinit(gpa); |
| 93 | argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{ | 221 | owned_files.deinit(gpa); |
| 94 | prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix, | 222 | inherit_files.deinit(gpa); |
| 95 | })); | 223 | protocol_args.deinit(gpa); |
| 96 | man.hash.add(arg.flags.make_absolute); | 224 | input_dirs.deinit(gpa); |
| 97 | man.hash.addBytesZ(prefix); | 225 | } |
| 98 | man.hash.addBytesZ(suffix); | 226 | switch (conf_run.flags.stdio) { |
| 99 | _ = try man.addInputPath(file_path, .{}); | 227 | .infer_from_args, .inherit, .check => {}, |
| 100 | }, | 228 | .zig_test => { |
| 101 | .path_directory => { | 229 | const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false); |
| 102 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | 230 | |
| 103 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | 231 | try argv_list.ensureUnusedCapacity(gpa, 3); |
| 104 | const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index); | 232 | argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string})); |
| 105 | const resolved_arg = try mem.concat(arena, u8, &.{ | 233 | argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed})); |
| 106 | prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix, | 234 | argv_list.appendAssumeCapacity("--listen=-"); |
| 107 | }); | ||
| 108 | argv_list.appendAssumeCapacity(resolved_arg); | ||
| 109 | man.hash.add(arg.flags.make_absolute); | ||
| 110 | man.hash.addBytes(resolved_arg); | ||
| 111 | }, | 235 | }, |
| 112 | .file_content => { | 236 | .protocol => { |
| 113 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | 237 | argv_list.appendAssumeCapacity("--listen=-"); |
| 114 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | 238 | try input_dirs.append(gpa, if (conf_run.cwd.value) |lazy_cwd| |
| 115 | const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index); | 239 | try maker.resolveLazyPathIndex(arena, lazy_cwd, run_index) |
| 116 | 240 | else | |
| 117 | var result: std.Io.Writer.Allocating = .init(arena); | 241 | .cwd()); |
| 118 | result.writer.writeAll(prefix) catch return error.OutOfMemory; | 242 | for (conf_run.args.slice[1..]) |arg_index| { |
| 243 | const arg = arg_index.get(conf); | ||
| 244 | switch (arg.flags.tag) { | ||
| 245 | .string => { | ||
| 246 | const string = arg.prefix.value.?.slice(conf); | ||
| 247 | |||
| 248 | man.hash.addBytesZ(string); | ||
| 249 | |||
| 250 | try protocol_args.ensureUnusedCapacity(gpa, 1 + string.len + 1); | ||
| 251 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.string)); | ||
| 252 | protocol_args.appendSliceAssumeCapacity(string); | ||
| 253 | protocol_args.appendAssumeCapacity(0); | ||
| 254 | }, | ||
| 255 | .path_file => { | ||
| 256 | const lazy_path = arg.path.value.?.get(conf); | ||
| 257 | switch (lazy_path) { | ||
| 258 | else => {}, | ||
| 259 | .relative => |relative| switch (relative.flags.base) { | ||
| 260 | else => {}, | ||
| 261 | .libc_runtimes => if (graph.libc_runtimes_dir == null) continue, | ||
| 262 | }, | ||
| 263 | } | ||
| 119 | 264 | ||
| 120 | const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err| | 265 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; |
| 121 | return step.fail(maker, "unable to open input file {f}: {t}", .{ file_path, err }); | 266 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; |
| 122 | defer file.close(io); | 267 | const file_path = try maker.resolveLazyPath(arena, lazy_path, run_index); |
| 268 | |||
| 269 | try owned_files.resize(gpa, owned_files.bit_length + 1, true); | ||
| 270 | try inherit_files.ensureUnusedCapacity(gpa, 1); | ||
| 271 | const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err| | ||
| 272 | return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err }); | ||
| 273 | inherit_files.appendAssumeCapacity(file); | ||
| 274 | |||
| 275 | man.hash.addBytesZ(prefix); | ||
| 276 | _ = try man.addInputPath(file_path, .{}); | ||
| 277 | man.hash.addBytesZ(suffix); | ||
| 278 | |||
| 279 | try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 + | ||
| 280 | 1 + @sizeOf(Io.File.Handle) + | ||
| 281 | 1 + suffix.len + 1); | ||
| 282 | if (prefix.len > 0) { | ||
| 283 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix)); | ||
| 284 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 285 | protocol_args.appendAssumeCapacity(0); | ||
| 286 | } | ||
| 287 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file)); | ||
| 288 | const file_handle: *align(1) Io.File.Handle = | ||
| 289 | @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle))); | ||
| 290 | file_handle.* = file.handle; | ||
| 291 | if (suffix.len > 0) { | ||
| 292 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix)); | ||
| 293 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 294 | protocol_args.appendAssumeCapacity(0); | ||
| 295 | } | ||
| 296 | }, | ||
| 297 | .path_directory => { | ||
| 298 | const lazy_path = arg.path.value.?.get(conf); | ||
| 299 | switch (lazy_path) { | ||
| 300 | else => {}, | ||
| 301 | .relative => |relative| switch (relative.flags.base) { | ||
| 302 | else => {}, | ||
| 303 | .libc_runtimes => if (graph.libc_runtimes_dir == null) continue, | ||
| 304 | }, | ||
| 305 | } | ||
| 123 | 306 | ||
| 124 | var file_reader = file.reader(io, &.{}); | 307 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; |
| 125 | _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) { | 308 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; |
| 126 | error.ReadFailed => switch (file_reader.err.?) { | 309 | const dir_path = try maker.resolveLazyPath(arena, lazy_path, run_index); |
| 127 | error.Canceled => |e| return e, | 310 | try input_dirs.append(gpa, dir_path); |
| 128 | else => |e| return step.fail(maker, "failed to read from {f}: {t}", .{ file_path, e }), | 311 | |
| 129 | }, | 312 | const has_sub_path = dir_path.sub_path.len > 0; |
| 130 | error.WriteFailed => return error.OutOfMemory, | 313 | try owned_dirs.resize(gpa, owned_dirs.bit_length + 1, has_sub_path); |
| 131 | }; | 314 | try inherit_dirs.ensureUnusedCapacity(gpa, 1); |
| 132 | result.writer.writeAll(suffix) catch return error.OutOfMemory; | 315 | const dir = if (has_sub_path) |
| 316 | dir_path.root_dir.handle.openDir(io, dir_path.sub_path, .{ | ||
| 317 | .iterate = true, | ||
| 318 | }) catch |err| return step.fail(maker, "unable to open input dir {qf}: {t}", .{ dir_path, err }) | ||
| 319 | else | ||
| 320 | dir_path.root_dir.handle; | ||
| 321 | inherit_dirs.appendAssumeCapacity(dir); | ||
| 322 | |||
| 323 | man.hash.addBytesZ(prefix); | ||
| 324 | man.hash.addOptionalBytes(dir_path.root_dir.path); | ||
| 325 | man.hash.addBytes(dir_path.sub_path); | ||
| 326 | man.hash.addBytesZ(suffix); | ||
| 327 | |||
| 328 | try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 + | ||
| 329 | 1 + @sizeOf(Io.Dir.Handle) + | ||
| 330 | 1 + suffix.len + 1); | ||
| 331 | if (prefix.len > 0) { | ||
| 332 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix)); | ||
| 333 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 334 | protocol_args.appendAssumeCapacity(0); | ||
| 335 | } | ||
| 336 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_dir)); | ||
| 337 | const dir_handle: *align(1) Io.Dir.Handle = | ||
| 338 | @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.Dir.Handle))); | ||
| 339 | dir_handle.* = dir.handle; | ||
| 340 | if (suffix.len > 0) { | ||
| 341 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix)); | ||
| 342 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 343 | protocol_args.appendAssumeCapacity(0); | ||
| 344 | } | ||
| 345 | }, | ||
| 346 | .file_content => { | ||
| 347 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | ||
| 348 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | ||
| 349 | const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index); | ||
| 350 | |||
| 351 | try owned_files.resize(gpa, owned_files.bit_length + 1, true); | ||
| 352 | try inherit_files.ensureUnusedCapacity(gpa, 1); | ||
| 353 | const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err| | ||
| 354 | return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err }); | ||
| 355 | inherit_files.appendAssumeCapacity(file); | ||
| 356 | |||
| 357 | man.hash.addBytesZ(prefix); | ||
| 358 | _ = try man.addInputPath(file_path, .{}); | ||
| 359 | man.hash.addBytesZ(suffix); | ||
| 360 | |||
| 361 | try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 + | ||
| 362 | 1 + @sizeOf(Io.File.Handle) + | ||
| 363 | 1 + suffix.len + 1); | ||
| 364 | if (prefix.len > 0) { | ||
| 365 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix)); | ||
| 366 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 367 | protocol_args.appendAssumeCapacity(0); | ||
| 368 | } | ||
| 369 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file_content)); | ||
| 370 | const file_handle: *align(1) Io.File.Handle = | ||
| 371 | @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle))); | ||
| 372 | file_handle.* = file.handle; | ||
| 373 | if (suffix.len > 0) { | ||
| 374 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix)); | ||
| 375 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 376 | protocol_args.appendAssumeCapacity(0); | ||
| 377 | } | ||
| 378 | }, | ||
| 379 | .artifact => { | ||
| 380 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | ||
| 381 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | ||
| 382 | const producer_index = arg.producer.value.?; | ||
| 383 | const producer_step = producer_index.ptr(conf); | ||
| 384 | const producer = producer_step.extended.get(conf.extra).compile; | ||
| 385 | const producer_make_comp_step = maker.stepByIndex(producer_index); | ||
| 386 | const producer_make_comp = &producer_make_comp_step.extended.compile; | ||
| 387 | |||
| 388 | const file_path = producer_make_comp.installed_path orelse | ||
| 389 | maker.generatedPath(producer.generated_bin.value.?); | ||
| 390 | |||
| 391 | try owned_files.resize(gpa, owned_files.bit_length + 1, true); | ||
| 392 | try inherit_files.ensureUnusedCapacity(gpa, 1); | ||
| 393 | const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err| | ||
| 394 | return step.fail(maker, "unable to open input artifact {qf}: {t}", .{ file_path, err }); | ||
| 395 | inherit_files.appendAssumeCapacity(file); | ||
| 396 | |||
| 397 | man.hash.addBytesZ(prefix); | ||
| 398 | _ = try man.addInputPath(file_path, .{}); | ||
| 399 | man.hash.addBytesZ(suffix); | ||
| 400 | |||
| 401 | try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 + | ||
| 402 | 1 + @sizeOf(Io.File.Handle) + | ||
| 403 | 1 + suffix.len + 1); | ||
| 404 | if (prefix.len > 0) { | ||
| 405 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix)); | ||
| 406 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 407 | protocol_args.appendAssumeCapacity(0); | ||
| 408 | } | ||
| 409 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file)); | ||
| 410 | const file_handle: *align(1) Io.File.Handle = | ||
| 411 | @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle))); | ||
| 412 | file_handle.* = file.handle; | ||
| 413 | if (suffix.len > 0) { | ||
| 414 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix)); | ||
| 415 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 416 | protocol_args.appendAssumeCapacity(0); | ||
| 417 | } | ||
| 418 | }, | ||
| 419 | .output_file => { | ||
| 420 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | ||
| 421 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | ||
| 422 | const basename = arg.basename.value.?.slice(conf); | ||
| 423 | |||
| 424 | man.hash.addBytesZ(prefix); | ||
| 425 | man.hash.addBytesZ(basename); | ||
| 426 | man.hash.addBytesZ(suffix); | ||
| 427 | assert(!arg.flags.dep_file); | ||
| 428 | |||
| 429 | try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 + | ||
| 430 | 1 + @sizeOf(Io.File.Handle) + | ||
| 431 | 1 + suffix.len + 1); | ||
| 432 | if (prefix.len > 0) { | ||
| 433 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix)); | ||
| 434 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 435 | protocol_args.appendAssumeCapacity(0); | ||
| 436 | } | ||
| 437 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.output_file)); | ||
| 438 | |||
| 439 | // Add a placeholder into the argument list because we need the | ||
| 440 | // manifest hash to be updated with all arguments before the | ||
| 441 | // object directory is computed. | ||
| 442 | try owned_files.resize(gpa, owned_files.bit_length + 1, false); | ||
| 443 | try inherit_files.ensureUnusedCapacity(gpa, 1); | ||
| 444 | try output_placeholders.append(gpa, .{ | ||
| 445 | .index = @intCast(inherit_files.items.len), | ||
| 446 | .offset = @intCast(protocol_args.items.len), | ||
| 447 | .arg_index = arg_index, | ||
| 448 | }); | ||
| 449 | _ = inherit_files.addOneAssumeCapacity(); | ||
| 450 | |||
| 451 | _ = protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle)); | ||
| 452 | if (suffix.len > 0) { | ||
| 453 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix)); | ||
| 454 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 455 | protocol_args.appendAssumeCapacity(0); | ||
| 456 | } | ||
| 457 | }, | ||
| 458 | .output_directory => { | ||
| 459 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | ||
| 460 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | ||
| 461 | const basename = arg.basename.value.?.slice(conf); | ||
| 462 | |||
| 463 | man.hash.addBytesZ(prefix); | ||
| 464 | man.hash.addBytesZ(basename); | ||
| 465 | man.hash.addBytesZ(suffix); | ||
| 466 | assert(!arg.flags.dep_file); | ||
| 467 | |||
| 468 | try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 + | ||
| 469 | 1 + @sizeOf(Io.Dir.Handle) + | ||
| 470 | 1 + suffix.len + 1); | ||
| 471 | if (prefix.len > 0) { | ||
| 472 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix)); | ||
| 473 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 474 | protocol_args.appendAssumeCapacity(0); | ||
| 475 | } | ||
| 476 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.output_dir)); | ||
| 477 | |||
| 478 | // Add a placeholder into the argument list because we need the | ||
| 479 | // manifest hash to be updated with all arguments before the | ||
| 480 | // object directory is computed. | ||
| 481 | try owned_dirs.resize(gpa, owned_dirs.bit_length + 1, false); | ||
| 482 | try inherit_dirs.ensureUnusedCapacity(gpa, 1); | ||
| 483 | try output_placeholders.append(gpa, .{ | ||
| 484 | .index = @intCast(inherit_dirs.items.len), | ||
| 485 | .offset = @intCast(protocol_args.items.len), | ||
| 486 | .arg_index = arg_index, | ||
| 487 | }); | ||
| 488 | _ = inherit_dirs.addOneAssumeCapacity(); | ||
| 489 | |||
| 490 | _ = protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.Dir.Handle)); | ||
| 491 | if (suffix.len > 0) { | ||
| 492 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix)); | ||
| 493 | protocol_args.appendSliceAssumeCapacity(prefix); | ||
| 494 | protocol_args.appendAssumeCapacity(0); | ||
| 495 | } | ||
| 496 | }, | ||
| 497 | .passthru => if (maker.run_args) |run_args| { | ||
| 498 | man.hash.addListOfBytes(run_args); | ||
| 499 | |||
| 500 | for (run_args) |run_arg| { | ||
| 501 | try protocol_args.ensureUnusedCapacity(gpa, 1 + run_arg.len + 1); | ||
| 502 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.string)); | ||
| 503 | protocol_args.appendSliceAssumeCapacity(run_arg); | ||
| 504 | protocol_args.appendAssumeCapacity(0); | ||
| 505 | } | ||
| 506 | }, | ||
| 507 | .enable_darling => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_darling, arg.prefix.value, arg.suffix.value), | ||
| 508 | .enable_qemu => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_qemu, arg.prefix.value, arg.suffix.value), | ||
| 509 | .enable_rosetta => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_rosetta, arg.prefix.value, arg.suffix.value), | ||
| 510 | .enable_wasmtime => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_wasmtime, arg.prefix.value, arg.suffix.value), | ||
| 511 | .enable_wine => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value), | ||
| 512 | } | ||
| 513 | } | ||
| 514 | }, | ||
| 515 | } | ||
| 133 | 516 | ||
| 134 | argv_list.appendAssumeCapacity(result.written()); | 517 | switch (conf_run.stdin.u) { |
| 135 | man.hash.addBytesZ(prefix); | 518 | .bytes => |bytes| { |
| 136 | man.hash.addBytesZ(suffix); | 519 | man.hash.addBytes(bytes.slice(conf)); |
| 520 | }, | ||
| 521 | .lazy_path => |lazy_path| { | ||
| 522 | const file_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index); | ||
| 137 | _ = try man.addInputPath(file_path, .{}); | 523 | _ = try man.addInputPath(file_path, .{}); |
| 138 | }, | 524 | }, |
| 139 | .artifact => { | 525 | .none => {}, |
| 140 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | 526 | } |
| 141 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | ||
| 142 | const producer_index = arg.producer.value.?; | ||
| 143 | const producer_step = producer_index.ptr(conf); | ||
| 144 | const producer = producer_step.extended.get(conf.extra).compile; | ||
| 145 | const producer_make_comp_step = maker.stepByIndex(producer_index); | ||
| 146 | const producer_make_comp = &producer_make_comp_step.extended.compile; | ||
| 147 | 527 | ||
| 148 | const file_path = producer_make_comp.installed_path orelse | 528 | if (conf_run.captured_stdout.value) |captured| { |
| 149 | maker.generatedPath(producer.generated_bin.value.?); | 529 | man.hash.addBytes(captured.basename.slice(conf)); |
| 530 | man.hash.add(conf_run.flags.stdout_trim_whitespace); | ||
| 531 | } | ||
| 150 | 532 | ||
| 151 | argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{ | 533 | if (conf_run.captured_stderr.value) |captured| { |
| 152 | prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix, | 534 | man.hash.addBytes(captured.basename.slice(conf)); |
| 153 | })); | 535 | man.hash.add(conf_run.flags.stderr_trim_whitespace); |
| 536 | } | ||
| 154 | 537 | ||
| 155 | man.hash.add(arg.flags.make_absolute); | 538 | switch (conf_run.flags.stdio) { |
| 156 | man.hash.addBytesZ(prefix); | 539 | .infer_from_args, .inherit, .zig_test, .protocol => {}, |
| 157 | man.hash.addBytesZ(suffix); | 540 | .check => { |
| 158 | _ = try man.addInputPath(file_path, .{}); | 541 | man.hash.addBytes(if (conf_run.expect_stderr_exact.value) |bytes| bytes.slice(conf) else ""); |
| 159 | }, | 542 | man.hash.addBytes(if (conf_run.expect_stdout_exact.value) |bytes| bytes.slice(conf) else ""); |
| 160 | .output_file, .output_directory => { | 543 | for (conf_run.expect_stderr_match.slice) |bytes| man.hash.addBytes(bytes.slice(conf)); |
| 161 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | 544 | for (conf_run.expect_stdout_match.slice) |bytes| man.hash.addBytes(bytes.slice(conf)); |
| 162 | const suffix = if (arg.suffix.value) |p| p.slice(conf) else ""; | 545 | man.hash.add(conf_run.flags2.expect_term_status); |
| 163 | const basename = arg.basename.value.?.slice(conf); | 546 | man.hash.addOptional(conf_run.expect_term_value.value); |
| 164 | |||
| 165 | man.hash.add(arg.flags.make_absolute); | ||
| 166 | man.hash.addBytesZ(prefix); | ||
| 167 | man.hash.addBytesZ(basename); | ||
| 168 | man.hash.addBytesZ(suffix); | ||
| 169 | man.hash.add(arg.flags.dep_file); | ||
| 170 | |||
| 171 | any_dep_files = any_dep_files or arg.flags.dep_file; | ||
| 172 | any_output_args = true; | ||
| 173 | |||
| 174 | // Add a placeholder into the argument list because we need the | ||
| 175 | // manifest hash to be updated with all arguments before the | ||
| 176 | // object directory is computed. | ||
| 177 | try output_placeholders.append(gpa, .{ | ||
| 178 | .index = @intCast(argv_list.items.len), | ||
| 179 | .arg_index = arg_index, | ||
| 180 | }); | ||
| 181 | argv_list.items.len += 1; | ||
| 182 | }, | ||
| 183 | .passthru => { | ||
| 184 | any_cli_positionals = true; | ||
| 185 | if (maker.run_args) |run_args| { | ||
| 186 | try argv_list.appendSlice(gpa, run_args); | ||
| 187 | man.hash.addListOfBytes(run_args); | ||
| 188 | } | ||
| 189 | }, | 547 | }, |
| 190 | .enable_darling => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_darling, arg.prefix.value, arg.suffix.value), | ||
| 191 | .enable_qemu => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_qemu, arg.prefix.value, arg.suffix.value), | ||
| 192 | .enable_rosetta => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_rosetta, arg.prefix.value, arg.suffix.value), | ||
| 193 | .enable_wasmtime => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_wasmtime, arg.prefix.value, arg.suffix.value), | ||
| 194 | .enable_wine => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value), | ||
| 195 | } | 548 | } |
| 196 | } | ||
| 197 | 549 | ||
| 198 | man.hash.add(conf_run.flags.test_runner_mode); | 550 | for (conf_run.file_inputs.slice) |lazy_path| { |
| 199 | if (conf_run.flags.test_runner_mode) { | ||
| 200 | const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false); | ||
| 201 | |||
| 202 | try argv_list.ensureUnusedCapacity(gpa, 3); | ||
| 203 | argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string})); | ||
| 204 | argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed})); | ||
| 205 | argv_list.appendAssumeCapacity("--listen=-"); | ||
| 206 | } | ||
| 207 | |||
| 208 | switch (conf_run.stdin.u) { | ||
| 209 | .bytes => |bytes| { | ||
| 210 | man.hash.addBytes(bytes.slice(conf)); | ||
| 211 | }, | ||
| 212 | .lazy_path => |lazy_path| { | ||
| 213 | const file_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index); | 551 | const file_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index); |
| 214 | _ = try man.addInputPath(file_path, .{}); | 552 | _ = try man.addInputPath(file_path, .{}); |
| 215 | }, | 553 | } |
| 216 | .none => {}, | ||
| 217 | } | ||
| 218 | |||
| 219 | if (conf_run.captured_stdout.value) |captured| { | ||
| 220 | man.hash.addBytes(captured.basename.slice(conf)); | ||
| 221 | man.hash.add(conf_run.flags.stdout_trim_whitespace); | ||
| 222 | } | ||
| 223 | 554 | ||
| 224 | if (conf_run.captured_stderr.value) |captured| { | 555 | if (conf_run.cwd.value) |lazy_path| { |
| 225 | man.hash.addBytes(captured.basename.slice(conf)); | 556 | const cwd_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index); |
| 226 | man.hash.add(conf_run.flags.stderr_trim_whitespace); | 557 | _ = man.hash.addBytes(try cwd_path.toString(arena)); |
| 227 | } | 558 | } |
| 228 | 559 | ||
| 229 | switch (conf_run.flags.stdio) { | 560 | // Whether the Run step has side effects *other than* updating the output arguments. |
| 230 | .infer_from_args, .inherit, .zig_test => {}, | 561 | // When fuzzing we need to always run the test runner to populate fuzz_tests. |
| 231 | .check => { | 562 | const has_side_effects = graph.fuzzing or conf_run.flags.has_side_effects or any_cli_positionals or |
| 232 | man.hash.addBytes(if (conf_run.expect_stderr_exact.value) |bytes| bytes.slice(conf) else ""); | 563 | switch (conf_run.flags.stdio) { |
| 233 | man.hash.addBytes(if (conf_run.expect_stdout_exact.value) |bytes| bytes.slice(conf) else ""); | 564 | .infer_from_args => !any_output_args and |
| 234 | for (conf_run.expect_stderr_match.slice) |bytes| man.hash.addBytes(bytes.slice(conf)); | 565 | conf_run.captured_stdout.value == null and |
| 235 | for (conf_run.expect_stdout_match.slice) |bytes| man.hash.addBytes(bytes.slice(conf)); | 566 | conf_run.captured_stderr.value == null, |
| 236 | man.hash.add(conf_run.flags2.expect_term_status); | 567 | .inherit => true, |
| 237 | man.hash.addOptional(conf_run.expect_term_value.value); | 568 | .check, .zig_test, .protocol => false, |
| 238 | }, | 569 | }; |
| 239 | } | ||
| 240 | 570 | ||
| 241 | for (conf_run.file_inputs.slice) |lazy_path| { | 571 | if (!has_side_effects and try step.cacheHitWatched(maker, &man, progress_node)) { |
| 242 | const file_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index); | 572 | // Cache hit; skip running command. |
| 243 | _ = try man.addInputPath(file_path, .{}); | 573 | const digest = man.hitDigestHex(); |
| 244 | } | 574 | try populateGeneratedStdIo(maker, &conf_run, &digest); |
| 575 | try populateGeneratedPaths(maker, output_placeholders.items, &digest); | ||
| 576 | step.result_cached = true; | ||
| 577 | return; | ||
| 578 | } | ||
| 245 | 579 | ||
| 246 | if (conf_run.cwd.value) |lazy_path| { | 580 | if (!any_discovered_inputs) { |
| 247 | const cwd_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index); | 581 | // We already know the final output paths; use them directly. |
| 248 | _ = man.hash.addBytes(try cwd_path.toString(arena)); | 582 | const digest = if (has_side_effects) man.hash.final() else man.missDigestHex(); |
| 249 | } | 583 | const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest; |
| 584 | try populateGeneratedStdIo(maker, &conf_run, &digest); | ||
| 585 | try populateGeneratedPathsCreateDirs(arena, run_index, maker, output_dir_path, output_placeholders.items, switch (conf_run.flags.stdio) { | ||
| 586 | .infer_from_args, .inherit, .check, .zig_test => .{ .argv = argv_list.items }, | ||
| 587 | .protocol => .{ .protocol = .{ | ||
| 588 | .owned_dirs = &owned_dirs, | ||
| 589 | .inherit_dirs = inherit_dirs.items, | ||
| 590 | .owned_files = &owned_files, | ||
| 591 | .inherit_files = inherit_files.items, | ||
| 592 | .args = protocol_args.items, | ||
| 593 | } }, | ||
| 594 | }); | ||
| 595 | runCommand(arena, run, run_index, maker, progress_node, argv_list.items, &.{}, &.{}, &.{}, &.{}, null, has_side_effects, output_dir_path, null) catch |err| switch (err) { | ||
| 596 | else => |e| return e, | ||
| 597 | error.MakeFailed, error.MakeSkipped => |e| { | ||
| 598 | try step.setWatchInputsFromManifest(maker, &man); | ||
| 599 | return e; | ||
| 600 | }, | ||
| 601 | }; | ||
| 602 | if (has_side_effects) { | ||
| 603 | try step.setWatchInputsFromManifest(maker, &man); | ||
| 604 | } else { | ||
| 605 | try step.finalizeManifestAndWatch(maker, &man); | ||
| 606 | } | ||
| 607 | return; | ||
| 608 | } | ||
| 250 | 609 | ||
| 251 | // Whether the Run step has side effects *other than* updating the output arguments. | 610 | // We do not know the final output paths yet; use temporary directory to run the command. |
| 252 | // When fuzzing we need to always run the test runner to populate fuzz_tests. | 611 | var rand_int: u64 = undefined; |
| 253 | const has_side_effects = graph.fuzzing or conf_run.flags.has_side_effects or any_cli_positionals or | 612 | io.random(@ptrCast(&rand_int)); |
| 254 | switch (conf_run.flags.stdio) { | 613 | tmp_dir_path = ("tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int)).*; |
| 255 | .infer_from_args => !any_output_args and | 614 | |
| 256 | conf_run.captured_stdout.value == null and | 615 | try populateGeneratedPathsCreateDirs(arena, run_index, maker, &tmp_dir_path, output_placeholders.items, switch (conf_run.flags.stdio) { |
| 257 | conf_run.captured_stderr.value == null, | 616 | .infer_from_args, .inherit, .check, .zig_test => .{ .argv = argv_list.items }, |
| 258 | .inherit => true, | 617 | .protocol => .{ .protocol = .{ |
| 259 | .check, .zig_test => false, | 618 | .owned_dirs = &owned_dirs, |
| 619 | .inherit_dirs = inherit_dirs.items, | ||
| 620 | .owned_files = &owned_files, | ||
| 621 | .inherit_files = inherit_files.items, | ||
| 622 | .args = protocol_args.items, | ||
| 623 | } }, | ||
| 624 | }); | ||
| 625 | runCommand( | ||
| 626 | arena, | ||
| 627 | run, | ||
| 628 | run_index, | ||
| 629 | maker, | ||
| 630 | progress_node, | ||
| 631 | argv_list.items, | ||
| 632 | inherit_dirs.items, | ||
| 633 | inherit_files.items, | ||
| 634 | protocol_args.items, | ||
| 635 | input_dirs.items, | ||
| 636 | &man, | ||
| 637 | has_side_effects, | ||
| 638 | &tmp_dir_path, | ||
| 639 | null, | ||
| 640 | ) catch |err| switch (err) { | ||
| 641 | else => |e| return e, | ||
| 642 | error.MakeFailed, error.MakeSkipped => |e| { | ||
| 643 | try step.setWatchInputsFromManifest(maker, &man); | ||
| 644 | return e; | ||
| 645 | }, | ||
| 260 | }; | 646 | }; |
| 261 | 647 | ||
| 262 | if (!has_side_effects and try step.cacheHitWatched(maker, &man, progress_node)) { | 648 | break :has_side_effects has_side_effects; |
| 263 | // Cache hit; skip running command. | 649 | }; |
| 264 | const digest = man.hitDigestHex(); | ||
| 265 | try populateGeneratedStdIo(maker, &conf_run, &digest); | ||
| 266 | try populateGeneratedPaths(maker, output_placeholders.items, &digest); | ||
| 267 | step.result_cached = true; | ||
| 268 | return; | ||
| 269 | } | ||
| 270 | |||
| 271 | if (!any_dep_files) { | ||
| 272 | // We already know the final output paths; use them directly. | ||
| 273 | const digest = if (has_side_effects) man.hash.final() else man.missDigestHex(); | ||
| 274 | const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest; | ||
| 275 | try populateGeneratedStdIo(maker, &conf_run, &digest); | ||
| 276 | try populateGeneratedPathsCreateDirs(arena, run_index, maker, output_dir_path, output_placeholders.items, argv_list.items); | ||
| 277 | try runCommand(arena, run, run_index, maker, progress_node, argv_list.items, has_side_effects, output_dir_path, null); | ||
| 278 | if (!has_side_effects) try step.finalizeManifestAndWatch(maker, &man); | ||
| 279 | return; | ||
| 280 | } | ||
| 281 | |||
| 282 | // We do not know the final output paths yet; use temporary directory to run the command. | ||
| 283 | var rand_int: u64 = undefined; | ||
| 284 | io.random(@ptrCast(&rand_int)); | ||
| 285 | const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); | ||
| 286 | |||
| 287 | try populateGeneratedPathsCreateDirs(arena, run_index, maker, tmp_dir_path, output_placeholders.items, argv_list.items); | ||
| 288 | try runCommand(arena, run, run_index, maker, progress_node, argv_list.items, has_side_effects, tmp_dir_path, null); | ||
| 289 | 650 | ||
| 290 | for (output_placeholders.items) |placeholder| { | 651 | for (output_placeholders.items) |placeholder| { |
| 291 | const arg = placeholder.arg_index.get(conf); | 652 | const arg = placeholder.arg_index.get(conf); |
| ... | @@ -296,10 +657,10 @@ pub fn make( | ... | @@ -296,10 +657,10 @@ pub fn make( |
| 296 | var diagnostic: Cache.DepTokenizer.Token = undefined; | 657 | var diagnostic: Cache.DepTokenizer.Token = undefined; |
| 297 | man.addInputDepFile(generated_path, &diagnostic) catch |err| switch (err) { | 658 | man.addInputDepFile(generated_path, &diagnostic) catch |err| switch (err) { |
| 298 | error.OutOfMemory, error.Canceled => |e| return e, | 659 | error.OutOfMemory, error.Canceled => |e| return e, |
| 299 | error.InvalidDepFile => return step.fail(maker, "failed adding dep file {f} to cache: {f}", .{ | 660 | error.InvalidDepFile => return step.fail(maker, "failed adding dep file {qf} to cache: {f}", .{ |
| 300 | generated_path, diagnostic, | 661 | generated_path, diagnostic, |
| 301 | }), | 662 | }), |
| 302 | else => |e| return step.fail(maker, "failed adding dep file {f} to cache: {t}", .{ | 663 | else => |e| return step.fail(maker, "failed adding dep file {qf} to cache: {t}", .{ |
| 303 | generated_path, e, | 664 | generated_path, e, |
| 304 | }), | 665 | }), |
| 305 | }; | 666 | }; |
| ... | @@ -307,13 +668,13 @@ pub fn make( | ... | @@ -307,13 +668,13 @@ pub fn make( |
| 307 | var diagnostic: Cache.Manifest.AddDiscoveredDepFileDiagnostic = undefined; | 668 | var diagnostic: Cache.Manifest.AddDiscoveredDepFileDiagnostic = undefined; |
| 308 | man.addDiscoveredDepFile(generated_path, &diagnostic) catch |err| switch (err) { | 669 | man.addDiscoveredDepFile(generated_path, &diagnostic) catch |err| switch (err) { |
| 309 | error.OutOfMemory, error.Canceled => |e| return e, | 670 | error.OutOfMemory, error.Canceled => |e| return e, |
| 310 | error.InvalidDepFile => return step.fail(maker, "failed adding dep file {f} to cache: {f}", .{ | 671 | error.InvalidDepFile => return step.fail(maker, "failed adding dep file {qf} to cache: {f}", .{ |
| 311 | generated_path, diagnostic.dep_tokenizer, | 672 | generated_path, diagnostic.dep_tokenizer, |
| 312 | }), | 673 | }), |
| 313 | error.FileSystemFailure => return step.fail(maker, "failed adding a path from dep file {f} to cache: {f}", .{ | 674 | error.FileSystemFailure => return step.fail(maker, "failed adding a path from dep file {qf} to cache: {f}", .{ |
| 314 | generated_path, diagnostic.add_discovered_path, | 675 | generated_path, diagnostic.add_discovered_path, |
| 315 | }), | 676 | }), |
| 316 | else => |e| return step.fail(maker, "failed adding dep file {f} to cache: {t}", .{ | 677 | else => |e| return step.fail(maker, "failed adding dep file {qf} to cache: {t}", .{ |
| 317 | generated_path, e, | 678 | generated_path, e, |
| 318 | }), | 679 | }), |
| 319 | }; | 680 | }; |
| ... | @@ -331,7 +692,7 @@ pub fn make( | ... | @@ -331,7 +692,7 @@ pub fn make( |
| 331 | 692 | ||
| 332 | if (any_output) { | 693 | if (any_output) { |
| 333 | // Rename into place. | 694 | // Rename into place. |
| 334 | const tmp_path: Path = .{ .root_dir = cache_root, .sub_path = tmp_dir_path }; | 695 | const tmp_path: Path = .{ .root_dir = cache_root, .sub_path = &tmp_dir_path }; |
| 335 | const dst_path: Path = .{ .root_dir = cache_root, .sub_path = "o" ++ Dir.path.sep_str ++ &digest }; | 696 | const dst_path: Path = .{ .root_dir = cache_root, .sub_path = "o" ++ Dir.path.sep_str ++ &digest }; |
| 336 | Dir.rename( | 697 | Dir.rename( |
| 337 | tmp_path.root_dir.handle, | 698 | tmp_path.root_dir.handle, |
| ... | @@ -342,7 +703,7 @@ pub fn make( | ... | @@ -342,7 +703,7 @@ pub fn make( |
| 342 | ) catch |err| switch (err) { | 703 | ) catch |err| switch (err) { |
| 343 | error.DirNotEmpty => { | 704 | error.DirNotEmpty => { |
| 344 | dst_path.root_dir.handle.deleteTree(io, dst_path.sub_path) catch |del_err| | 705 | dst_path.root_dir.handle.deleteTree(io, dst_path.sub_path) catch |del_err| |
| 345 | return step.fail(maker, "failed to remove tree {f}: {t}", .{ dst_path, del_err }); | 706 | return step.fail(maker, "failed to remove tree {qf}: {t}", .{ dst_path, del_err }); |
| 346 | 707 | ||
| 347 | Dir.rename( | 708 | Dir.rename( |
| 348 | tmp_path.root_dir.handle, | 709 | tmp_path.root_dir.handle, |
| ... | @@ -350,17 +711,21 @@ pub fn make( | ... | @@ -350,17 +711,21 @@ pub fn make( |
| 350 | dst_path.root_dir.handle, | 711 | dst_path.root_dir.handle, |
| 351 | dst_path.sub_path, | 712 | dst_path.sub_path, |
| 352 | io, | 713 | io, |
| 353 | ) catch |retry_err| return step.fail(maker, "failed to rename directory {f} to {f}: {t}", .{ | 714 | ) catch |retry_err| return step.fail(maker, "failed to rename directory {qf} to {qf}: {t}", .{ |
| 354 | tmp_path, dst_path, retry_err, | 715 | tmp_path, dst_path, retry_err, |
| 355 | }); | 716 | }); |
| 356 | }, | 717 | }, |
| 357 | else => return step.fail(maker, "failed to rename directory {f} to {f}: {t}", .{ | 718 | else => return step.fail(maker, "failed to rename directory {qf} to {qf}: {t}", .{ |
| 358 | tmp_path, dst_path, err, | 719 | tmp_path, dst_path, err, |
| 359 | }), | 720 | }), |
| 360 | }; | 721 | }; |
| 361 | } | 722 | } |
| 362 | 723 | ||
| 363 | if (!has_side_effects) try step.finalizeManifestAndWatch(maker, &man); | 724 | if (has_side_effects) { |
| 725 | try step.setWatchInputsFromManifest(maker, &man); | ||
| 726 | } else { | ||
| 727 | try step.finalizeManifestAndWatch(maker, &man); | ||
| 728 | } | ||
| 364 | 729 | ||
| 365 | try populateGeneratedStdIo(maker, &conf_run, &digest); | 730 | try populateGeneratedStdIo(maker, &conf_run, &digest); |
| 366 | try populateGeneratedPaths(maker, output_placeholders.items, &digest); | 731 | try populateGeneratedPaths(maker, output_placeholders.items, &digest); |
| ... | @@ -386,19 +751,30 @@ fn thirdPartyToggle( | ... | @@ -386,19 +751,30 @@ fn thirdPartyToggle( |
| 386 | enable: ?Configuration.String, | 751 | enable: ?Configuration.String, |
| 387 | disable: ?Configuration.String, | 752 | disable: ?Configuration.String, |
| 388 | ) void { | 753 | ) void { |
| 389 | if (setting) { | 754 | const string = (if (setting) enable else disable) orelse return; |
| 390 | if (enable) |string| { | 755 | const slice = string.slice(conf); |
| 391 | const slice = string.slice(conf); | 756 | if (man_hash) |h| h.addBytesZ(slice); |
| 392 | if (man_hash) |h| h.addBytesZ(slice); | 757 | argv_list.appendAssumeCapacity(slice); |
| 393 | argv_list.appendAssumeCapacity(slice); | 758 | } |
| 394 | } | 759 | |
| 395 | } else { | 760 | fn thirdPartyToggleProtocol( |
| 396 | if (disable) |string| { | 761 | gpa: Allocator, |
| 397 | const slice = string.slice(conf); | 762 | man_hash: *Cache.HashHelper, |
| 398 | if (man_hash) |h| h.addBytesZ(slice); | 763 | protocol_args: *std.ArrayList(u8), |
| 399 | argv_list.appendAssumeCapacity(slice); | 764 | conf: *const Configuration, |
| 400 | } | 765 | setting: bool, |
| 401 | } | 766 | enable: ?Configuration.String, |
| 767 | disable: ?Configuration.String, | ||
| 768 | ) Allocator.Error!void { | ||
| 769 | const string = (if (setting) enable else disable) orelse return; | ||
| 770 | const slice = string.slice(conf); | ||
| 771 | |||
| 772 | man_hash.addBytesZ(slice); | ||
| 773 | |||
| 774 | try protocol_args.ensureUnusedCapacity(gpa, 1 + slice.len + 1); | ||
| 775 | protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.string)); | ||
| 776 | protocol_args.appendSliceAssumeCapacity(slice); | ||
| 777 | protocol_args.appendAssumeCapacity(0); | ||
| 402 | } | 778 | } |
| 403 | 779 | ||
| 404 | /// Reads stdout of a Zig test process until a termination condition is reached: | 780 | /// Reads stdout of a Zig test process until a termination condition is reached: |
| ... | @@ -411,7 +787,11 @@ fn waitZigTest( | ... | @@ -411,7 +787,11 @@ fn waitZigTest( |
| 411 | run_index: Configuration.Step.Index, | 787 | run_index: Configuration.Step.Index, |
| 412 | maker: *Maker, | 788 | maker: *Maker, |
| 413 | child: *process.Child, | 789 | child: *process.Child, |
| 414 | progress_node: std.Progress.Node, | 790 | tests_prog_node: std.Progress.Node, |
| 791 | test_prog_node: std.Progress.Node, | ||
| 792 | protocol_args: []const u8, | ||
| 793 | input_dirs: []const Cache.Path, | ||
| 794 | man: ?*Cache.Manifest, | ||
| 415 | multi_reader: *Io.File.MultiReader, | 795 | multi_reader: *Io.File.MultiReader, |
| 416 | opt_metadata: *?TestMetadata, | 796 | opt_metadata: *?TestMetadata, |
| 417 | results: *Step.TestResults, | 797 | results: *Step.TestResults, |
| ... | @@ -431,9 +811,6 @@ fn waitZigTest( | ... | @@ -431,9 +811,6 @@ fn waitZigTest( |
| 431 | const io = graph.io; | 811 | const io = graph.io; |
| 432 | const step = maker.stepByIndex(run_index); | 812 | const step = maker.stepByIndex(run_index); |
| 433 | 813 | ||
| 434 | var sub_prog_node: ?std.Progress.Node = null; | ||
| 435 | defer if (sub_prog_node) |n| n.end(); | ||
| 436 | |||
| 437 | const stdout = multi_reader.reader(0); | 814 | const stdout = multi_reader.reader(0); |
| 438 | const stderr = multi_reader.reader(1); | 815 | const stderr = multi_reader.reader(1); |
| 439 | 816 | ||
| ... | @@ -444,9 +821,17 @@ fn waitZigTest( | ... | @@ -444,9 +821,17 @@ fn waitZigTest( |
| 444 | .out = &stdin_writer.interface, | 821 | .out = &stdin_writer.interface, |
| 445 | }; | 822 | }; |
| 446 | 823 | ||
| 824 | if (protocol_args.len > 0) { | ||
| 825 | try client.serveMessageHeader(.{ | ||
| 826 | .tag = .args, | ||
| 827 | .bytes_len = @intCast(protocol_args.len), | ||
| 828 | }); | ||
| 829 | try client.out.writeAll(protocol_args); | ||
| 830 | } | ||
| 831 | |||
| 447 | if (opt_metadata.*) |*md| { | 832 | if (opt_metadata.*) |*md| { |
| 448 | // Previous unit test process died or was killed; we're continuing where it left off | 833 | // Previous unit test process died or was killed; we're continuing where it left off |
| 449 | requestNextTest(&client, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; | 834 | requestNextTest(&client, md, test_prog_node) catch |err| return .{ .write_failed = err }; |
| 450 | } else { | 835 | } else { |
| 451 | // Running unit tests normally | 836 | // Running unit tests normally |
| 452 | run.fuzz_tests.clearRetainingCapacity(); | 837 | run.fuzz_tests.clearRetainingCapacity(); |
| ... | @@ -492,6 +877,7 @@ fn waitZigTest( | ... | @@ -492,6 +877,7 @@ fn waitZigTest( |
| 492 | var body_r: std.Io.Reader = .fixed(body); | 877 | var body_r: std.Io.Reader = .fixed(body); |
| 493 | 878 | ||
| 494 | switch (header.tag) { | 879 | switch (header.tag) { |
| 880 | else => {}, // ignore other messages | ||
| 495 | .zig_version => { | 881 | .zig_version => { |
| 496 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail( | 882 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail( |
| 497 | maker, | 883 | maker, |
| ... | @@ -499,6 +885,7 @@ fn waitZigTest( | ... | @@ -499,6 +885,7 @@ fn waitZigTest( |
| 499 | .{ builtin.zig_version_string, body }, | 885 | .{ builtin.zig_version_string, body }, |
| 500 | ); | 886 | ); |
| 501 | }, | 887 | }, |
| 888 | .error_bundle => step.result_error_bundle = try std.zig.Server.allocErrorBundle(gpa, body), | ||
| 502 | .test_metadata => { | 889 | .test_metadata => { |
| 503 | // `metadata` would only be populated if we'd already seen a `test_metadata`, but we | 890 | // `metadata` would only be populated if we'd already seen a `test_metadata`, but we |
| 504 | // only request it once (and importantly, we don't re-request it if we kill and | 891 | // only request it once (and importantly, we don't re-request it if we kill and |
| ... | @@ -516,27 +903,27 @@ fn waitZigTest( | ... | @@ -516,27 +903,27 @@ fn waitZigTest( |
| 516 | 903 | ||
| 517 | const string_bytes = body_r.take(tm_hdr.string_bytes_len) catch unreachable; | 904 | const string_bytes = body_r.take(tm_hdr.string_bytes_len) catch unreachable; |
| 518 | 905 | ||
| 519 | progress_node.setEstimatedTotalItems(names.len); | 906 | tests_prog_node.setEstimatedTotalItems(names.len); |
| 520 | opt_metadata.* = .{ | 907 | opt_metadata.* = .{ |
| 521 | .string_bytes = try arena.dupe(u8, string_bytes), | 908 | .string_bytes = try arena.dupe(u8, string_bytes), |
| 522 | .ns_per_test = try arena.alloc(u64, results.test_count), | 909 | .ns_per_test = try arena.alloc(u64, results.test_count), |
| 523 | .names = names, | 910 | .names = names, |
| 524 | .expected_panic_msgs = expected_panic_msgs, | 911 | .expected_panic_msgs = expected_panic_msgs, |
| 525 | .next_index = 0, | 912 | .next_index = 0, |
| 526 | .prog_node = progress_node, | ||
| 527 | }; | 913 | }; |
| 528 | @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64)); | 914 | @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64)); |
| 529 | 915 | ||
| 530 | active_test_index = null; | 916 | active_test_index = null; |
| 531 | last_update = .now(io, .awake); | 917 | last_update = .now(io, .awake); |
| 532 | 918 | ||
| 533 | requestNextTest(&client, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err }; | 919 | requestNextTest(&client, &opt_metadata.*.?, test_prog_node) catch |err| return .{ .write_failed = err }; |
| 534 | }, | 920 | }, |
| 535 | .test_started => { | 921 | .test_started => { |
| 536 | active_test_index = opt_metadata.*.?.next_index - 1; | 922 | active_test_index = opt_metadata.*.?.next_index - 1; |
| 537 | last_update = .now(io, .awake); | 923 | last_update = .now(io, .awake); |
| 538 | }, | 924 | }, |
| 539 | .test_results => { | 925 | .test_results => { |
| 926 | tests_prog_node.completeOne(); | ||
| 540 | const md = &opt_metadata.*.?; | 927 | const md = &opt_metadata.*.?; |
| 541 | 928 | ||
| 542 | const tr_hdr = body_r.takeStruct(std.zig.Server.Message.TestResults, .little) catch unreachable; | 929 | const tr_hdr = body_r.takeStruct(std.zig.Server.Message.TestResults, .little) catch unreachable; |
| ... | @@ -558,10 +945,12 @@ fn waitZigTest( | ... | @@ -558,10 +945,12 @@ fn waitZigTest( |
| 558 | const name = md.testName(tr_hdr.index); | 945 | const name = md.testName(tr_hdr.index); |
| 559 | const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n"); | 946 | const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n"); |
| 560 | stderr.tossBuffered(); | 947 | stderr.tossBuffered(); |
| 561 | if (stderr_bytes.len == 0) { | 948 | if (stderr_bytes.len > 0) { |
| 562 | try step.addError(maker, "'{s}' failed without output", .{name}); | ||
| 563 | } else { | ||
| 564 | try step.addError(maker, "'{s}' failed:\n{s}", .{ name, stderr_bytes }); | 949 | try step.addError(maker, "'{s}' failed:\n{s}", .{ name, stderr_bytes }); |
| 950 | } else if (step.result_error_bundle.errorMessageCount() > 0) { | ||
| 951 | try step.addError(maker, "'{s}' errored", .{name}); | ||
| 952 | } else { | ||
| 953 | try step.addError(maker, "'{s}' failed without output", .{name}); | ||
| 565 | } | 954 | } |
| 566 | } else if (leak_count > 0) { | 955 | } else if (leak_count > 0) { |
| 567 | const name = md.testName(tr_hdr.index); | 956 | const name = md.testName(tr_hdr.index); |
| ... | @@ -581,9 +970,18 @@ fn waitZigTest( | ... | @@ -581,9 +970,18 @@ fn waitZigTest( |
| 581 | md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds); | 970 | md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds); |
| 582 | last_update = now; | 971 | last_update = now; |
| 583 | 972 | ||
| 584 | requestNextTest(&client, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; | 973 | requestNextTest(&client, md, test_prog_node) catch |err| return .{ .write_failed = err }; |
| 974 | }, | ||
| 975 | .discovered_inputs => while (body_r.takeEnum( | ||
| 976 | std.zig.Server.Message.InputDir, | ||
| 977 | .little, | ||
| 978 | )) |input_dir| try man.?.addDiscoveredPath(.{ .discovered_path = .{ | ||
| 979 | .unresolved = try input_dirs[@backingInt(input_dir)] | ||
| 980 | .join(arena, try body_r.takeSentinel(0)), | ||
| 981 | } }) else |err| switch (err) { | ||
| 982 | else => |e| return e, | ||
| 983 | error.EndOfStream => {}, | ||
| 585 | }, | 984 | }, |
| 586 | else => {}, // ignore other messages | ||
| 587 | } | 985 | } |
| 588 | } | 986 | } |
| 589 | } | 987 | } |
| ... | @@ -820,6 +1218,7 @@ const FuzzTestRunner = struct { | ... | @@ -820,6 +1218,7 @@ const FuzzTestRunner = struct { |
| 820 | } | 1218 | } |
| 821 | 1219 | ||
| 822 | switch (header.tag) { | 1220 | switch (header.tag) { |
| 1221 | else => {}, // ignore other messages | ||
| 823 | .zig_version => { | 1222 | .zig_version => { |
| 824 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail( | 1223 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail( |
| 825 | maker, | 1224 | maker, |
| ... | @@ -882,7 +1281,6 @@ const FuzzTestRunner = struct { | ... | @@ -882,7 +1281,6 @@ const FuzzTestRunner = struct { |
| 882 | f.pending_broadcasts.appendSliceAssumeCapacity(@ptrCast(&footer)); | 1281 | f.pending_broadcasts.appendSliceAssumeCapacity(@ptrCast(&footer)); |
| 883 | } | 1282 | } |
| 884 | }, | 1283 | }, |
| 885 | else => {}, // ignore other messages | ||
| 886 | } | 1284 | } |
| 887 | 1285 | ||
| 888 | instance.message.clearRetainingCapacity(); | 1286 | instance.message.clearRetainingCapacity(); |
| ... | @@ -1146,6 +1544,9 @@ fn evalZigTest( | ... | @@ -1146,6 +1544,9 @@ fn evalZigTest( |
| 1146 | maker: *Maker, | 1544 | maker: *Maker, |
| 1147 | progress_node: std.Progress.Node, | 1545 | progress_node: std.Progress.Node, |
| 1148 | spawn_options: process.SpawnOptions, | 1546 | spawn_options: process.SpawnOptions, |
| 1547 | protocol_args: []const u8, | ||
| 1548 | input_dirs: []const Cache.Path, | ||
| 1549 | man: ?*Cache.Manifest, | ||
| 1149 | fuzz_context: ?FuzzContext, | 1550 | fuzz_context: ?FuzzContext, |
| 1150 | ) !void { | 1551 | ) !void { |
| 1151 | if (fuzz_context != null) { | 1552 | if (fuzz_context != null) { |
| ... | @@ -1194,6 +1595,10 @@ fn evalZigTest( | ... | @@ -1194,6 +1595,10 @@ fn evalZigTest( |
| 1194 | maker, | 1595 | maker, |
| 1195 | &child, | 1596 | &child, |
| 1196 | progress_node, | 1597 | progress_node, |
| 1598 | spawn_options.progress_node, | ||
| 1599 | protocol_args, | ||
| 1600 | input_dirs, | ||
| 1601 | man, | ||
| 1197 | &multi_reader, | 1602 | &multi_reader, |
| 1198 | &test_metadata, | 1603 | &test_metadata, |
| 1199 | &test_results, | 1604 | &test_results, |
| ... | @@ -1323,7 +1728,6 @@ const TestMetadata = struct { | ... | @@ -1323,7 +1728,6 @@ const TestMetadata = struct { |
| 1323 | expected_panic_msgs: []const u32, | 1728 | expected_panic_msgs: []const u32, |
| 1324 | string_bytes: []const u8, | 1729 | string_bytes: []const u8, |
| 1325 | next_index: u32, | 1730 | next_index: u32, |
| 1326 | prog_node: std.Progress.Node, | ||
| 1327 | 1731 | ||
| 1328 | fn toCachedTestMetadata(tm: TestMetadata) CachedTestMetadata { | 1732 | fn toCachedTestMetadata(tm: TestMetadata) CachedTestMetadata { |
| 1329 | return .{ | 1733 | return .{ |
| ... | @@ -1346,16 +1750,14 @@ pub const CachedTestMetadata = struct { | ... | @@ -1346,16 +1750,14 @@ pub const CachedTestMetadata = struct { |
| 1346 | } | 1750 | } |
| 1347 | }; | 1751 | }; |
| 1348 | 1752 | ||
| 1349 | fn requestNextTest(client: *std.zig.Client, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { | 1753 | fn requestNextTest(client: *std.zig.Client, metadata: *TestMetadata, test_prog_node: std.Progress.Node) !void { |
| 1350 | while (metadata.next_index < metadata.names.len) { | 1754 | while (metadata.next_index < metadata.names.len) { |
| 1351 | const i = metadata.next_index; | 1755 | const i = metadata.next_index; |
| 1352 | metadata.next_index += 1; | 1756 | metadata.next_index += 1; |
| 1353 | 1757 | ||
| 1354 | if (metadata.expected_panic_msgs[i] != 0) continue; | 1758 | if (metadata.expected_panic_msgs[i] != 0) continue; |
| 1355 | 1759 | ||
| 1356 | const name = metadata.testName(i); | 1760 | test_prog_node.setName(metadata.testName(i)); |
| 1357 | if (sub_prog_node.*) |n| n.end(); | ||
| 1358 | sub_prog_node.* = metadata.prog_node.start(name, 0); | ||
| 1359 | 1761 | ||
| 1360 | try client.serveRunTest(i); | 1762 | try client.serveRunTest(i); |
| 1361 | return; | 1763 | return; |
| ... | @@ -1402,7 +1804,7 @@ fn evalGeneric( | ... | @@ -1402,7 +1804,7 @@ fn evalGeneric( |
| 1402 | var write_buffer: [1024]u8 = undefined; | 1804 | var write_buffer: [1024]u8 = undefined; |
| 1403 | var stdin_writer = child.stdin.?.writerStreaming(io, &write_buffer); | 1805 | var stdin_writer = child.stdin.?.writerStreaming(io, &write_buffer); |
| 1404 | _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) { | 1806 | _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) { |
| 1405 | error.ReadFailed => return step.fail(maker, "failed to read from {f}: {t}", .{ | 1807 | error.ReadFailed => return step.fail(maker, "failed to read from {qf}: {t}", .{ |
| 1406 | path, file_reader.err.?, | 1808 | path, file_reader.err.?, |
| 1407 | }), | 1809 | }), |
| 1408 | error.WriteFailed => return step.fail(maker, "failed to write to stdin: {t}", .{ | 1810 | error.WriteFailed => return step.fail(maker, "failed to write to stdin: {t}", .{ |
| ... | @@ -1490,6 +1892,7 @@ fn evalGeneric( | ... | @@ -1490,6 +1892,7 @@ fn evalGeneric( |
| 1490 | 1892 | ||
| 1491 | const IndexedOutput = struct { | 1893 | const IndexedOutput = struct { |
| 1492 | index: u32, | 1894 | index: u32, |
| 1895 | offset: u32, | ||
| 1493 | arg_index: Configuration.Step.Run.Arg.Index, | 1896 | arg_index: Configuration.Step.Run.Arg.Index, |
| 1494 | }; | 1897 | }; |
| 1495 | 1898 | ||
| ... | @@ -1516,13 +1919,16 @@ pub fn rerunInFuzzMode( | ... | @@ -1516,13 +1919,16 @@ pub fn rerunInFuzzMode( |
| 1516 | var argv_list: std.ArrayList([]const u8) = .empty; | 1919 | var argv_list: std.ArrayList([]const u8) = .empty; |
| 1517 | defer argv_list.deinit(gpa); | 1920 | defer argv_list.deinit(gpa); |
| 1518 | 1921 | ||
| 1519 | for (conf_run.args.slice) |arg_index| { | 1922 | for (switch (conf_run.flags.stdio) { |
| 1923 | .infer_from_args, .inherit, .check, .zig_test => conf_run.args.slice, | ||
| 1924 | .protocol => @panic("protocol fuzz unimplemented"), | ||
| 1925 | }) |arg_index| { | ||
| 1520 | const arg = arg_index.get(conf); | 1926 | const arg = arg_index.get(conf); |
| 1521 | try argv_list.ensureUnusedCapacity(gpa, 1); | 1927 | try argv_list.ensureUnusedCapacity(gpa, 1); |
| 1522 | switch (arg.flags.tag) { | 1928 | switch (arg.flags.tag) { |
| 1523 | .string => { | 1929 | .string => { |
| 1524 | const prefix = arg.prefix.value.?.slice(conf); | 1930 | const string = arg.prefix.value.?.slice(conf); |
| 1525 | argv_list.appendAssumeCapacity(prefix); | 1931 | argv_list.appendAssumeCapacity(string); |
| 1526 | }, | 1932 | }, |
| 1527 | .path_file => { | 1933 | .path_file => { |
| 1528 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; | 1934 | const prefix = if (arg.prefix.value) |p| p.slice(conf) else ""; |
| ... | @@ -1550,14 +1956,14 @@ pub fn rerunInFuzzMode( | ... | @@ -1550,14 +1956,14 @@ pub fn rerunInFuzzMode( |
| 1550 | result.writer.writeAll(prefix) catch return error.OutOfMemory; | 1956 | result.writer.writeAll(prefix) catch return error.OutOfMemory; |
| 1551 | 1957 | ||
| 1552 | const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err| | 1958 | const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err| |
| 1553 | return step.fail(maker, "unable to open input file {f}: {t}", .{ file_path, err }); | 1959 | return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err }); |
| 1554 | defer file.close(io); | 1960 | defer file.close(io); |
| 1555 | 1961 | ||
| 1556 | var file_reader = file.reader(io, &.{}); | 1962 | var file_reader = file.reader(io, &.{}); |
| 1557 | _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) { | 1963 | _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) { |
| 1558 | error.ReadFailed => switch (file_reader.err.?) { | 1964 | error.ReadFailed => switch (file_reader.err.?) { |
| 1559 | error.Canceled => |e| return e, | 1965 | error.Canceled => |e| return e, |
| 1560 | else => |e| return step.fail(maker, "failed to read from {f}: {t}", .{ file_path, e }), | 1966 | else => |e| return step.fail(maker, "failed to read from {qf}: {t}", .{ file_path, e }), |
| 1561 | }, | 1967 | }, |
| 1562 | error.WriteFailed => return error.OutOfMemory, | 1968 | error.WriteFailed => return error.OutOfMemory, |
| 1563 | }; | 1969 | }; |
| ... | @@ -1591,14 +1997,17 @@ pub fn rerunInFuzzMode( | ... | @@ -1591,14 +1997,17 @@ pub fn rerunInFuzzMode( |
| 1591 | .enable_wine => thirdPartyToggle(null, &argv_list, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value), | 1997 | .enable_wine => thirdPartyToggle(null, &argv_list, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value), |
| 1592 | } | 1998 | } |
| 1593 | } | 1999 | } |
| 1594 | 2000 | switch (conf_run.flags.stdio) { | |
| 1595 | if (conf_run.flags.test_runner_mode) { | 2001 | .infer_from_args, .inherit, .check => {}, |
| 1596 | const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false); | 2002 | .zig_test => { |
| 1597 | 2003 | const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false); | |
| 1598 | try argv_list.ensureUnusedCapacity(gpa, 3); | 2004 | |
| 1599 | argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string})); | 2005 | try argv_list.ensureUnusedCapacity(gpa, 3); |
| 1600 | argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed})); | 2006 | argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string})); |
| 1601 | argv_list.appendAssumeCapacity("--listen=-"); | 2007 | argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed})); |
| 2008 | argv_list.appendAssumeCapacity("--listen=-"); | ||
| 2009 | }, | ||
| 2010 | .protocol => unreachable, | ||
| 1602 | } | 2011 | } |
| 1603 | 2012 | ||
| 1604 | step.clearFailedCommand(gpa); | 2013 | step.clearFailedCommand(gpa); |
| ... | @@ -1607,7 +2016,7 @@ pub fn rerunInFuzzMode( | ... | @@ -1607,7 +2016,7 @@ pub fn rerunInFuzzMode( |
| 1607 | var rand_int: u64 = undefined; | 2016 | var rand_int: u64 = undefined; |
| 1608 | io.random(@ptrCast(&rand_int)); | 2017 | io.random(@ptrCast(&rand_int)); |
| 1609 | const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); | 2018 | const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); |
| 1610 | try runCommand(arena, run, run_index, maker, prog_node, argv_list.items, has_side_effects, tmp_dir_path, .{ | 2019 | try runCommand(arena, run, run_index, maker, prog_node, argv_list.items, &.{}, &.{}, &.{}, &.{}, null, has_side_effects, tmp_dir_path, .{ |
| 1611 | .fuzz = fuzz, | 2020 | .fuzz = fuzz, |
| 1612 | }); | 2021 | }); |
| 1613 | } | 2022 | } |
| ... | @@ -1633,7 +2042,16 @@ fn populateGeneratedPathsCreateDirs( | ... | @@ -1633,7 +2042,16 @@ fn populateGeneratedPathsCreateDirs( |
| 1633 | maker: *Maker, | 2042 | maker: *Maker, |
| 1634 | output_dir_path: []const u8, | 2043 | output_dir_path: []const u8, |
| 1635 | output_placeholders: []const IndexedOutput, | 2044 | output_placeholders: []const IndexedOutput, |
| 1636 | argv: [][]const u8, | 2045 | populate: union(enum) { |
| 2046 | argv: [][]const u8, | ||
| 2047 | protocol: struct { | ||
| 2048 | owned_dirs: *std.bit_set.Dynamic, | ||
| 2049 | inherit_dirs: []Io.Dir, | ||
| 2050 | owned_files: *std.bit_set.Dynamic, | ||
| 2051 | inherit_files: []Io.File, | ||
| 2052 | args: []u8, | ||
| 2053 | }, | ||
| 2054 | }, | ||
| 1637 | ) !void { | 2055 | ) !void { |
| 1638 | const step = maker.stepByIndex(run_index); | 2056 | const step = maker.stepByIndex(run_index); |
| 1639 | const conf = &maker.scanned_config.configuration; | 2057 | const conf = &maker.scanned_config.configuration; |
| ... | @@ -1653,16 +2071,52 @@ fn populateGeneratedPathsCreateDirs( | ... | @@ -1653,16 +2071,52 @@ fn populateGeneratedPathsCreateDirs( |
| 1653 | const create_path: Path = .{ | 2071 | const create_path: Path = .{ |
| 1654 | .root_dir = cache_root, | 2072 | .root_dir = cache_root, |
| 1655 | .sub_path = switch (arg.flags.tag) { | 2073 | .sub_path = switch (arg.flags.tag) { |
| 2074 | else => unreachable, | ||
| 1656 | .output_file => Dir.path.dirname(generated_path.sub_path).?, | 2075 | .output_file => Dir.path.dirname(generated_path.sub_path).?, |
| 1657 | .output_directory => generated_path.sub_path, | 2076 | .output_directory => generated_path.sub_path, |
| 1658 | else => unreachable, | ||
| 1659 | }, | 2077 | }, |
| 1660 | }; | 2078 | }; |
| 1661 | create_path.root_dir.handle.createDirPath(io, create_path.sub_path) catch |err| | 2079 | create_path.root_dir.handle.createDirPath(io, create_path.sub_path) catch |err| |
| 1662 | return step.fail(maker, "unable to make path {f}: {t}", .{ create_path, err }); | 2080 | return step.fail(maker, "unable to make path {qf}: {t}", .{ create_path, err }); |
| 1663 | 2081 | ||
| 1664 | const arg_output_path = try convertPathArg(arena, run_index, maker, generated_path, arg.flags.make_absolute); | 2082 | switch (populate) { |
| 1665 | argv[placeholder.index] = try mem.concat(arena, u8, &.{ prefix, arg_output_path, suffix }); | 2083 | .argv => |argv| { |
| 2084 | const arg_output_path = try convertPathArg(arena, run_index, maker, generated_path, arg.flags.make_absolute); | ||
| 2085 | argv[placeholder.index] = try mem.concat(arena, u8, &.{ prefix, arg_output_path, suffix }); | ||
| 2086 | }, | ||
| 2087 | .protocol => |protocol| switch (arg.flags.tag) { | ||
| 2088 | else => unreachable, | ||
| 2089 | .output_file => { | ||
| 2090 | const file = generated_path.root_dir.handle.createFile( | ||
| 2091 | io, | ||
| 2092 | generated_path.sub_path, | ||
| 2093 | .{ .read = true }, | ||
| 2094 | ) catch |err| return step.fail(maker, "unable to create output file {qf}: {t}", .{ | ||
| 2095 | generated_path, err, | ||
| 2096 | }); | ||
| 2097 | protocol.inherit_files[placeholder.index] = file; | ||
| 2098 | protocol.owned_files.set(placeholder.index); | ||
| 2099 | const file_handle: *align(1) Io.File.Handle = | ||
| 2100 | @ptrCast(protocol.args[placeholder.offset..][0..@sizeOf(Io.File.Handle)]); | ||
| 2101 | file_handle.* = file.handle; | ||
| 2102 | }, | ||
| 2103 | .output_directory => { | ||
| 2104 | assert(generated_path.sub_path.len > 0); | ||
| 2105 | const dir = generated_path.root_dir.handle.openDir( | ||
| 2106 | io, | ||
| 2107 | generated_path.sub_path, | ||
| 2108 | .{ .iterate = true }, | ||
| 2109 | ) catch |err| return step.fail(maker, "unable to open output dir {qf}: {t}", .{ | ||
| 2110 | generated_path, err, | ||
| 2111 | }); | ||
| 2112 | protocol.inherit_dirs[placeholder.index] = dir; | ||
| 2113 | protocol.owned_dirs.set(placeholder.index); | ||
| 2114 | const dir_handle: *align(1) Io.Dir.Handle = | ||
| 2115 | @ptrCast(protocol.args[placeholder.offset..][0..@sizeOf(Io.Dir.Handle)]); | ||
| 2116 | dir_handle.* = dir.handle; | ||
| 2117 | }, | ||
| 2118 | }, | ||
| 2119 | } | ||
| 1666 | } | 2120 | } |
| 1667 | } | 2121 | } |
| 1668 | 2122 | ||
| ... | @@ -1711,6 +2165,11 @@ fn runCommand( | ... | @@ -1711,6 +2165,11 @@ fn runCommand( |
| 1711 | maker: *Maker, | 2165 | maker: *Maker, |
| 1712 | progress_node: std.Progress.Node, | 2166 | progress_node: std.Progress.Node, |
| 1713 | argv: []const []const u8, | 2167 | argv: []const []const u8, |
| 2168 | inherit_dirs: []const Io.Dir, | ||
| 2169 | inherit_files: []const Io.File, | ||
| 2170 | protocol_args: []const u8, | ||
| 2171 | input_dirs: []const Cache.Path, | ||
| 2172 | man: ?*Cache.Manifest, | ||
| 1714 | has_side_effects: bool, | 2173 | has_side_effects: bool, |
| 1715 | output_dir_path: []const u8, | 2174 | output_dir_path: []const u8, |
| 1716 | fuzz_context: ?FuzzContext, | 2175 | fuzz_context: ?FuzzContext, |
| ... | @@ -1777,10 +2236,16 @@ fn runCommand( | ... | @@ -1777,10 +2236,16 @@ fn runCommand( |
| 1777 | progress_node, | 2236 | progress_node, |
| 1778 | argv, | 2237 | argv, |
| 1779 | &environ_map, | 2238 | &environ_map, |
| 2239 | inherit_dirs, | ||
| 2240 | inherit_files, | ||
| 2241 | protocol_args, | ||
| 2242 | input_dirs, | ||
| 2243 | man, | ||
| 1780 | has_side_effects, | 2244 | has_side_effects, |
| 1781 | fuzz_context, | 2245 | fuzz_context, |
| 1782 | ) catch |err| term: { | 2246 | ) catch |err| term: { |
| 1783 | switch (err) { | 2247 | switch (err) { |
| 2248 | else => {}, | ||
| 1784 | error.InvalidExe, // cpu arch mismatch | 2249 | error.InvalidExe, // cpu arch mismatch |
| 1785 | error.FileNotFound, // can happen with a wrong dynamic linker path | 2250 | error.FileNotFound, // can happen with a wrong dynamic linker path |
| 1786 | => interpret: { | 2251 | => interpret: { |
| ... | @@ -1794,24 +2259,21 @@ fn runCommand( | ... | @@ -1794,24 +2259,21 @@ fn runCommand( |
| 1794 | const root_module = producer.root_module.get(conf); | 2259 | const root_module = producer.root_module.get(conf); |
| 1795 | const root_module_target = root_module.resolved_target.get(conf).?.result.get(conf); | 2260 | const root_module_target = root_module.resolved_target.get(conf).?.result.get(conf); |
| 1796 | const root_target = root_module_target.unwrapTarget(conf); | 2261 | const root_target = root_module_target.unwrapTarget(conf); |
| 1797 | const link_libc = maker.stepByIndex(producer_index).extended.compile.is_linking_libc; | 2262 | const config = maker.stepByIndex(producer_index).extended.compile.config.?; |
| 1798 | 2263 | ||
| 1799 | const host: std.Target = std.zig.system.resolveTargetQuery(io, .{}) catch |he| switch (he) { | 2264 | const host: std.Target = std.zig.system.resolveTargetQuery(io, .{}) catch |he| switch (he) { |
| 1800 | error.Canceled => |e| return e, | 2265 | error.Canceled => |e| return e, |
| 1801 | else => builtin.target, | 2266 | else => builtin.target, |
| 1802 | }; | 2267 | }; |
| 1803 | 2268 | ||
| 1804 | const need_cross_libc = link_libc and root_target.os.tag == .linux and | 2269 | const need_cross_libc = root_target.os.tag == .linux and |
| 1805 | switch (producer.flags2.linkage) { | 2270 | config.flags.link_libc and config.flags.link_mode == .dynamic; |
| 1806 | .static => false, | ||
| 1807 | .dynamic => true, | ||
| 1808 | .default => root_target.isGnuLibC(), | ||
| 1809 | }; | ||
| 1810 | switch (std.zig.system.getExternalExecutor(io, &root_target, .{ | 2271 | switch (std.zig.system.getExternalExecutor(io, &root_target, .{ |
| 1811 | .host_cpu_arch = host.cpu.arch, | 2272 | .host_cpu_arch = host.cpu.arch, |
| 1812 | .host_os_tag = host.os.tag, | 2273 | .host_os_tag = host.os.tag, |
| 1813 | .qemu_fixes_dl = need_cross_libc and graph.libc_runtimes_dir != null, | 2274 | .qemu_fixes_dl = need_cross_libc and graph.libc_runtimes_dir != null, |
| 1814 | .link_libc = link_libc, | 2275 | .link_mode = config.flags.link_mode, |
| 2276 | .link_libc = config.flags.link_libc, | ||
| 1815 | })) { | 2277 | })) { |
| 1816 | .native, .rosetta => { | 2278 | .native, .rosetta => { |
| 1817 | if (allow_skip) return error.MakeSkipped; | 2279 | if (allow_skip) return error.MakeSkipped; |
| ... | @@ -1874,7 +2336,7 @@ fn runCommand( | ... | @@ -1874,7 +2336,7 @@ fn runCommand( |
| 1874 | interp_argv.appendAssumeCapacity(bin_name); | 2336 | interp_argv.appendAssumeCapacity(bin_name); |
| 1875 | interp_argv.appendAssumeCapacity("--dir=."); | 2337 | interp_argv.appendAssumeCapacity("--dir=."); |
| 1876 | for (conf_run.preopens.slice) |preopen| { | 2338 | for (conf_run.preopens.slice) |preopen| { |
| 1877 | const path = try maker.resolveLazyPath(arena, preopen.path.get(conf), run_index); | 2339 | const path = try maker.resolveLazyPathIndex(arena, preopen.path, run_index); |
| 1878 | path.root_dir.handle.createDirPath(io, path.subPathOrDot()) catch |e| | 2340 | path.root_dir.handle.createDirPath(io, path.subPathOrDot()) catch |e| |
| 1879 | return step.fail(maker, "failed creating directory {f}: {t}", .{ path, e }); | 2341 | return step.fail(maker, "failed creating directory {f}: {t}", .{ path, e }); |
| 1880 | interp_argv.appendAssumeCapacity(try arena.print("--dir={f}::{s}", .{ path, preopen.name.slice(conf) })); | 2342 | interp_argv.appendAssumeCapacity(try arena.print("--dir={f}::{s}", .{ path, preopen.name.slice(conf) })); |
| ... | @@ -1928,6 +2390,11 @@ fn runCommand( | ... | @@ -1928,6 +2390,11 @@ fn runCommand( |
| 1928 | progress_node, | 2390 | progress_node, |
| 1929 | interp_argv.items, | 2391 | interp_argv.items, |
| 1930 | &environ_map, | 2392 | &environ_map, |
| 2393 | inherit_dirs, | ||
| 2394 | inherit_files, | ||
| 2395 | protocol_args, | ||
| 2396 | input_dirs, | ||
| 2397 | man, | ||
| 1931 | has_side_effects, | 2398 | has_side_effects, |
| 1932 | fuzz_context, | 2399 | fuzz_context, |
| 1933 | ) catch |e| { | 2400 | ) catch |e| { |
| ... | @@ -1937,21 +2404,21 @@ fn runCommand( | ... | @@ -1937,21 +2404,21 @@ fn runCommand( |
| 1937 | }; | 2404 | }; |
| 1938 | }, | 2405 | }, |
| 1939 | error.MakeFailed, error.OutOfMemory, error.Canceled => |e| return e, | 2406 | error.MakeFailed, error.OutOfMemory, error.Canceled => |e| return e, |
| 1940 | else => {}, | ||
| 1941 | } | 2407 | } |
| 1942 | return step.fail(maker, "failed to spawn and capture stdio from {s}: {t}", .{ argv[0], err }); | 2408 | return step.fail(maker, "failed to spawn and capture stdio from {s}: {t}", .{ argv[0], err }); |
| 1943 | }; | 2409 | }; |
| 1944 | 2410 | ||
| 1945 | const generic_result = opt_generic_result orelse { | 2411 | const generic_result = switch (conf_run.flags.stdio) { |
| 1946 | assert(conf_run.flags.stdio == .zig_test); | 2412 | .infer_from_args, .inherit, .check => opt_generic_result.?, |
| 1947 | // Specific errors have already been reported, and test results are populated. All we need | 2413 | .zig_test, .protocol => { |
| 1948 | // to do is report step failure if any test failed. | 2414 | assert(opt_generic_result == null); |
| 1949 | if (!step.test_results.isSuccess()) return error.MakeFailed; | 2415 | // Specific errors have already been reported, and test results are populated. All we need |
| 1950 | return; | 2416 | // to do is report step failure if any test failed. |
| 2417 | if (!step.test_results.isSuccess()) return error.MakeFailed; | ||
| 2418 | return; | ||
| 2419 | }, | ||
| 1951 | }; | 2420 | }; |
| 1952 | |||
| 1953 | assert(fuzz_context == null); | 2421 | assert(fuzz_context == null); |
| 1954 | assert(conf_run.flags.stdio != .zig_test); | ||
| 1955 | 2422 | ||
| 1956 | // Capture stdout and stderr to GeneratedFile objects. | 2423 | // Capture stdout and stderr to GeneratedFile objects. |
| 1957 | const Stream = struct { | 2424 | const Stream = struct { |
| ... | @@ -1978,7 +2445,7 @@ fn runCommand( | ... | @@ -1978,7 +2445,7 @@ fn runCommand( |
| 1978 | 2445 | ||
| 1979 | const sub_path_parent = output_path.dirname().?; | 2446 | const sub_path_parent = output_path.dirname().?; |
| 1980 | sub_path_parent.root_dir.handle.createDirPath(io, sub_path_parent.sub_path) catch |err| | 2447 | sub_path_parent.root_dir.handle.createDirPath(io, sub_path_parent.sub_path) catch |err| |
| 1981 | return step.fail(maker, "unable to make path {f}: {t}", .{ sub_path_parent, err }); | 2448 | return step.fail(maker, "unable to make path {qf}: {t}", .{ sub_path_parent, err }); |
| 1982 | 2449 | ||
| 1983 | const data = switch (stream.trim_whitespace) { | 2450 | const data = switch (stream.trim_whitespace) { |
| 1984 | .none => stream.bytes.?, | 2451 | .none => stream.bytes.?, |
| ... | @@ -1989,12 +2456,12 @@ fn runCommand( | ... | @@ -1989,12 +2456,12 @@ fn runCommand( |
| 1989 | output_path.root_dir.handle.writeFile(io, .{ | 2456 | output_path.root_dir.handle.writeFile(io, .{ |
| 1990 | .sub_path = output_path.sub_path, | 2457 | .sub_path = output_path.sub_path, |
| 1991 | .data = data, | 2458 | .data = data, |
| 1992 | }) catch |err| return step.fail(maker, "unable to write file {f}: {t}", .{ output_path, err }); | 2459 | }) catch |err| return step.fail(maker, "unable to write file {qf}: {t}", .{ output_path, err }); |
| 1993 | } | 2460 | } |
| 1994 | } | 2461 | } |
| 1995 | 2462 | ||
| 1996 | switch (conf_run.flags.stdio) { | 2463 | switch (conf_run.flags.stdio) { |
| 1997 | .zig_test => unreachable, | 2464 | .zig_test, .protocol => unreachable, |
| 1998 | .check => { | 2465 | .check => { |
| 1999 | if (conf_run.expect_stderr_exact.value) |bytes| { | 2466 | if (conf_run.expect_stderr_exact.value) |bytes| { |
| 2000 | const expected_bytes = bytes.slice(conf); | 2467 | const expected_bytes = bytes.slice(conf); |
| ... | @@ -2186,6 +2653,11 @@ fn spawnChildAndCollect( | ... | @@ -2186,6 +2653,11 @@ fn spawnChildAndCollect( |
| 2186 | progress_node: std.Progress.Node, | 2653 | progress_node: std.Progress.Node, |
| 2187 | argv: []const []const u8, | 2654 | argv: []const []const u8, |
| 2188 | environ_map: *EnvMap, | 2655 | environ_map: *EnvMap, |
| 2656 | inherit_dirs: []const Io.Dir, | ||
| 2657 | inherit_files: []const Io.File, | ||
| 2658 | protocol_args: []const u8, | ||
| 2659 | input_dirs: []const Cache.Path, | ||
| 2660 | man: ?*Cache.Manifest, | ||
| 2189 | has_side_effects: bool, | 2661 | has_side_effects: bool, |
| 2190 | fuzz_context: ?FuzzContext, | 2662 | fuzz_context: ?FuzzContext, |
| 2191 | ) !?EvalGenericResult { | 2663 | ) !?EvalGenericResult { |
| ... | @@ -2199,7 +2671,10 @@ fn spawnChildAndCollect( | ... | @@ -2199,7 +2671,10 @@ fn spawnChildAndCollect( |
| 2199 | 2671 | ||
| 2200 | if (fuzz_context != null) { | 2672 | if (fuzz_context != null) { |
| 2201 | assert(!has_side_effects); | 2673 | assert(!has_side_effects); |
| 2202 | assert(conf_run.flags.stdio == .zig_test); | 2674 | switch (conf_run.flags.stdio) { |
| 2675 | .infer_from_args, .inherit, .check => unreachable, | ||
| 2676 | .zig_test, .protocol => {}, | ||
| 2677 | } | ||
| 2203 | } | 2678 | } |
| 2204 | 2679 | ||
| 2205 | const child_cwd: process.Child.Cwd = if (conf_run.cwd.value) |lazy_cwd| | 2680 | const child_cwd: process.Child.Cwd = if (conf_run.cwd.value) |lazy_cwd| |
| ... | @@ -2230,68 +2705,88 @@ fn spawnChildAndCollect( | ... | @@ -2230,68 +2705,88 @@ fn spawnChildAndCollect( |
| 2230 | .cwd = child_cwd, | 2705 | .cwd = child_cwd, |
| 2231 | .environ_map = environ_map, | 2706 | .environ_map = environ_map, |
| 2232 | .request_resource_usage_statistics = true, | 2707 | .request_resource_usage_statistics = true, |
| 2233 | .stdin = if (conf_run.stdin.u != .none) s: { | 2708 | .stdin = if (conf_run.stdin.u != .none) stdin: { |
| 2234 | assert(conf_run.flags.stdio != .inherit); | 2709 | assert(conf_run.flags.stdio != .inherit); |
| 2235 | break :s .pipe; | 2710 | break :stdin .pipe; |
| 2236 | } else switch (conf_run.flags.stdio) { | 2711 | } else switch (conf_run.flags.stdio) { |
| 2237 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .ignore, | 2712 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .ignore, |
| 2238 | .inherit => .inherit, | 2713 | .inherit => .inherit, |
| 2239 | .check => .ignore, | 2714 | .check => .ignore, |
| 2240 | .zig_test => .pipe, | 2715 | .zig_test, .protocol => .pipe, |
| 2241 | }, | 2716 | }, |
| 2242 | .stdout = if (conf_run.captured_stdout.value != null) .pipe else switch (conf_run.flags.stdio) { | 2717 | .stdout = if (conf_run.captured_stdout.value != null) .pipe else switch (conf_run.flags.stdio) { |
| 2243 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .ignore, | 2718 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .ignore, |
| 2244 | .inherit => .inherit, | 2719 | .inherit => .inherit, |
| 2245 | .check => if (checksContainStdout(&conf_run)) .pipe else .ignore, | 2720 | .check => if (checksContainStdout(&conf_run)) .pipe else .ignore, |
| 2246 | .zig_test => .pipe, | 2721 | .zig_test, .protocol => .pipe, |
| 2247 | }, | 2722 | }, |
| 2248 | .stderr = if (conf_run.captured_stderr.value != null) .pipe else switch (conf_run.flags.stdio) { | 2723 | .stderr = if (conf_run.captured_stderr.value != null) .pipe else switch (conf_run.flags.stdio) { |
| 2249 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .pipe, | 2724 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .pipe, |
| 2250 | .inherit => if (maker.protocol_server == null) .inherit else .pipe, | 2725 | .inherit => if (maker.protocol_server == null) .inherit else .pipe, |
| 2251 | .check => .pipe, | 2726 | .check => .pipe, |
| 2252 | .zig_test => .pipe, | 2727 | .zig_test, .protocol => .pipe, |
| 2253 | }, | 2728 | }, |
| 2729 | .inherit_dirs = inherit_dirs, | ||
| 2730 | .inherit_files = inherit_files, | ||
| 2254 | }; | 2731 | }; |
| 2255 | 2732 | ||
| 2256 | if (maker.protocol_server != null) { | 2733 | if (maker.protocol_server != null) { |
| 2257 | if (spawn_options.stdin == .inherit) { | 2734 | if (spawn_options.stdin == .inherit) { |
| 2258 | return step.fail(maker, "Cannot inherit stdin when running through over the build system protocol", .{}); | 2735 | return step.fail(maker, "Cannot inherit stdin when running over the build system protocol", .{}); |
| 2259 | } | 2736 | } |
| 2260 | if (spawn_options.stdout == .inherit) { | 2737 | if (spawn_options.stdout == .inherit) { |
| 2261 | return step.fail(maker, "Cannot inherit stdout when running through over the build system protocol", .{}); | 2738 | return step.fail(maker, "Cannot inherit stdout when running over the build system protocol", .{}); |
| 2262 | } | 2739 | } |
| 2263 | assert(spawn_options.stderr != .inherit); | 2740 | assert(spawn_options.stderr != .inherit); |
| 2264 | } | 2741 | } |
| 2265 | 2742 | ||
| 2266 | if (conf_run.flags.stdio == .zig_test) { | 2743 | switch (conf_run.flags.stdio) { |
| 2267 | try setColorEnvironmentVariables(&conf_run, environ_map, graph.stderr_mode.?); | 2744 | .infer_from_args, .inherit, .check => { |
| 2268 | const started: Io.Clock.Timestamp = .now(io, .awake); | 2745 | const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit; |
| 2269 | const result = evalZigTest(run, run_index, maker, progress_node, spawn_options, fuzz_context) catch |err| switch (err) { | 2746 | if (!conf_run.flags.disable_zig_progress and !inherit) { |
| 2270 | error.Canceled => |e| return e, | 2747 | spawn_options.progress_node = progress_node; |
| 2271 | else => |e| e, | 2748 | } |
| 2272 | }; | 2749 | const terminal_mode: Io.Terminal.Mode = if (inherit) m: { |
| 2273 | step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds); | 2750 | const stderr = try io.lockStderr(&.{}, graph.stderr_mode); |
| 2274 | try result; | 2751 | break :m stderr.terminal_mode; |
| 2275 | return null; | 2752 | } else .no_color; |
| 2276 | } else { | 2753 | defer if (inherit) io.unlockStderr(); |
| 2277 | const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit; | 2754 | try setColorEnvironmentVariables(&conf_run, environ_map, terminal_mode); |
| 2278 | if (!conf_run.flags.disable_zig_progress and !inherit) { | 2755 | |
| 2279 | spawn_options.progress_node = progress_node; | 2756 | const started: Io.Clock.Timestamp = .now(io, .awake); |
| 2280 | } | 2757 | const result = evalGeneric(arena, run_index, maker, spawn_options) catch |err| switch (err) { |
| 2281 | const terminal_mode: Io.Terminal.Mode = if (inherit) m: { | 2758 | error.Canceled => |e| return e, |
| 2282 | const stderr = try io.lockStderr(&.{}, graph.stderr_mode); | 2759 | else => |e| e, |
| 2283 | break :m stderr.terminal_mode; | 2760 | }; |
| 2284 | } else .no_color; | 2761 | step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds); |
| 2285 | defer if (inherit) io.unlockStderr(); | 2762 | return try result; |
| 2286 | try setColorEnvironmentVariables(&conf_run, environ_map, terminal_mode); | 2763 | }, |
| 2287 | 2764 | .zig_test, .protocol => |stdio| { | |
| 2288 | const started: Io.Clock.Timestamp = .now(io, .awake); | 2765 | spawn_options.progress_node = if (conf_run.flags.disable_zig_progress or fuzz_context != null) |
| 2289 | const result = evalGeneric(arena, run_index, maker, spawn_options) catch |err| switch (err) { | 2766 | .none |
| 2290 | error.Canceled => |e| return e, | 2767 | else |
| 2291 | else => |e| e, | 2768 | progress_node.start(@tagName(stdio), 0); |
| 2292 | }; | 2769 | defer spawn_options.progress_node.end(); |
| 2293 | step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds); | 2770 | try setColorEnvironmentVariables(&conf_run, environ_map, graph.stderr_mode.?); |
| 2294 | return try result; | 2771 | const started: Io.Clock.Timestamp = .now(io, .awake); |
| 2772 | const result = evalZigTest( | ||
| 2773 | run, | ||
| 2774 | run_index, | ||
| 2775 | maker, | ||
| 2776 | progress_node, | ||
| 2777 | spawn_options, | ||
| 2778 | protocol_args, | ||
| 2779 | input_dirs, | ||
| 2780 | man, | ||
| 2781 | fuzz_context, | ||
| 2782 | ) catch |err| switch (err) { | ||
| 2783 | error.Canceled => |e| return e, | ||
| 2784 | else => |e| e, | ||
| 2785 | }; | ||
| 2786 | step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds); | ||
| 2787 | try result; | ||
| 2788 | return null; | ||
| 2789 | }, | ||
| 2295 | } | 2790 | } |
| 2296 | } | 2791 | } |
| 2297 | 2792 | ||
| ... | @@ -2341,7 +2836,7 @@ fn setColorEnvironmentVariables( | ... | @@ -2341,7 +2836,7 @@ fn setColorEnvironmentVariables( |
| 2341 | .auto => { | 2836 | .auto => { |
| 2342 | const capture_stderr = conf_run.captured_stderr.value != null or switch (conf_run.flags.stdio) { | 2837 | const capture_stderr = conf_run.captured_stderr.value != null or switch (conf_run.flags.stdio) { |
| 2343 | .check => checksContainStderr(conf_run), | 2838 | .check => checksContainStderr(conf_run), |
| 2344 | .infer_from_args, .inherit, .zig_test => false, | 2839 | .infer_from_args, .inherit, .zig_test, .protocol => false, |
| 2345 | }; | 2840 | }; |
| 2346 | if (capture_stderr) { | 2841 | if (capture_stderr) { |
| 2347 | continue :color .disable; | 2842 | continue :color .disable; |
lib/compiler/resinator/main.zig+3-6| ... | @@ -790,8 +790,7 @@ fn cliDiagnosticsToErrorBundle( | ... | @@ -790,8 +790,7 @@ fn cliDiagnosticsToErrorBundle( |
| 790 | ) !ErrorBundle { | 790 | ) !ErrorBundle { |
| 791 | @branchHint(.cold); | 791 | @branchHint(.cold); |
| 792 | 792 | ||
| 793 | var bundle: ErrorBundle.Wip = undefined; | 793 | var bundle: ErrorBundle.Wip = try .init(gpa); |
| 794 | try bundle.init(gpa); | ||
| 795 | errdefer bundle.deinit(); | 794 | errdefer bundle.deinit(); |
| 796 | 795 | ||
| 797 | try bundle.addRootErrorMessage(.{ | 796 | try bundle.addRootErrorMessage(.{ |
| ... | @@ -837,8 +836,7 @@ fn diagnosticsToErrorBundle( | ... | @@ -837,8 +836,7 @@ fn diagnosticsToErrorBundle( |
| 837 | ) !ErrorBundle { | 836 | ) !ErrorBundle { |
| 838 | @branchHint(.cold); | 837 | @branchHint(.cold); |
| 839 | 838 | ||
| 840 | var bundle: ErrorBundle.Wip = undefined; | 839 | var bundle: ErrorBundle.Wip = try .init(gpa); |
| 841 | try bundle.init(gpa); | ||
| 842 | errdefer bundle.deinit(); | 840 | errdefer bundle.deinit(); |
| 843 | 841 | ||
| 844 | var msg_buf: std.Io.Writer.Allocating = .init(gpa); | 842 | var msg_buf: std.Io.Writer.Allocating = .init(gpa); |
| ... | @@ -919,8 +917,7 @@ fn diagnosticsToErrorBundle( | ... | @@ -919,8 +917,7 @@ fn diagnosticsToErrorBundle( |
| 919 | 917 | ||
| 920 | fn errorStringToErrorBundle(allocator: Allocator, comptime format: []const u8, args: anytype) !ErrorBundle { | 918 | fn errorStringToErrorBundle(allocator: Allocator, comptime format: []const u8, args: anytype) !ErrorBundle { |
| 921 | @branchHint(.cold); | 919 | @branchHint(.cold); |
| 922 | var bundle: ErrorBundle.Wip = undefined; | 920 | var bundle: ErrorBundle.Wip = try .init(allocator); |
| 923 | try bundle.init(allocator); | ||
| 924 | errdefer bundle.deinit(); | 921 | errdefer bundle.deinit(); |
| 925 | try bundle.addRootErrorMessage(.{ | 922 | try bundle.addRootErrorMessage(.{ |
| 926 | .msg = try bundle.printString(format, args), | 923 | .msg = try bundle.printString(format, args), |
lib/compiler/util.zig+1-2| ... | @@ -10,8 +10,7 @@ pub fn aroDiagnosticsToErrorBundle( | ... | @@ -10,8 +10,7 @@ pub fn aroDiagnosticsToErrorBundle( |
| 10 | ) !ErrorBundle { | 10 | ) !ErrorBundle { |
| 11 | @branchHint(.cold); | 11 | @branchHint(.cold); |
| 12 | 12 | ||
| 13 | var bundle: ErrorBundle.Wip = undefined; | 13 | var bundle: ErrorBundle.Wip = try .init(gpa); |
| 14 | try bundle.init(gpa); | ||
| 15 | errdefer bundle.deinit(); | 14 | errdefer bundle.deinit(); |
| 16 | 15 | ||
| 17 | if (fail_msg) |msg| { | 16 | if (fail_msg) |msg| { |
lib/std/Build.zig+5-5| ... | @@ -768,9 +768,8 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run { | ... | @@ -768,9 +768,8 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run { |
| 768 | 768 | ||
| 769 | const run_step = Step.Run.create(b, step_name); | 769 | const run_step = Step.Run.create(b, step_name); |
| 770 | run_step.producer = exe; | 770 | run_step.producer = exe; |
| 771 | run_step.addArtifactArg(exe); | ||
| 771 | if (exe.kind == .@"test") { | 772 | if (exe.kind == .@"test") { |
| 772 | run_step.addArtifactArg(exe); | ||
| 773 | |||
| 774 | const test_server_mode: bool = s: { | 773 | const test_server_mode: bool = s: { |
| 775 | if (exe.test_runner) |r| break :s r.mode == .server; | 774 | if (exe.test_runner) |r| break :s r.mode == .server; |
| 776 | if (exe.use_llvm == false) { | 775 | if (exe.use_llvm == false) { |
| ... | @@ -802,8 +801,6 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run { | ... | @@ -802,8 +801,6 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run { |
| 802 | // communicate failure via its exit code. | 801 | // communicate failure via its exit code. |
| 803 | run_step.expectExitCode(0); | 802 | run_step.expectExitCode(0); |
| 804 | } | 803 | } |
| 805 | } else { | ||
| 806 | run_step.addArtifactArg(exe); | ||
| 807 | } | 804 | } |
| 808 | 805 | ||
| 809 | return run_step; | 806 | return run_step; |
| ... | @@ -2696,12 +2693,15 @@ fn validateConfigureDependency(lazy_path: LazyPath) void { | ... | @@ -2696,12 +2693,15 @@ fn validateConfigureDependency(lazy_path: LazyPath) void { |
| 2696 | .src_path, .cwd_relative, .dependency => {}, // OK | 2693 | .src_path, .cwd_relative, .dependency => {}, // OK |
| 2697 | .generated => @panic("configure phase cannot depend on files generated during make phase"), | 2694 | .generated => @panic("configure phase cannot depend on files generated during make phase"), |
| 2698 | .relative => |relative| switch (relative.base) { | 2695 | .relative => |relative| switch (relative.base) { |
| 2699 | .cwd, .build_root, .local_cache, .global_cache, .zig_exe, .zig_lib => {}, // OK | 2696 | .cwd, .build_root, .local_cache, .global_cache, .zig_lib => {}, // OK |
| 2697 | .zig_exe => if (relative.sub_path.len > 0) @panic("file base cannot have a sub path"), | ||
| 2700 | .install_prefix, | 2698 | .install_prefix, |
| 2701 | .install_lib, | 2699 | .install_lib, |
| 2702 | .install_bin, | 2700 | .install_bin, |
| 2703 | .install_include, | 2701 | .install_include, |
| 2704 | => @panic("configure phase cannot depend on files installed during make phase"), | 2702 | => @panic("configure phase cannot depend on files installed during make phase"), |
| 2703 | .libc_runtimes, | ||
| 2704 | => @panic("configure phase cannot depend on directory known only during make phase"), | ||
| 2705 | }, | 2705 | }, |
| 2706 | } | 2706 | } |
| 2707 | } | 2707 | } |
lib/std/Build/Configuration.zig+2-2| ... | @@ -665,7 +665,7 @@ pub const Step = extern struct { | ... | @@ -665,7 +665,7 @@ pub const Step = extern struct { |
| 665 | pub const Tag = enum(u2) { none, bytes, lazy_path }; | 665 | pub const Tag = enum(u2) { none, bytes, lazy_path }; |
| 666 | }; | 666 | }; |
| 667 | pub const TrimWhitespace = enum(u2) { none, all, leading, trailing }; | 667 | pub const TrimWhitespace = enum(u2) { none, all, leading, trailing }; |
| 668 | pub const StdIo = enum(u2) { infer_from_args, inherit, check, zig_test }; | 668 | pub const StdIo = enum(u3) { infer_from_args, inherit, check, zig_test, protocol }; |
| 669 | 669 | ||
| 670 | pub const ExpectTermStatus = enum(u2) { exited, signal, stopped, unknown }; | 670 | pub const ExpectTermStatus = enum(u2) { exited, signal, stopped, unknown }; |
| 671 | 671 | ||
| ... | @@ -675,7 +675,6 @@ pub const Step = extern struct { | ... | @@ -675,7 +675,6 @@ pub const Step = extern struct { |
| 675 | skip_foreign_checks: bool, | 675 | skip_foreign_checks: bool, |
| 676 | failing_to_execute_foreign_is_an_error: bool, | 676 | failing_to_execute_foreign_is_an_error: bool, |
| 677 | has_side_effects: bool, | 677 | has_side_effects: bool, |
| 678 | test_runner_mode: bool, | ||
| 679 | color: Color, | 678 | color: Color, |
| 680 | stdin: StdIn.Tag, | 679 | stdin: StdIn.Tag, |
| 681 | stdio: StdIo, | 680 | stdio: StdIo, |
| ... | @@ -1576,6 +1575,7 @@ pub const LazyPath = union(@This().Tag) { | ... | @@ -1576,6 +1575,7 @@ pub const LazyPath = union(@This().Tag) { |
| 1576 | install_lib, | 1575 | install_lib, |
| 1577 | install_bin, | 1576 | install_bin, |
| 1578 | install_include, | 1577 | install_include, |
| 1578 | libc_runtimes, | ||
| 1579 | }; | 1579 | }; |
| 1580 | }; | 1580 | }; |
| 1581 | }; | 1581 | }; |
lib/std/Build/Serialize.zig+1-1| ... | @@ -462,13 +462,13 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi | ... | @@ -462,13 +462,13 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi |
| 462 | .skip_foreign_checks = run.skip_foreign_checks, | 462 | .skip_foreign_checks = run.skip_foreign_checks, |
| 463 | .failing_to_execute_foreign_is_an_error = run.failing_to_execute_foreign_is_an_error, | 463 | .failing_to_execute_foreign_is_an_error = run.failing_to_execute_foreign_is_an_error, |
| 464 | .has_side_effects = run.has_side_effects, | 464 | .has_side_effects = run.has_side_effects, |
| 465 | .test_runner_mode = run.test_runner_mode, | ||
| 466 | .color = run.color, | 465 | .color = run.color, |
| 467 | .stdio = switch (run.stdio) { | 466 | .stdio = switch (run.stdio) { |
| 468 | .infer_from_args => .infer_from_args, | 467 | .infer_from_args => .infer_from_args, |
| 469 | .inherit => .inherit, | 468 | .inherit => .inherit, |
| 470 | .check => .check, | 469 | .check => .check, |
| 471 | .zig_test => .zig_test, | 470 | .zig_test => .zig_test, |
| 471 | .protocol => .protocol, | ||
| 472 | }, | 472 | }, |
| 473 | .stdin = switch (run.stdin) { | 473 | .stdin = switch (run.stdin) { |
| 474 | .none => .none, | 474 | .none => .none, |
lib/std/Build/Step.zig+1-1| ... | @@ -95,7 +95,7 @@ pub fn init(options: StepOptions) Step { | ... | @@ -95,7 +95,7 @@ pub fn init(options: StepOptions) Step { |
| 95 | 95 | ||
| 96 | return .{ | 96 | return .{ |
| 97 | .tag = options.tag, | 97 | .tag = options.tag, |
| 98 | .name = arena.dupe(u8, options.name) catch @panic("OOM"), | 98 | .name = options.owner.graph.dupeString(options.name), |
| 99 | .owner = options.owner, | 99 | .owner = options.owner, |
| 100 | .dependencies = .empty, | 100 | .dependencies = .empty, |
| 101 | .max_rss = options.max_rss, | 101 | .max_rss = options.max_rss, |
lib/std/Build/Step/CheckFile.zig+1-1| ... | @@ -41,5 +41,5 @@ pub fn create(owner: *std.Build, file: std.Build.LazyPath, options: Options) *Ch | ... | @@ -41,5 +41,5 @@ pub fn create(owner: *std.Build, file: std.Build.LazyPath, options: Options) *Ch |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | pub fn setName(check_file: *CheckFile, name: []const u8) void { | 43 | pub fn setName(check_file: *CheckFile, name: []const u8) void { |
| 44 | check_file.step.name = name; | 44 | check_file.step.name = check_file.step.owner.graph.dupeString(name); |
| 45 | } | 45 | } |
lib/std/Build/Step/Run.zig+17-8| ... | @@ -92,7 +92,6 @@ captured_stdout: ?*CapturedStdIo, | ... | @@ -92,7 +92,6 @@ captured_stdout: ?*CapturedStdIo, |
| 92 | captured_stderr: ?*CapturedStdIo, | 92 | captured_stderr: ?*CapturedStdIo, |
| 93 | 93 | ||
| 94 | has_side_effects: bool, | 94 | has_side_effects: bool, |
| 95 | test_runner_mode: bool = false, | ||
| 96 | 95 | ||
| 97 | /// If this Run step was produced by a Compile step, it is tracked here. | 96 | /// If this Run step was produced by a Compile step, it is tracked here. |
| 98 | producer: ?*Step.Compile, | 97 | producer: ?*Step.Compile, |
| ... | @@ -127,7 +126,10 @@ pub const StdIo = union(enum) { | ... | @@ -127,7 +126,10 @@ pub const StdIo = union(enum) { |
| 127 | check: std.ArrayList(Check), | 126 | check: std.ArrayList(Check), |
| 128 | /// This Run step is running a zig unit test binary and will communicate | 127 | /// This Run step is running a zig unit test binary and will communicate |
| 129 | /// extra metadata over the IPC protocol. | 128 | /// extra metadata over the IPC protocol. |
| 129 | /// Deprecated, use `.protocol`. | ||
| 130 | zig_test, | 130 | zig_test, |
| 131 | /// This Run step is running a binary that will communicate over the IPC protocol. | ||
| 132 | protocol, | ||
| 131 | 133 | ||
| 132 | pub const Check = union(enum) { | 134 | pub const Check = union(enum) { |
| 133 | expect_stderr_exact: []const u8, | 135 | expect_stderr_exact: []const u8, |
| ... | @@ -239,14 +241,17 @@ pub fn create(owner: *std.Build, name: []const u8) *Run { | ... | @@ -239,14 +241,17 @@ pub fn create(owner: *std.Build, name: []const u8) *Run { |
| 239 | } | 241 | } |
| 240 | 242 | ||
| 241 | pub fn setName(run: *Run, name: []const u8) void { | 243 | pub fn setName(run: *Run, name: []const u8) void { |
| 242 | run.step.name = name; | 244 | run.step.name = run.step.owner.graph.dupeString(name); |
| 243 | run.rename_step_with_output_arg = false; | 245 | run.rename_step_with_output_arg = false; |
| 244 | } | 246 | } |
| 245 | 247 | ||
| 248 | /// Deprecated, use `enableProtocolMode`. | ||
| 246 | pub fn enableTestRunnerMode(run: *Run) void { | 249 | pub fn enableTestRunnerMode(run: *Run) void { |
| 247 | if (run.test_runner_mode) return; | ||
| 248 | run.stdio = .zig_test; | 250 | run.stdio = .zig_test; |
| 249 | run.test_runner_mode = true; | 251 | } |
| 252 | |||
| 253 | pub fn enableProtocolMode(run: *Run) void { | ||
| 254 | run.stdio = .protocol; | ||
| 250 | } | 255 | } |
| 251 | 256 | ||
| 252 | pub const ArgOptions = struct { | 257 | pub const ArgOptions = struct { |
| ... | @@ -750,8 +755,10 @@ pub fn addCheck(run: *Run, new_check: StdIo.Check) void { | ... | @@ -750,8 +755,10 @@ pub fn addCheck(run: *Run, new_check: StdIo.Check) void { |
| 750 | } | 755 | } |
| 751 | 756 | ||
| 752 | pub fn captureStdErr(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPath { | 757 | pub fn captureStdErr(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPath { |
| 753 | assert(run.stdio != .inherit); | 758 | switch (run.stdio) { |
| 754 | assert(run.stdio != .zig_test); | 759 | .infer_from_args, .check => {}, |
| 760 | .inherit, .zig_test, .protocol => unreachable, | ||
| 761 | } | ||
| 755 | 762 | ||
| 756 | const b = run.step.owner; | 763 | const b = run.step.owner; |
| 757 | const graph = b.graph; | 764 | const graph = b.graph; |
| ... | @@ -771,8 +778,10 @@ pub fn captureStdErr(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPa | ... | @@ -771,8 +778,10 @@ pub fn captureStdErr(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPa |
| 771 | } | 778 | } |
| 772 | 779 | ||
| 773 | pub fn captureStdOut(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPath { | 780 | pub fn captureStdOut(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPath { |
| 774 | assert(run.stdio != .inherit); | 781 | switch (run.stdio) { |
| 775 | assert(run.stdio != .zig_test); | 782 | .infer_from_args, .check => {}, |
| 783 | .inherit, .zig_test, .protocol => unreachable, | ||
| 784 | } | ||
| 776 | 785 | ||
| 777 | const b = run.step.owner; | 786 | const b = run.step.owner; |
| 778 | const graph = b.graph; | 787 | const graph = b.graph; |
lib/std/Io.zig+21-18| ... | @@ -213,13 +213,13 @@ pub const VTable = struct { | ... | @@ -213,13 +213,13 @@ pub const VTable = struct { |
| 213 | processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, | 213 | processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, |
| 214 | processSetCurrentPath: *const fn (?*anyopaque, []const u8) std.process.SetCurrentPathError!void, | 214 | processSetCurrentPath: *const fn (?*anyopaque, []const u8) std.process.SetCurrentPathError!void, |
| 215 | processReplace: *const fn (?*anyopaque, std.process.ReplaceOptions) std.process.ReplaceError, | 215 | processReplace: *const fn (?*anyopaque, std.process.ReplaceOptions) std.process.ReplaceError, |
| 216 | processReplacePath: *const fn (?*anyopaque, Dir, std.process.ReplaceOptions) std.process.ReplaceError, | ||
| 217 | processSpawn: *const fn (?*anyopaque, std.process.SpawnOptions) std.process.SpawnError!std.process.Child, | 216 | processSpawn: *const fn (?*anyopaque, std.process.SpawnOptions) std.process.SpawnError!std.process.Child, |
| 218 | processSpawnPath: *const fn (?*anyopaque, Dir, std.process.SpawnOptions) std.process.SpawnError!std.process.Child, | ||
| 219 | childWait: *const fn (?*anyopaque, *std.process.Child) std.process.Child.WaitError!std.process.Child.Term, | 217 | childWait: *const fn (?*anyopaque, *std.process.Child) std.process.Child.WaitError!std.process.Child.Term, |
| 220 | childKill: *const fn (?*anyopaque, *std.process.Child) void, | 218 | childKill: *const fn (?*anyopaque, *std.process.Child) void, |
| 221 | 219 | ||
| 222 | progressParentFile: *const fn (?*anyopaque) std.Progress.ParentFileError!File, | 220 | progressParentFile: *const fn (?*anyopaque) std.Progress.ParentFileError!File, |
| 221 | inheritParentDir: *const fn (?*anyopaque, handle: Dir.Handle) InheritParentHandleError!Dir, | ||
| 222 | inheritParentFile: *const fn (?*anyopaque, handle: File.Handle, flags: File.Flags) InheritParentHandleError!File, | ||
| 223 | 223 | ||
| 224 | now: *const fn (?*anyopaque, Clock) Timestamp, | 224 | now: *const fn (?*anyopaque, Clock) Timestamp, |
| 225 | clockResolution: *const fn (?*anyopaque, Clock) Clock.ResolutionError!Duration, | 225 | clockResolution: *const fn (?*anyopaque, Clock) Clock.ResolutionError!Duration, |
| ... | @@ -827,6 +827,10 @@ pub const UnexpectedError = error{ | ... | @@ -827,6 +827,10 @@ pub const UnexpectedError = error{ |
| 827 | Unexpected, | 827 | Unexpected, |
| 828 | }; | 828 | }; |
| 829 | 829 | ||
| 830 | pub const InheritParentHandleError = error{ | ||
| 831 | UnsupportedOperation, | ||
| 832 | } || Cancelable || UnexpectedError; | ||
| 833 | |||
| 830 | pub const Clock = enum { | 834 | pub const Clock = enum { |
| 831 | /// A settable system-wide clock that measures real (i.e. wall-clock) | 835 | /// A settable system-wide clock that measures real (i.e. wall-clock) |
| 832 | /// time. This clock is affected by discontinuous jumps in the system | 836 | /// time. This clock is affected by discontinuous jumps in the system |
| ... | @@ -2797,13 +2801,13 @@ pub const failing: std.Io = .{ | ... | @@ -2797,13 +2801,13 @@ pub const failing: std.Io = .{ |
| 2797 | .processSetCurrentDir = failingProcessSetCurrentDir, | 2801 | .processSetCurrentDir = failingProcessSetCurrentDir, |
| 2798 | .processSetCurrentPath = failingProcessSetCurrentPath, | 2802 | .processSetCurrentPath = failingProcessSetCurrentPath, |
| 2799 | .processReplace = failingProcessReplace, | 2803 | .processReplace = failingProcessReplace, |
| 2800 | .processReplacePath = failingProcessReplacePath, | ||
| 2801 | .processSpawn = failingProcessSpawn, | 2804 | .processSpawn = failingProcessSpawn, |
| 2802 | .processSpawnPath = failingProcessSpawnPath, | ||
| 2803 | .childWait = unreachableChildWait, | 2805 | .childWait = unreachableChildWait, |
| 2804 | .childKill = unreachableChildKill, | 2806 | .childKill = unreachableChildKill, |
| 2805 | 2807 | ||
| 2806 | .progressParentFile = failingProgressParentFile, | 2808 | .progressParentFile = failingProgressParentFile, |
| 2809 | .inheritParentDir = failingInheritParentDir, | ||
| 2810 | .inheritParentFile = failingInheritParentFile, | ||
| 2807 | 2811 | ||
| 2808 | .random = noRandom, | 2812 | .random = noRandom, |
| 2809 | .randomSecure = failingRandomSecure, | 2813 | .randomSecure = failingRandomSecure, |
| ... | @@ -3451,26 +3455,12 @@ pub fn failingProcessReplace(userdata: ?*anyopaque, options: std.process.Replace | ... | @@ -3451,26 +3455,12 @@ pub fn failingProcessReplace(userdata: ?*anyopaque, options: std.process.Replace |
| 3451 | return error.OperationUnsupported; | 3455 | return error.OperationUnsupported; |
| 3452 | } | 3456 | } |
| 3453 | 3457 | ||
| 3454 | pub fn failingProcessReplacePath(userdata: ?*anyopaque, dir: Dir, options: std.process.ReplaceOptions) std.process.ReplaceError { | ||
| 3455 | _ = userdata; | ||
| 3456 | _ = dir; | ||
| 3457 | _ = options; | ||
| 3458 | return error.OperationUnsupported; | ||
| 3459 | } | ||
| 3460 | |||
| 3461 | pub fn failingProcessSpawn(userdata: ?*anyopaque, options: std.process.SpawnOptions) std.process.SpawnError!std.process.Child { | 3458 | pub fn failingProcessSpawn(userdata: ?*anyopaque, options: std.process.SpawnOptions) std.process.SpawnError!std.process.Child { |
| 3462 | _ = userdata; | 3459 | _ = userdata; |
| 3463 | _ = options; | 3460 | _ = options; |
| 3464 | return error.OperationUnsupported; | 3461 | return error.OperationUnsupported; |
| 3465 | } | 3462 | } |
| 3466 | 3463 | ||
| 3467 | pub fn failingProcessSpawnPath(userdata: ?*anyopaque, dir: Dir, options: std.process.SpawnOptions) std.process.SpawnError!std.process.Child { | ||
| 3468 | _ = userdata; | ||
| 3469 | _ = dir; | ||
| 3470 | _ = options; | ||
| 3471 | return error.OperationUnsupported; | ||
| 3472 | } | ||
| 3473 | |||
| 3474 | pub fn unreachableChildWait(userdata: ?*anyopaque, child: *std.process.Child) std.process.Child.WaitError!std.process.Child.Term { | 3464 | pub fn unreachableChildWait(userdata: ?*anyopaque, child: *std.process.Child) std.process.Child.WaitError!std.process.Child.Term { |
| 3475 | _ = userdata; | 3465 | _ = userdata; |
| 3476 | _ = child; | 3466 | _ = child; |
| ... | @@ -3488,6 +3478,19 @@ pub fn failingProgressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileE | ... | @@ -3488,6 +3478,19 @@ pub fn failingProgressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileE |
| 3488 | return error.UnsupportedOperation; | 3478 | return error.UnsupportedOperation; |
| 3489 | } | 3479 | } |
| 3490 | 3480 | ||
| 3481 | pub fn failingInheritParentDir(userdata: ?*anyopaque, handle: Dir.Handle) InheritParentHandleError!Dir { | ||
| 3482 | _ = userdata; | ||
| 3483 | _ = handle; | ||
| 3484 | return error.UnsupportedOperation; | ||
| 3485 | } | ||
| 3486 | |||
| 3487 | pub fn failingInheritParentFile(userdata: ?*anyopaque, handle: File.Handle, flags: File.Flags) InheritParentHandleError!File { | ||
| 3488 | _ = userdata; | ||
| 3489 | _ = handle; | ||
| 3490 | _ = flags; | ||
| 3491 | return error.UnsupportedOperation; | ||
| 3492 | } | ||
| 3493 | |||
| 3491 | pub fn noRandom(userdata: ?*anyopaque, buffer: []u8) void { | 3494 | pub fn noRandom(userdata: ?*anyopaque, buffer: []u8) void { |
| 3492 | _ = userdata; | 3495 | _ = userdata; |
| 3493 | @memset(buffer, 0); | 3496 | @memset(buffer, 0); |
lib/std/Io/Reader/Limited.zig+2-4| ... | @@ -28,8 +28,7 @@ pub fn init(reader: *Reader, limit: Limit, buffer: []u8) Limited { | ... | @@ -28,8 +28,7 @@ pub fn init(reader: *Reader, limit: Limit, buffer: []u8) Limited { |
| 28 | fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize { | 28 | fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize { |
| 29 | const l: *Limited = @fieldParentPtr("interface", r); | 29 | const l: *Limited = @fieldParentPtr("interface", r); |
| 30 | if (l.remaining == .nothing) return error.EndOfStream; | 30 | if (l.remaining == .nothing) return error.EndOfStream; |
| 31 | const combined_limit = limit.min(l.remaining); | 31 | const n = try l.unlimited.stream(w, limit.min(l.remaining)); |
| 32 | const n = try l.unlimited.stream(w, combined_limit); | ||
| 33 | l.remaining = l.remaining.subtract(n).?; | 32 | l.remaining = l.remaining.subtract(n).?; |
| 34 | return n; | 33 | return n; |
| 35 | } | 34 | } |
| ... | @@ -53,8 +52,7 @@ test stream { | ... | @@ -53,8 +52,7 @@ test stream { |
| 53 | fn discard(r: *Reader, limit: Limit) Reader.Error!usize { | 52 | fn discard(r: *Reader, limit: Limit) Reader.Error!usize { |
| 54 | const l: *Limited = @fieldParentPtr("interface", r); | 53 | const l: *Limited = @fieldParentPtr("interface", r); |
| 55 | if (l.remaining == .nothing) return error.EndOfStream; | 54 | if (l.remaining == .nothing) return error.EndOfStream; |
| 56 | const combined_limit = limit.min(l.remaining); | 55 | const n = try l.unlimited.discard(limit.min(l.remaining)); |
| 57 | const n = try l.unlimited.discard(combined_limit); | ||
| 58 | l.remaining = l.remaining.subtract(n).?; | 56 | l.remaining = l.remaining.subtract(n).?; |
| 59 | return n; | 57 | return n; |
| 60 | } | 58 | } |
lib/std/Io/Threaded.zig+872-226| ... | @@ -85,6 +85,8 @@ csprng: Csprng = .uninitialized, | ... | @@ -85,6 +85,8 @@ csprng: Csprng = .uninitialized, |
| 85 | 85 | ||
| 86 | system_basic_information: SystemBasicInformation = .{}, | 86 | system_basic_information: SystemBasicInformation = .{}, |
| 87 | 87 | ||
| 88 | process_spawn_mutex: if (is_windows) Io.Mutex else void = if (is_windows) .init else {}, | ||
| 89 | |||
| 88 | const SystemBasicInformation = if (!is_windows) struct {} else struct { | 90 | const SystemBasicInformation = if (!is_windows) struct {} else struct { |
| 89 | buffer: windows.SYSTEM.BASIC_INFORMATION = undefined, | 91 | buffer: windows.SYSTEM.BASIC_INFORMATION = undefined, |
| 90 | initialized: std.atomic.Value(bool) = .{ .raw = false }, | 92 | initialized: std.atomic.Value(bool) = .{ .raw = false }, |
| ... | @@ -1567,6 +1569,10 @@ const poll_buffer_len = 64; | ... | @@ -1567,6 +1569,10 @@ const poll_buffer_len = 64; |
| 1567 | pub const default_PATH = "/usr/local/bin:/bin:/usr/bin"; | 1569 | pub const default_PATH = "/usr/local/bin:/bin:/usr/bin"; |
| 1568 | /// There are multiple kernel bugs being worked around with retries. | 1570 | /// There are multiple kernel bugs being worked around with retries. |
| 1569 | const max_windows_kernel_bug_retries = 13; | 1571 | const max_windows_kernel_bug_retries = 13; |
| 1572 | /// Similar in nature to `max_windows_kernel_bug_retries`, but much more likely to | ||
| 1573 | /// be a real error that won't go away after a retry, so the number of retries | ||
| 1574 | /// is lower. | ||
| 1575 | const max_windows_rename_retries = 3; | ||
| 1570 | 1576 | ||
| 1571 | comptime { | 1577 | comptime { |
| 1572 | if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX); | 1578 | if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX); |
| ... | @@ -1706,7 +1712,7 @@ var global_single_threaded_instance: Threaded = .init_single_threaded; | ... | @@ -1706,7 +1712,7 @@ var global_single_threaded_instance: Threaded = .init_single_threaded; |
| 1706 | pub const global_single_threaded: *Threaded = &global_single_threaded_instance; | 1712 | pub const global_single_threaded: *Threaded = &global_single_threaded_instance; |
| 1707 | 1713 | ||
| 1708 | pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void { | 1714 | pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void { |
| 1709 | mutexLock(&t.mutex); | 1715 | mutexLockUncancelable(&t.mutex); |
| 1710 | defer mutexUnlock(&t.mutex); | 1716 | defer mutexUnlock(&t.mutex); |
| 1711 | t.async_limit = new_limit; | 1717 | t.async_limit = new_limit; |
| 1712 | } | 1718 | } |
| ... | @@ -1727,7 +1733,7 @@ pub fn deinit(t: *Threaded) void { | ... | @@ -1727,7 +1733,7 @@ pub fn deinit(t: *Threaded) void { |
| 1727 | fn join(t: *Threaded) void { | 1733 | fn join(t: *Threaded) void { |
| 1728 | if (builtin.single_threaded) return; | 1734 | if (builtin.single_threaded) return; |
| 1729 | { | 1735 | { |
| 1730 | mutexLock(&t.mutex); | 1736 | mutexLockUncancelable(&t.mutex); |
| 1731 | defer mutexUnlock(&t.mutex); | 1737 | defer mutexUnlock(&t.mutex); |
| 1732 | t.join_requested = true; | 1738 | t.join_requested = true; |
| 1733 | } | 1739 | } |
| ... | @@ -1789,7 +1795,7 @@ fn worker(t: *Threaded) void { | ... | @@ -1789,7 +1795,7 @@ fn worker(t: *Threaded) void { |
| 1789 | 1795 | ||
| 1790 | defer t.wait_group.finish(); | 1796 | defer t.wait_group.finish(); |
| 1791 | 1797 | ||
| 1792 | mutexLock(&t.mutex); | 1798 | mutexLockUncancelable(&t.mutex); |
| 1793 | defer mutexUnlock(&t.mutex); | 1799 | defer mutexUnlock(&t.mutex); |
| 1794 | 1800 | ||
| 1795 | while (true) { | 1801 | while (true) { |
| ... | @@ -1798,7 +1804,7 @@ fn worker(t: *Threaded) void { | ... | @@ -1798,7 +1804,7 @@ fn worker(t: *Threaded) void { |
| 1798 | thread.cancel_protection = .unblocked; | 1804 | thread.cancel_protection = .unblocked; |
| 1799 | const runnable: *Runnable = @fieldParentPtr("node", runnable_node); | 1805 | const runnable: *Runnable = @fieldParentPtr("node", runnable_node); |
| 1800 | runnable.startFn(runnable, &thread, t); | 1806 | runnable.startFn(runnable, &thread, t); |
| 1801 | mutexLock(&t.mutex); | 1807 | mutexLockUncancelable(&t.mutex); |
| 1802 | t.busy_count -= 1; | 1808 | t.busy_count -= 1; |
| 1803 | } | 1809 | } |
| 1804 | if (t.join_requested) break; | 1810 | if (t.join_requested) break; |
| ... | @@ -1901,13 +1907,13 @@ pub fn io(t: *Threaded) Io { | ... | @@ -1901,13 +1907,13 @@ pub fn io(t: *Threaded) Io { |
| 1901 | .processSetCurrentDir = processSetCurrentDir, | 1907 | .processSetCurrentDir = processSetCurrentDir, |
| 1902 | .processSetCurrentPath = processSetCurrentPath, | 1908 | .processSetCurrentPath = processSetCurrentPath, |
| 1903 | .processReplace = processReplace, | 1909 | .processReplace = processReplace, |
| 1904 | .processReplacePath = processReplacePath, | ||
| 1905 | .processSpawn = processSpawn, | 1910 | .processSpawn = processSpawn, |
| 1906 | .processSpawnPath = processSpawnPath, | ||
| 1907 | .childWait = childWait, | 1911 | .childWait = childWait, |
| 1908 | .childKill = childKill, | 1912 | .childKill = childKill, |
| 1909 | 1913 | ||
| 1910 | .progressParentFile = progressParentFile, | 1914 | .progressParentFile = progressParentFile, |
| 1915 | .inheritParentDir = inheritParentDir, | ||
| 1916 | .inheritParentFile = inheritParentFile, | ||
| 1911 | 1917 | ||
| 1912 | .now = now, | 1918 | .now = now, |
| 1913 | .clockResolution = clockResolution, | 1919 | .clockResolution = clockResolution, |
| ... | @@ -1975,6 +1981,10 @@ const have_copy_file_range = switch (native_os) { | ... | @@ -1975,6 +1981,10 @@ const have_copy_file_range = switch (native_os) { |
| 1975 | .linux, .freebsd => true, | 1981 | .linux, .freebsd => true, |
| 1976 | else => false, | 1982 | else => false, |
| 1977 | }; | 1983 | }; |
| 1984 | const have_fcntl = switch (native_os) { | ||
| 1985 | .linux, .dragonfly, .freebsd, .netbsd, .openbsd, .illumos, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => true, | ||
| 1986 | else => false, | ||
| 1987 | }; | ||
| 1978 | const have_fcopyfile = is_darwin; | 1988 | const have_fcopyfile = is_darwin; |
| 1979 | const have_fchmodat2 = native_os == .linux and | 1989 | const have_fchmodat2 = native_os == .linux and |
| 1980 | (builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse true) and | 1990 | (builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse true) and |
| ... | @@ -2084,7 +2094,7 @@ fn async( | ... | @@ -2084,7 +2094,7 @@ fn async( |
| 2084 | }, | 2094 | }, |
| 2085 | }; | 2095 | }; |
| 2086 | 2096 | ||
| 2087 | mutexLock(&t.mutex); | 2097 | mutexLockUncancelable(&t.mutex); |
| 2088 | 2098 | ||
| 2089 | const busy_count = t.busy_count; | 2099 | const busy_count = t.busy_count; |
| 2090 | 2100 | ||
| ... | @@ -2136,7 +2146,7 @@ fn concurrent( | ... | @@ -2136,7 +2146,7 @@ fn concurrent( |
| 2136 | }; | 2146 | }; |
| 2137 | errdefer future.destroy(gpa); | 2147 | errdefer future.destroy(gpa); |
| 2138 | 2148 | ||
| 2139 | mutexLock(&t.mutex); | 2149 | mutexLockUncancelable(&t.mutex); |
| 2140 | defer mutexUnlock(&t.mutex); | 2150 | defer mutexUnlock(&t.mutex); |
| 2141 | 2151 | ||
| 2142 | const busy_count = t.busy_count; | 2152 | const busy_count = t.busy_count; |
| ... | @@ -2181,7 +2191,7 @@ fn groupAsync( | ... | @@ -2181,7 +2191,7 @@ fn groupAsync( |
| 2181 | error.OutOfMemory => return groupAsyncEager(start, context.ptr), | 2191 | error.OutOfMemory => return groupAsyncEager(start, context.ptr), |
| 2182 | }; | 2192 | }; |
| 2183 | 2193 | ||
| 2184 | mutexLock(&t.mutex); | 2194 | mutexLockUncancelable(&t.mutex); |
| 2185 | 2195 | ||
| 2186 | const busy_count = t.busy_count; | 2196 | const busy_count = t.busy_count; |
| 2187 | 2197 | ||
| ... | @@ -2244,7 +2254,7 @@ fn groupConcurrent( | ... | @@ -2244,7 +2254,7 @@ fn groupConcurrent( |
| 2244 | }; | 2254 | }; |
| 2245 | errdefer task.destroy(gpa); | 2255 | errdefer task.destroy(gpa); |
| 2246 | 2256 | ||
| 2247 | mutexLock(&t.mutex); | 2257 | mutexLockUncancelable(&t.mutex); |
| 2248 | defer mutexUnlock(&t.mutex); | 2258 | defer mutexUnlock(&t.mutex); |
| 2249 | 2259 | ||
| 2250 | const busy_count = t.busy_count; | 2260 | const busy_count = t.busy_count; |
| ... | @@ -4114,7 +4124,7 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { | ... | @@ -4114,7 +4124,7 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 4114 | 4124 | ||
| 4115 | fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION { | 4125 | fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION { |
| 4116 | if (!t.system_basic_information.initialized.load(.acquire)) { | 4126 | if (!t.system_basic_information.initialized.load(.acquire)) { |
| 4117 | mutexLock(&t.mutex); | 4127 | mutexLockUncancelable(&t.mutex); |
| 4118 | defer mutexUnlock(&t.mutex); | 4128 | defer mutexUnlock(&t.mutex); |
| 4119 | 4129 | ||
| 4120 | switch (windows.ntdll.NtQuerySystemInformation( | 4130 | switch (windows.ntdll.NtQuerySystemInformation( |
| ... | @@ -4462,7 +4472,7 @@ fn dirCreateFilePosix( | ... | @@ -4462,7 +4472,7 @@ fn dirCreateFilePosix( |
| 4462 | var fl_flags: usize = fl: { | 4472 | var fl_flags: usize = fl: { |
| 4463 | const syscall: Syscall = try .start(); | 4473 | const syscall: Syscall = try .start(); |
| 4464 | while (true) { | 4474 | while (true) { |
| 4465 | const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0)); | 4475 | const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(u32, 0)); |
| 4466 | switch (posix.errno(rc)) { | 4476 | switch (posix.errno(rc)) { |
| 4467 | .SUCCESS => { | 4477 | .SUCCESS => { |
| 4468 | syscall.finish(); | 4478 | syscall.finish(); |
| ... | @@ -5058,7 +5068,7 @@ fn dirOpenFilePosix( | ... | @@ -5058,7 +5068,7 @@ fn dirOpenFilePosix( |
| 5058 | var fl_flags: usize = fl: { | 5068 | var fl_flags: usize = fl: { |
| 5059 | const syscall: Syscall = try .start(); | 5069 | const syscall: Syscall = try .start(); |
| 5060 | while (true) { | 5070 | while (true) { |
| 5061 | const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0)); | 5071 | const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(u32, 0)); |
| 5062 | switch (posix.errno(rc)) { | 5072 | switch (posix.errno(rc)) { |
| 5063 | .SUCCESS => { | 5073 | .SUCCESS => { |
| 5064 | syscall.finish(); | 5074 | syscall.finish(); |
| ... | @@ -7030,6 +7040,11 @@ fn fileRealPathPosix(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.R | ... | @@ -7030,6 +7040,11 @@ fn fileRealPathPosix(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.R |
| 7030 | } | 7040 | } |
| 7031 | 7041 | ||
| 7032 | fn realPathPosix(fd: posix.fd_t, out_buffer: []u8) File.RealPathError!usize { | 7042 | fn realPathPosix(fd: posix.fd_t, out_buffer: []u8) File.RealPathError!usize { |
| 7043 | if (fd == posix.AT.FDCWD) return processCurrentPathPosix(out_buffer) catch |err| switch (err) { | ||
| 7044 | else => |e| return e, | ||
| 7045 | error.CurrentDirUnlinked => return error.FileNotFound, | ||
| 7046 | error.PathExceedsLimit => return error.NameTooLong, | ||
| 7047 | }; | ||
| 7033 | switch (native_os) { | 7048 | switch (native_os) { |
| 7034 | .dragonfly, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => { | 7049 | .dragonfly, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => { |
| 7035 | var sufficient_buffer: [posix.PATH_MAX]u8 = undefined; | 7050 | var sufficient_buffer: [posix.PATH_MAX]u8 = undefined; |
| ... | @@ -7674,6 +7689,8 @@ fn dirRenameWindowsInner( | ... | @@ -7674,6 +7689,8 @@ fn dirRenameWindowsInner( |
| 7674 | defer w.CloseHandle(src_fd); | 7689 | defer w.CloseHandle(src_fd); |
| 7675 | 7690 | ||
| 7676 | var rc: w.NTSTATUS = undefined; | 7691 | var rc: w.NTSTATUS = undefined; |
| 7692 | var attempt: u5 = 0; | ||
| 7693 | |||
| 7677 | // FileRenameInformationEx has varying levels of support: | 7694 | // FileRenameInformationEx has varying levels of support: |
| 7678 | // - FILE_RENAME_INFORMATION_EX requires >= win10_rs1 | 7695 | // - FILE_RENAME_INFORMATION_EX requires >= win10_rs1 |
| 7679 | // (INVALID_INFO_CLASS is returned if not supported) | 7696 | // (INVALID_INFO_CLASS is returned if not supported) |
| ... | @@ -7697,24 +7714,43 @@ fn dirRenameWindowsInner( | ... | @@ -7697,24 +7714,43 @@ fn dirRenameWindowsInner( |
| 7697 | }); | 7714 | }); |
| 7698 | var io_status_block: w.IO_STATUS_BLOCK = undefined; | 7715 | var io_status_block: w.IO_STATUS_BLOCK = undefined; |
| 7699 | const rename_info_buf = rename_info.toBuffer(); | 7716 | const rename_info_buf = rename_info.toBuffer(); |
| 7700 | rc = w.ntdll.NtSetInformationFile( | 7717 | var syscall: Syscall = try .start(); |
| 7701 | src_fd, | 7718 | while (true) { |
| 7702 | &io_status_block, | 7719 | rc = w.ntdll.NtSetInformationFile( |
| 7703 | rename_info_buf.ptr, | 7720 | src_fd, |
| 7704 | @intCast(rename_info_buf.len), | 7721 | &io_status_block, |
| 7705 | .RenameEx, | 7722 | rename_info_buf.ptr, |
| 7706 | ); | 7723 | @intCast(rename_info_buf.len), |
| 7707 | switch (rc) { | 7724 | .RenameEx, |
| 7708 | .SUCCESS => return, | 7725 | ); |
| 7709 | // The filesystem does not support FileDispositionInformationEx | 7726 | switch (rc) { |
| 7710 | .INVALID_PARAMETER, | 7727 | // For all other statuses, fall down to the switch below to handle them. |
| 7711 | // The operating system does not support FileDispositionInformationEx | 7728 | else => { |
| 7712 | .INVALID_INFO_CLASS, | 7729 | syscall.finish(); |
| 7713 | // The operating system does not support one of the flags | 7730 | break :need_fallback false; |
| 7714 | .NOT_SUPPORTED, | 7731 | }, |
| 7715 | => break :need_fallback true, | 7732 | .CANCELLED => try syscall.checkCancel(), |
| 7716 | // For all other statuses, fall down to the switch below to handle them. | 7733 | // The filesystem does not support FileDispositionInformationEx |
| 7717 | else => break :need_fallback false, | 7734 | .INVALID_PARAMETER, |
| 7735 | // The operating system does not support FileDispositionInformationEx | ||
| 7736 | .INVALID_INFO_CLASS, | ||
| 7737 | // The operating system does not support one of the flags | ||
| 7738 | .NOT_SUPPORTED, | ||
| 7739 | => { | ||
| 7740 | syscall.finish(); | ||
| 7741 | break :need_fallback true; | ||
| 7742 | }, | ||
| 7743 | .ACCESS_DENIED => { | ||
| 7744 | syscall.finish(); | ||
| 7745 | if (max_windows_rename_retries - attempt == 0) break :need_fallback false; | ||
| 7746 | try parking_sleep.sleep(.{ .duration = .{ | ||
| 7747 | .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1), | ||
| 7748 | .clock = .awake, | ||
| 7749 | } }); | ||
| 7750 | attempt += 1; | ||
| 7751 | syscall = try .start(); | ||
| 7752 | }, | ||
| 7753 | } | ||
| 7718 | } | 7754 | } |
| 7719 | }; | 7755 | }; |
| 7720 | 7756 | ||
| ... | @@ -7726,13 +7762,33 @@ fn dirRenameWindowsInner( | ... | @@ -7726,13 +7762,33 @@ fn dirRenameWindowsInner( |
| 7726 | }); | 7762 | }); |
| 7727 | var io_status_block: w.IO_STATUS_BLOCK = undefined; | 7763 | var io_status_block: w.IO_STATUS_BLOCK = undefined; |
| 7728 | const rename_info_buf = rename_info.toBuffer(); | 7764 | const rename_info_buf = rename_info.toBuffer(); |
| 7729 | rc = w.ntdll.NtSetInformationFile( | 7765 | var syscall: Syscall = try .start(); |
| 7730 | src_fd, | 7766 | while (true) { |
| 7731 | &io_status_block, | 7767 | rc = w.ntdll.NtSetInformationFile( |
| 7732 | rename_info_buf.ptr, | 7768 | src_fd, |
| 7733 | @intCast(rename_info_buf.len), | 7769 | &io_status_block, |
| 7734 | .Rename, | 7770 | rename_info_buf.ptr, |
| 7735 | ); | 7771 | @intCast(rename_info_buf.len), |
| 7772 | .Rename, | ||
| 7773 | ); | ||
| 7774 | switch (rc) { | ||
| 7775 | else => { | ||
| 7776 | syscall.finish(); | ||
| 7777 | break; | ||
| 7778 | }, | ||
| 7779 | .CANCELLED => try syscall.checkCancel(), | ||
| 7780 | .ACCESS_DENIED => { | ||
| 7781 | syscall.finish(); | ||
| 7782 | if (max_windows_rename_retries - attempt == 0) break; | ||
| 7783 | try parking_sleep.sleep(.{ .duration = .{ | ||
| 7784 | .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1), | ||
| 7785 | .clock = .awake, | ||
| 7786 | } }); | ||
| 7787 | attempt += 1; | ||
| 7788 | syscall = try .start(); | ||
| 7789 | }, | ||
| 7790 | } | ||
| 7791 | } | ||
| 7736 | } | 7792 | } |
| 7737 | 7793 | ||
| 7738 | switch (rc) { | 7794 | switch (rc) { |
| ... | @@ -10002,7 +10058,7 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { | ... | @@ -10002,7 +10058,7 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { |
| 10002 | .PENDING => unreachable, | 10058 | .PENDING => unreachable, |
| 10003 | .CANCELLED => unreachable, | 10059 | .CANCELLED => unreachable, |
| 10004 | .SUCCESS => return io_status_block.Information, | 10060 | .SUCCESS => return io_status_block.Information, |
| 10005 | .END_OF_FILE, .PIPE_BROKEN => return error.EndOfStream, | 10061 | .END_OF_FILE, .PIPE_BROKEN, .PIPE_CLOSING => return error.EndOfStream, |
| 10006 | .INVALID_HANDLE => return error.NotOpenForReading, | 10062 | .INVALID_HANDLE => return error.NotOpenForReading, |
| 10007 | .INVALID_DEVICE_REQUEST => return error.IsDir, | 10063 | .INVALID_DEVICE_REQUEST => return error.IsDir, |
| 10008 | .FILE_LOCK_CONFLICT => return error.LockViolation, | 10064 | .FILE_LOCK_CONFLICT => return error.LockViolation, |
| ... | @@ -10019,7 +10075,7 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { | ... | @@ -10019,7 +10075,7 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { |
| 10019 | .INVALID_USER_BUFFER => return error.SystemResources, | 10075 | .INVALID_USER_BUFFER => return error.SystemResources, |
| 10020 | .NO_MEMORY => return error.SystemResources, | 10076 | .NO_MEMORY => return error.SystemResources, |
| 10021 | .QUOTA_EXCEEDED => return error.SystemResources, | 10077 | .QUOTA_EXCEEDED => return error.SystemResources, |
| 10022 | .PIPE_BROKEN => return error.BrokenPipe, | 10078 | .PIPE_BROKEN, .PIPE_CLOSING => return error.BrokenPipe, |
| 10023 | .INVALID_HANDLE => return error.NotOpenForWriting, | 10079 | .INVALID_HANDLE => return error.NotOpenForWriting, |
| 10024 | .FILE_LOCK_CONFLICT => return error.LockViolation, | 10080 | .FILE_LOCK_CONFLICT => return error.LockViolation, |
| 10025 | .ACCESS_DENIED => return error.AccessDenied, | 10081 | .ACCESS_DENIED => return error.AccessDenied, |
| ... | @@ -14082,7 +14138,7 @@ fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancel | ... | @@ -14082,7 +14138,7 @@ fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancel |
| 14082 | const current_thread_id = Thread.currentId(); | 14138 | const current_thread_id = Thread.currentId(); |
| 14083 | 14139 | ||
| 14084 | if (@atomicLoad(std.Thread.Id, &t.stderr_mutex_locker, .unordered) != current_thread_id) { | 14140 | if (@atomicLoad(std.Thread.Id, &t.stderr_mutex_locker, .unordered) != current_thread_id) { |
| 14085 | mutexLock(&t.stderr_mutex); | 14141 | try mutexLock(&t.stderr_mutex); |
| 14086 | assert(t.stderr_mutex_lock_count == 0); | 14142 | assert(t.stderr_mutex_lock_count == 0); |
| 14087 | @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, current_thread_id, .unordered); | 14143 | @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, current_thread_id, .unordered); |
| 14088 | } | 14144 | } |
| ... | @@ -14145,6 +14201,7 @@ fn unlockStderr(userdata: ?*anyopaque) void { | ... | @@ -14145,6 +14201,7 @@ fn unlockStderr(userdata: ?*anyopaque) void { |
| 14145 | fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathError!usize { | 14201 | fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathError!usize { |
| 14146 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 14202 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 14147 | _ = t; | 14203 | _ = t; |
| 14204 | |||
| 14148 | if (is_windows) { | 14205 | if (is_windows) { |
| 14149 | var wtf16le_buf: [windows.PATH_MAX_WIDE:0]u16 = undefined; | 14206 | var wtf16le_buf: [windows.PATH_MAX_WIDE:0]u16 = undefined; |
| 14150 | const n = windows.ntdll.RtlGetCurrentDirectory_U(wtf16le_buf.len * 2 + 2, &wtf16le_buf) / 2; | 14207 | const n = windows.ntdll.RtlGetCurrentDirectory_U(wtf16le_buf.len * 2 + 2, &wtf16le_buf) / 2; |
| ... | @@ -14160,12 +14217,18 @@ fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathEr | ... | @@ -14160,12 +14217,18 @@ fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathEr |
| 14160 | end_index += std.unicode.wtf8Encode(codepoint, buffer[end_index..]) catch unreachable; | 14217 | end_index += std.unicode.wtf8Encode(codepoint, buffer[end_index..]) catch unreachable; |
| 14161 | } | 14218 | } |
| 14162 | return end_index; | 14219 | return end_index; |
| 14163 | } else if (native_os == .wasi and !builtin.link_libc) { | 14220 | } |
| 14221 | |||
| 14222 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 14164 | if (buffer.len == 0) return error.NameTooLong; | 14223 | if (buffer.len == 0) return error.NameTooLong; |
| 14165 | buffer[0] = '.'; | 14224 | buffer[0] = '.'; |
| 14166 | return 1; | 14225 | return 1; |
| 14167 | } | 14226 | } |
| 14168 | 14227 | ||
| 14228 | return processCurrentPathPosix(buffer); | ||
| 14229 | } | ||
| 14230 | |||
| 14231 | fn processCurrentPathPosix(buffer: []u8) process.CurrentPathError!usize { | ||
| 14169 | const err: posix.E = if (builtin.link_libc) err: { | 14232 | const err: posix.E = if (builtin.link_libc) err: { |
| 14170 | const c_err = if (std.c.getcwd(buffer.ptr, buffer.len)) |_| 0 else std.c._errno().*; | 14233 | const c_err = if (std.c.getcwd(buffer.ptr, buffer.len)) |_| 0 else std.c._errno().*; |
| 14171 | break :err @fromBackingInt(@intCast(c_err)); | 14234 | break :err @fromBackingInt(@intCast(c_err)); |
| ... | @@ -15101,7 +15164,7 @@ const WindowsEnvironStrings = struct { | ... | @@ -15101,7 +15164,7 @@ const WindowsEnvironStrings = struct { |
| 15101 | }; | 15164 | }; |
| 15102 | 15165 | ||
| 15103 | fn scanEnviron(t: *Threaded) void { | 15166 | fn scanEnviron(t: *Threaded) void { |
| 15104 | mutexLock(&t.mutex); | 15167 | mutexLockUncancelable(&t.mutex); |
| 15105 | defer mutexUnlock(&t.mutex); | 15168 | defer mutexUnlock(&t.mutex); |
| 15106 | if (t.environ_initialized) return; | 15169 | if (t.environ_initialized) return; |
| 15107 | t.environ.scan(t.allocator); | 15170 | t.environ.scan(t.allocator); |
| ... | @@ -15133,28 +15196,54 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces | ... | @@ -15133,28 +15196,54 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces |
| 15133 | }); | 15196 | }); |
| 15134 | }; | 15197 | }; |
| 15135 | 15198 | ||
| 15136 | return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); | 15199 | if (is_darwin) if (t.spawnDarwin( |
| 15137 | } | 15200 | .replace, |
| 15138 | 15201 | options.exe, | |
| 15139 | fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError { | 15202 | argv_buf.ptr, |
| 15140 | if (!process.can_replace) return error.OperationUnsupported; | 15203 | options.cwd, |
| 15141 | _ = userdata; | 15204 | env_block, |
| 15142 | _ = dir; | 15205 | options.expand_arg0, |
| 15143 | _ = options; | 15206 | .none, |
| 15144 | @panic("TODO processReplacePath"); | 15207 | .inherit, |
| 15145 | } | 15208 | .inherit, |
| 15146 | 15209 | .inherit, | |
| 15147 | fn processSpawnPath(userdata: ?*anyopaque, dir: Dir, options: process.SpawnOptions) process.SpawnError!process.Child { | 15210 | options.inherit_dirs, |
| 15148 | if (!process.can_spawn) return error.OperationUnsupported; | 15211 | options.inherit_files, |
| 15149 | _ = userdata; | 15212 | null, |
| 15150 | _ = dir; | 15213 | options.start_suspended, |
| 15151 | _ = options; | 15214 | PATH, |
| 15152 | @panic("TODO processSpawnPath"); | 15215 | )) |_| unreachable else |err| switch (err) { |
| 15216 | else => |e| return e, | ||
| 15217 | error.WouldBlock => return error.SystemResources, | ||
| 15218 | error.FileTooBig, | ||
| 15219 | error.NoSpaceLeft, | ||
| 15220 | error.PathAlreadyExists, | ||
| 15221 | error.SymLinkLoop, | ||
| 15222 | error.NetworkNotFound, | ||
| 15223 | error.PipeBusy, | ||
| 15224 | error.AntivirusInterference, | ||
| 15225 | => return error.FileNotFound, | ||
| 15226 | error.DeviceBusy, | ||
| 15227 | error.NoDevice, | ||
| 15228 | error.UnrecognizedVolume, | ||
| 15229 | error.ReadOnlyFileSystem, | ||
| 15230 | => return error.FileSystem, | ||
| 15231 | error.ResourceLimitReached => return error.SystemResources, | ||
| 15232 | error.FileLocksUnsupported => unreachable, // lock not requested | ||
| 15233 | error.InvalidWtf8 => unreachable, // windows only | ||
| 15234 | error.InvalidBatchScriptArg => unreachable, // windows only | ||
| 15235 | error.InvalidUserId => unreachable, // not supported | ||
| 15236 | error.InvalidProcessGroupId => unreachable, // passed null | ||
| 15237 | error.InvalidName => unreachable, // windows only | ||
| 15238 | error.ProcessAlreadyExec => unreachable, // linux only | ||
| 15239 | }; | ||
| 15240 | return posixExec(options.exe, argv_buf.ptr, options.expand_arg0, env_block, PATH); | ||
| 15153 | } | 15241 | } |
| 15154 | 15242 | ||
| 15155 | const processSpawn = switch (native_os) { | 15243 | const processSpawn = switch (native_os) { |
| 15156 | .wasi, .emscripten, .ios, .tvos, .visionos, .watchos => processSpawnUnsupported, | 15244 | .wasi, .emscripten, .ios, .tvos, .visionos, .watchos => processSpawnUnsupported, |
| 15157 | .windows => processSpawnWindows, | 15245 | .windows => processSpawnWindows, |
| 15246 | .driverkit, .maccatalyst, .macos => processSpawnDarwin, | ||
| 15158 | else => processSpawnPosix, | 15247 | else => processSpawnPosix, |
| 15159 | }; | 15248 | }; |
| 15160 | 15249 | ||
| ... | @@ -15230,16 +15319,12 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -15230,16 +15319,12 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 15230 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null); | 15319 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null); |
| 15231 | for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeSentinel(u8, arg, 0)).ptr; | 15320 | for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeSentinel(u8, arg, 0)).ptr; |
| 15232 | 15321 | ||
| 15233 | const prog_fileno = 3; | ||
| 15234 | comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno); | ||
| 15235 | |||
| 15236 | const env_block = env_block: { | 15322 | const env_block = env_block: { |
| 15237 | const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; | ||
| 15238 | if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{ | 15323 | if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{ |
| 15239 | .zig_progress_fd = prog_fd, | 15324 | .zig_progress_fd = prog_pipe[1], |
| 15240 | }); | 15325 | }); |
| 15241 | break :env_block try t.environ.process_environ.createPosixBlock(arena, .{ | 15326 | break :env_block try t.environ.process_environ.createPosixBlock(arena, .{ |
| 15242 | .zig_progress_fd = prog_fd, | 15327 | .zig_progress_fd = prog_pipe[1], |
| 15243 | }); | 15328 | }); |
| 15244 | }; | 15329 | }; |
| 15245 | 15330 | ||
| ... | @@ -15267,23 +15352,42 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -15267,23 +15352,42 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 15267 | if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked; | 15352 | if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked; |
| 15268 | const ep1 = err_pipe[1]; | 15353 | const ep1 = err_pipe[1]; |
| 15269 | 15354 | ||
| 15270 | setUpChildIo(options.stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); | 15355 | // Must happen before clobbering fds below. |
| 15271 | setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); | ||
| 15272 | setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); | ||
| 15273 | |||
| 15274 | switch (options.cwd) { | 15356 | switch (options.cwd) { |
| 15275 | .inherit => {}, | 15357 | .inherit => {}, |
| 15276 | .dir => |cwd| { | 15358 | .dir => |cwd| fchdir(cwd.handle) catch |err| forkBail(ep1, err), |
| 15277 | fchdir(cwd.handle) catch |err| forkBail(ep1, err); | 15359 | .path => |cwd| chdir(cwd) catch |err| forkBail(ep1, err), |
| 15278 | }, | ||
| 15279 | .path => |cwd| { | ||
| 15280 | chdir(cwd) catch |err| forkBail(ep1, err); | ||
| 15281 | }, | ||
| 15282 | } | 15360 | } |
| 15283 | 15361 | ||
| 15284 | // Must happen after fchdir above, the cwd file descriptor might be | 15362 | for (options.inherit_dirs) |inherit_dir| switch (posix.errno( |
| 15285 | // equal to prog_fileno and be clobbered by this dup2 call. | 15363 | posix.system.fcntl(inherit_dir.handle, posix.F.SETFD, @as(u32, 0)), |
| 15286 | if (prog_pipe[1] != -1) dup2(prog_pipe[1], prog_fileno) catch |err| forkBail(ep1, err); | 15364 | )) { |
| 15365 | .SUCCESS => {}, | ||
| 15366 | else => |err| forkBail(ep1, posix.unexpectedErrno(err)), | ||
| 15367 | }; | ||
| 15368 | for (options.inherit_files) |file| switch (posix.errno( | ||
| 15369 | posix.system.fcntl(file.handle, posix.F.SETFD, @as(u32, 0)), | ||
| 15370 | )) { | ||
| 15371 | .SUCCESS => {}, | ||
| 15372 | else => |err| forkBail(ep1, posix.unexpectedErrno(err)), | ||
| 15373 | }; | ||
| 15374 | if (prog_pipe[1] != -1) switch (posix.errno( | ||
| 15375 | posix.system.fcntl(prog_pipe[1], posix.F.SETFD, @as(u32, 0)), | ||
| 15376 | )) { | ||
| 15377 | .SUCCESS => {}, | ||
| 15378 | else => |err| forkBail(ep1, posix.unexpectedErrno(err)), | ||
| 15379 | }; | ||
| 15380 | for ( | ||
| 15381 | [_]process.SpawnOptions.StdIo{ options.stdin, options.stdout, options.stderr }, | ||
| 15382 | [_]posix.fd_t{ stdin_pipe[0], stdout_pipe[1], stderr_pipe[1] }, | ||
| 15383 | [_]posix.fd_t{ posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO }, | ||
| 15384 | ) |stdio, pipe_fd, std_fd| switch (stdio) { | ||
| 15385 | .pipe => dup2(pipe_fd, std_fd) catch |err| forkBail(ep1, err), | ||
| 15386 | .close => closeFd(std_fd), | ||
| 15387 | .inherit => {}, | ||
| 15388 | .ignore => dup2(dev_null_fd, std_fd) catch |err| forkBail(ep1, err), | ||
| 15389 | .file => |file| dup2(file.handle, std_fd) catch |err| forkBail(ep1, err), | ||
| 15390 | }; | ||
| 15287 | 15391 | ||
| 15288 | if (options.gid) |gid| { | 15392 | if (options.gid) |gid| { |
| 15289 | switch (posix.errno(posix.system.setregid(gid, gid))) { | 15393 | switch (posix.errno(posix.system.setregid(gid, gid))) { |
| ... | @@ -15316,14 +15420,14 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -15316,14 +15420,14 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 15316 | } | 15420 | } |
| 15317 | 15421 | ||
| 15318 | if (options.start_suspended) { | 15422 | if (options.start_suspended) { |
| 15319 | switch (posix.errno(posix.system.kill(0, .STOP))) { | 15423 | switch (posix.errno(posix.system.kill(posix.system.getpid(), .STOP))) { |
| 15320 | .SUCCESS => {}, | 15424 | .SUCCESS => {}, |
| 15321 | .PERM => forkBail(ep1, error.PermissionDenied), | 15425 | .PERM => forkBail(ep1, error.PermissionDenied), |
| 15322 | else => forkBail(ep1, error.Unexpected), | 15426 | else => forkBail(ep1, error.Unexpected), |
| 15323 | } | 15427 | } |
| 15324 | } | 15428 | } |
| 15325 | 15429 | ||
| 15326 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); | 15430 | const err = posixExec(options.exe, argv_buf.ptr, options.expand_arg0, env_block, PATH); |
| 15327 | forkBail(ep1, err); | 15431 | forkBail(ep1, err); |
| 15328 | } | 15432 | } |
| 15329 | 15433 | ||
| ... | @@ -15359,7 +15463,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -15359,7 +15463,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 15359 | 15463 | ||
| 15360 | fn getDevNullFd(t: *Threaded) !posix.fd_t { | 15464 | fn getDevNullFd(t: *Threaded) !posix.fd_t { |
| 15361 | { | 15465 | { |
| 15362 | mutexLock(&t.mutex); | 15466 | try mutexLock(&t.mutex); |
| 15363 | defer mutexUnlock(&t.mutex); | 15467 | defer mutexUnlock(&t.mutex); |
| 15364 | if (t.null_file.fd != -1) return t.null_file.fd; | 15468 | if (t.null_file.fd != -1) return t.null_file.fd; |
| 15365 | } | 15469 | } |
| ... | @@ -15371,7 +15475,7 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t { | ... | @@ -15371,7 +15475,7 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t { |
| 15371 | .SUCCESS => { | 15475 | .SUCCESS => { |
| 15372 | syscall.finish(); | 15476 | syscall.finish(); |
| 15373 | const fresh_fd: posix.fd_t = @intCast(rc); | 15477 | const fresh_fd: posix.fd_t = @intCast(rc); |
| 15374 | mutexLock(&t.mutex); // Another thread might have won the race. | 15478 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 15375 | defer mutexUnlock(&t.mutex); | 15479 | defer mutexUnlock(&t.mutex); |
| 15376 | if (t.null_file.fd != -1) { | 15480 | if (t.null_file.fd != -1) { |
| 15377 | closeFd(fresh_fd); | 15481 | closeFd(fresh_fd); |
| ... | @@ -15746,19 +15850,137 @@ fn destroyPipe(pipe: [2]posix.fd_t) void { | ... | @@ -15746,19 +15850,137 @@ fn destroyPipe(pipe: [2]posix.fd_t) void { |
| 15746 | if (pipe[0] != pipe[1]) closeFd(pipe[1]); | 15850 | if (pipe[0] != pipe[1]) closeFd(pipe[1]); |
| 15747 | } | 15851 | } |
| 15748 | 15852 | ||
| 15749 | fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void { | 15853 | fn processSpawnDarwin(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { |
| 15750 | switch (stdio) { | 15854 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 15751 | .pipe => try dup2(pipe_fd, std_fileno), | 15855 | |
| 15752 | .close => closeFd(std_fileno), | 15856 | t.scanEnviron(); // for PATH |
| 15753 | .inherit => {}, | 15857 | const PATH = t.environ.string.PATH orelse default_PATH; |
| 15754 | .ignore => try dup2(dev_null_fd, std_fileno), | 15858 | |
| 15755 | .file => |file| try dup2(file.handle, std_fileno), | 15859 | var arena_allocator = std.heap.ArenaAllocator.init(t.allocator); |
| 15756 | } | 15860 | defer arena_allocator.deinit(); |
| 15861 | const arena = arena_allocator.allocator(); | ||
| 15862 | |||
| 15863 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null); | ||
| 15864 | for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeSentinel(u8, arg, 0)).ptr; | ||
| 15865 | |||
| 15866 | const env_block = env_block: { | ||
| 15867 | const prog_fd: i32 = -1; | ||
| 15868 | if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{ | ||
| 15869 | .zig_progress_fd = prog_fd, | ||
| 15870 | }); | ||
| 15871 | break :env_block try t.environ.process_environ.createPosixBlock(arena, .{ | ||
| 15872 | .zig_progress_fd = prog_fd, | ||
| 15873 | }); | ||
| 15874 | }; | ||
| 15875 | |||
| 15876 | const spawned = try t.spawnDarwin( | ||
| 15877 | .spawn, | ||
| 15878 | options.exe, | ||
| 15879 | argv_buf.ptr, | ||
| 15880 | options.cwd, | ||
| 15881 | env_block, | ||
| 15882 | options.expand_arg0, | ||
| 15883 | options.progress_node, | ||
| 15884 | options.stdin, | ||
| 15885 | options.stdout, | ||
| 15886 | options.stderr, | ||
| 15887 | options.inherit_dirs, | ||
| 15888 | options.inherit_files, | ||
| 15889 | options.pgid, | ||
| 15890 | options.start_suspended, | ||
| 15891 | PATH, | ||
| 15892 | ); | ||
| 15893 | return .{ | ||
| 15894 | .id = spawned.pid, | ||
| 15895 | .thread_handle = {}, | ||
| 15896 | .stdin = spawned.stdin, | ||
| 15897 | .stdout = spawned.stdout, | ||
| 15898 | .stderr = spawned.stderr, | ||
| 15899 | .request_resource_usage_statistics = options.request_resource_usage_statistics, | ||
| 15900 | }; | ||
| 15757 | } | 15901 | } |
| 15758 | 15902 | ||
| 15759 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { | 15903 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { |
| 15760 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 15904 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 15761 | 15905 | ||
| 15906 | var arena_allocator = std.heap.ArenaAllocator.init(t.allocator); | ||
| 15907 | defer arena_allocator.deinit(); | ||
| 15908 | const arena = arena_allocator.allocator(); | ||
| 15909 | |||
| 15910 | const cwd_w = cwd_w: { | ||
| 15911 | switch (options.cwd) { | ||
| 15912 | .inherit => break :cwd_w null, | ||
| 15913 | .dir => |cwd_dir| { | ||
| 15914 | var dir_path_buffer = try arena.alloc(u16, windows.PATH_MAX_WIDE + 1); | ||
| 15915 | const dir_path = try GetFinalPathNameByHandle( | ||
| 15916 | cwd_dir.handle, | ||
| 15917 | .{}, | ||
| 15918 | dir_path_buffer[0..windows.PATH_MAX_WIDE], | ||
| 15919 | ); | ||
| 15920 | dir_path_buffer[dir_path.len] = 0; | ||
| 15921 | // Shrink the allocation down to just the path buffer + sentinel | ||
| 15922 | dir_path_buffer = try arena.realloc(dir_path_buffer, dir_path.len + 1); | ||
| 15923 | break :cwd_w dir_path_buffer[0..dir_path.len :0]; | ||
| 15924 | }, | ||
| 15925 | .path => |cwd| { | ||
| 15926 | break :cwd_w try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd); | ||
| 15927 | }, | ||
| 15928 | } | ||
| 15929 | }; | ||
| 15930 | const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; | ||
| 15931 | |||
| 15932 | // If the app name has more than just a filename, then we need to separate | ||
| 15933 | // that into the basename and dirname and use the dirname as an addition to | ||
| 15934 | // the cwd path. This is because NtQueryDirectoryFile cannot accept | ||
| 15935 | // FileName params with path separators. | ||
| 15936 | var app_w_buf: [windows.PATH_MAX_WIDE]u16 = undefined; | ||
| 15937 | // If the app name is absolute, then the cwd will already have the app's dirname in it, | ||
| 15938 | // so only populate app_dir if app name is a relative path with > 0 path separators. | ||
| 15939 | const app_path_w, const app_dir_w, const app_name_w = app: { | ||
| 15940 | const path = switch (options.exe) { | ||
| 15941 | .detect, .search, .path => options.argv[0], | ||
| 15942 | .file => |file| { | ||
| 15943 | const path_w = try GetFinalPathNameByHandle(file.handle, .{}, &app_w_buf); | ||
| 15944 | var path_it: Dir.path.ComponentIterator(.windows, u16) = .init(path_w); | ||
| 15945 | const base = path_it.last().?; | ||
| 15946 | break :app .{ | ||
| 15947 | null, | ||
| 15948 | if (path_it.previous()) |dir| dir.path else path_it.root(), | ||
| 15949 | base.name, | ||
| 15950 | }; | ||
| 15951 | }, | ||
| 15952 | .explicit => |explicit| explicit.path, | ||
| 15953 | }; | ||
| 15954 | break :app .{ | ||
| 15955 | if (Dir.path.isAbsolute(path)) null else switch (options.exe) { | ||
| 15956 | // The cwd provided by options is in effect when choosing the executable | ||
| 15957 | // path to match POSIX semantics. | ||
| 15958 | .detect, .search => cwd_w, | ||
| 15959 | .path => |dir| try GetFinalPathNameByHandle(dir.handle, .{}, &app_w_buf), | ||
| 15960 | .file => unreachable, | ||
| 15961 | .explicit => |explicit| try GetFinalPathNameByHandle( | ||
| 15962 | explicit.dir.handle, | ||
| 15963 | .{}, | ||
| 15964 | &app_w_buf, | ||
| 15965 | ), | ||
| 15966 | }, | ||
| 15967 | if (Dir.path.dirname(path)) |app_dir| | ||
| 15968 | try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_dir) | ||
| 15969 | else | ||
| 15970 | null, | ||
| 15971 | try std.unicode.wtf8ToWtf16LeAllocZ(arena, Dir.path.basename(path)), | ||
| 15972 | }; | ||
| 15973 | }; | ||
| 15974 | |||
| 15975 | const flags: windows.CreateProcessFlags = .{ | ||
| 15976 | .create_suspended = options.start_suspended, | ||
| 15977 | .create_unicode_environment = true, | ||
| 15978 | .create_no_window = options.create_no_window, | ||
| 15979 | }; | ||
| 15980 | |||
| 15981 | try mutexLock(&t.process_spawn_mutex); | ||
| 15982 | defer mutexUnlock(&t.process_spawn_mutex); | ||
| 15983 | |||
| 15762 | const any_ignore = | 15984 | const any_ignore = |
| 15763 | options.stdin == .ignore or | 15985 | options.stdin == .ignore or |
| 15764 | options.stdout == .ignore or | 15986 | options.stdout == .ignore or |
| ... | @@ -15800,6 +16022,43 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15800,6 +16022,43 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15800 | }) else undefined; | 16022 | }) else undefined; |
| 15801 | errdefer if (options.progress_node.index != .none) for (prog_pipe) |handle| windows.CloseHandle(handle); | 16023 | errdefer if (options.progress_node.index != .none) for (prog_pipe) |handle| windows.CloseHandle(handle); |
| 15802 | 16024 | ||
| 16025 | defer for (options.inherit_dirs) |inherit_dir| switch (windows.ntdll.NtSetInformationObject( | ||
| 16026 | inherit_dir.handle, | ||
| 16027 | .HandleFlag, | ||
| 16028 | &windows.OBJECT.HANDLE_FLAG{ .INHERIT = false }, | ||
| 16029 | @sizeOf(windows.OBJECT.HANDLE_FLAG), | ||
| 16030 | )) { | ||
| 16031 | .SUCCESS => {}, | ||
| 16032 | else => |status| windows.unexpectedStatus(status) catch {}, | ||
| 16033 | }; | ||
| 16034 | for (options.inherit_dirs) |inherit_dir| switch (windows.ntdll.NtSetInformationObject( | ||
| 16035 | inherit_dir.handle, | ||
| 16036 | .HandleFlag, | ||
| 16037 | &windows.OBJECT.HANDLE_FLAG{ .INHERIT = true }, | ||
| 16038 | @sizeOf(windows.OBJECT.HANDLE_FLAG), | ||
| 16039 | )) { | ||
| 16040 | .SUCCESS => {}, | ||
| 16041 | else => |status| return windows.unexpectedStatus(status), | ||
| 16042 | }; | ||
| 16043 | defer for (options.inherit_files) |inherit_file| switch (windows.ntdll.NtSetInformationObject( | ||
| 16044 | inherit_file.handle, | ||
| 16045 | .HandleFlag, | ||
| 16046 | &windows.OBJECT.HANDLE_FLAG{ .INHERIT = false }, | ||
| 16047 | @sizeOf(windows.OBJECT.HANDLE_FLAG), | ||
| 16048 | )) { | ||
| 16049 | .SUCCESS => {}, | ||
| 16050 | else => |status| windows.unexpectedStatus(status) catch {}, | ||
| 16051 | }; | ||
| 16052 | for (options.inherit_files) |inherit_file| switch (windows.ntdll.NtSetInformationObject( | ||
| 16053 | inherit_file.handle, | ||
| 16054 | .HandleFlag, | ||
| 16055 | &windows.OBJECT.HANDLE_FLAG{ .INHERIT = true }, | ||
| 16056 | @sizeOf(windows.OBJECT.HANDLE_FLAG), | ||
| 16057 | )) { | ||
| 16058 | .SUCCESS => {}, | ||
| 16059 | else => |status| return windows.unexpectedStatus(status), | ||
| 16060 | }; | ||
| 16061 | |||
| 15803 | var siStartInfo: windows.STARTUPINFOW = .{ | 16062 | var siStartInfo: windows.STARTUPINFOW = .{ |
| 15804 | .cb = @sizeOf(windows.STARTUPINFOW), | 16063 | .cb = @sizeOf(windows.STARTUPINFOW), |
| 15805 | .dwFlags = windows.STARTF_USESTDHANDLES, | 16064 | .dwFlags = windows.STARTF_USESTDHANDLES, |
| ... | @@ -15877,32 +16136,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15877,32 +16136,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15877 | }; | 16136 | }; |
| 15878 | var piProcInfo: windows.PROCESS.INFORMATION = undefined; | 16137 | var piProcInfo: windows.PROCESS.INFORMATION = undefined; |
| 15879 | 16138 | ||
| 15880 | var arena_allocator = std.heap.ArenaAllocator.init(t.allocator); | ||
| 15881 | defer arena_allocator.deinit(); | ||
| 15882 | const arena = arena_allocator.allocator(); | ||
| 15883 | |||
| 15884 | const cwd_w = cwd_w: { | ||
| 15885 | switch (options.cwd) { | ||
| 15886 | .inherit => break :cwd_w null, | ||
| 15887 | .dir => |cwd_dir| { | ||
| 15888 | var dir_path_buffer = try arena.alloc(u16, windows.PATH_MAX_WIDE + 1); | ||
| 15889 | const dir_path = try GetFinalPathNameByHandle( | ||
| 15890 | cwd_dir.handle, | ||
| 15891 | .{}, | ||
| 15892 | dir_path_buffer[0..windows.PATH_MAX_WIDE], | ||
| 15893 | ); | ||
| 15894 | dir_path_buffer[dir_path.len] = 0; | ||
| 15895 | // Shrink the allocation down to just the path buffer + sentinel | ||
| 15896 | dir_path_buffer = try arena.realloc(dir_path_buffer, dir_path.len + 1); | ||
| 15897 | break :cwd_w dir_path_buffer[0..dir_path.len :0]; | ||
| 15898 | }, | ||
| 15899 | .path => |cwd| { | ||
| 15900 | break :cwd_w try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd); | ||
| 15901 | }, | ||
| 15902 | } | ||
| 15903 | }; | ||
| 15904 | const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; | ||
| 15905 | |||
| 15906 | const env_block = env_block: { | 16139 | const env_block = env_block: { |
| 15907 | const prog_handle = if (options.progress_node.index != .none) | 16140 | const prog_handle = if (options.progress_node.index != .none) |
| 15908 | prog_pipe[1] | 16141 | prog_pipe[1] |
| ... | @@ -15916,45 +16149,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15916,45 +16149,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15916 | }); | 16149 | }); |
| 15917 | }; | 16150 | }; |
| 15918 | 16151 | ||
| 15919 | const app_name_wtf8 = options.argv[0]; | ||
| 15920 | const app_name_is_absolute = Dir.path.isAbsolute(app_name_wtf8); | ||
| 15921 | |||
| 15922 | // The cwd provided by options is in effect when choosing the executable | ||
| 15923 | // path to match POSIX semantics. | ||
| 15924 | const cwd_path_w = x: { | ||
| 15925 | // If the app name is absolute, then we need to use its dirname as the cwd | ||
| 15926 | if (app_name_is_absolute) { | ||
| 15927 | const dir = Dir.path.dirname(app_name_wtf8).?; | ||
| 15928 | break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, dir); | ||
| 15929 | } else if (cwd_w) |cwd| { | ||
| 15930 | break :x cwd; | ||
| 15931 | } else { | ||
| 15932 | break :x &[_:0]u16{}; // empty for cwd | ||
| 15933 | } | ||
| 15934 | }; | ||
| 15935 | |||
| 15936 | // If the app name has more than just a filename, then we need to separate | ||
| 15937 | // that into the basename and dirname and use the dirname as an addition to | ||
| 15938 | // the cwd path. This is because NtQueryDirectoryFile cannot accept | ||
| 15939 | // FileName params with path separators. | ||
| 15940 | const app_basename_wtf8 = Dir.path.basename(app_name_wtf8); | ||
| 15941 | // If the app name is absolute, then the cwd will already have the app's dirname in it, | ||
| 15942 | // so only populate app_dirname if app name is a relative path with > 0 path separators. | ||
| 15943 | const maybe_app_dirname_wtf8 = if (!app_name_is_absolute) Dir.path.dirname(app_name_wtf8) else null; | ||
| 15944 | const app_dirname_w: ?[:0]u16 = x: { | ||
| 15945 | if (maybe_app_dirname_wtf8) |app_dirname_wtf8| { | ||
| 15946 | break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_dirname_wtf8); | ||
| 15947 | } | ||
| 15948 | break :x null; | ||
| 15949 | }; | ||
| 15950 | const app_name_w = try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_basename_wtf8); | ||
| 15951 | |||
| 15952 | const flags: windows.CreateProcessFlags = .{ | ||
| 15953 | .create_suspended = options.start_suspended, | ||
| 15954 | .create_unicode_environment = true, | ||
| 15955 | .create_no_window = options.create_no_window, | ||
| 15956 | }; | ||
| 15957 | |||
| 15958 | run: { | 16152 | run: { |
| 15959 | // We have to scan each time because the PEB environment pointer is not stable. | 16153 | // We have to scan each time because the PEB environment pointer is not stable. |
| 15960 | const env_strings: WindowsEnvironStrings = .scan(); | 16154 | const env_strings: WindowsEnvironStrings = .scan(); |
| ... | @@ -15975,10 +16169,8 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15975,10 +16169,8 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15975 | 16169 | ||
| 15976 | var dir_buf: std.ArrayList(u16) = .empty; | 16170 | var dir_buf: std.ArrayList(u16) = .empty; |
| 15977 | 16171 | ||
| 15978 | if (cwd_path_w.len > 0) { | 16172 | if (app_path_w) |app_path| try dir_buf.appendSlice(arena, app_path); |
| 15979 | try dir_buf.appendSlice(arena, cwd_path_w); | 16173 | if (app_dir_w) |app_dir| { |
| 15980 | } | ||
| 15981 | if (app_dirname_w) |app_dir| { | ||
| 15982 | if (dir_buf.items.len > 0) try dir_buf.append(arena, Dir.path.sep); | 16174 | if (dir_buf.items.len > 0) try dir_buf.append(arena, Dir.path.sep); |
| 15983 | try dir_buf.appendSlice(arena, app_dir); | 16175 | try dir_buf.appendSlice(arena, app_dir); |
| 15984 | } | 16176 | } |
| ... | @@ -16003,15 +16195,18 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -16003,15 +16195,18 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 16003 | else => |e| return e, | 16195 | else => |e| return e, |
| 16004 | }; | 16196 | }; |
| 16005 | 16197 | ||
| 16006 | // If the app name had path separators, that disallows PATH searching, | ||
| 16007 | // and there's no need to search the PATH if the app name is absolute. | ||
| 16008 | // We still search the path if the cwd is absolute because of the | 16198 | // We still search the path if the cwd is absolute because of the |
| 16009 | // "cwd provided by options is in effect when choosing the executable path | 16199 | // "cwd provided by options is in effect when choosing the executable path |
| 16010 | // to match posix semantics" behavior--we don't want to skip searching | 16200 | // to match posix semantics" behavior--we don't want to skip searching |
| 16011 | // the PATH just because we were trying to set the cwd of the child process. | 16201 | // the PATH just because we were trying to set the cwd of the child process. |
| 16012 | if (app_dirname_w != null or app_name_is_absolute) { | 16202 | if (switch (options.exe) { |
| 16013 | return original_err; | 16203 | // If the app name had path separators, that disallows PATH searching. |
| 16014 | } | 16204 | .detect => app_dir_w != null, |
| 16205 | // There's no need to search the PATH if the app name is absolute. | ||
| 16206 | .search => app_path_w == null, | ||
| 16207 | // PATH searching is disallowed. | ||
| 16208 | .path, .file, .explicit => true, | ||
| 16209 | }) return original_err; | ||
| 16015 | 16210 | ||
| 16016 | var it = std.mem.tokenizeScalar(u16, PATH, ';'); | 16211 | var it = std.mem.tokenizeScalar(u16, PATH, ';'); |
| 16017 | while (it.next()) |search_path| { | 16212 | while (it.next()) |search_path| { |
| ... | @@ -16089,11 +16284,9 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -16089,11 +16284,9 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 16089 | }; | 16284 | }; |
| 16090 | } | 16285 | } |
| 16091 | 16286 | ||
| 16092 | fn inheritFile() windows.HANDLE {} | ||
| 16093 | |||
| 16094 | fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | 16287 | fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 16095 | { | 16288 | { |
| 16096 | mutexLock(&t.mutex); | 16289 | try mutexLock(&t.mutex); |
| 16097 | defer mutexUnlock(&t.mutex); | 16290 | defer mutexUnlock(&t.mutex); |
| 16098 | if (t.random_file.handle) |handle| return handle; | 16291 | if (t.random_file.handle) |handle| return handle; |
| 16099 | } | 16292 | } |
| ... | @@ -16116,7 +16309,7 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | ... | @@ -16116,7 +16309,7 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 16116 | )) { | 16309 | )) { |
| 16117 | .SUCCESS => { | 16310 | .SUCCESS => { |
| 16118 | syscall.finish(); | 16311 | syscall.finish(); |
| 16119 | mutexLock(&t.mutex); // Another thread might have won the race. | 16312 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 16120 | defer mutexUnlock(&t.mutex); | 16313 | defer mutexUnlock(&t.mutex); |
| 16121 | if (t.random_file.handle) |prev_handle| { | 16314 | if (t.random_file.handle) |prev_handle| { |
| 16122 | windows.CloseHandle(fresh_handle); | 16315 | windows.CloseHandle(fresh_handle); |
| ... | @@ -16137,7 +16330,7 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | ... | @@ -16137,7 +16330,7 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 16137 | 16330 | ||
| 16138 | fn getNulDevice(t: *Threaded) !windows.HANDLE { | 16331 | fn getNulDevice(t: *Threaded) !windows.HANDLE { |
| 16139 | { | 16332 | { |
| 16140 | mutexLock(&t.mutex); | 16333 | try mutexLock(&t.mutex); |
| 16141 | defer mutexUnlock(&t.mutex); | 16334 | defer mutexUnlock(&t.mutex); |
| 16142 | if (t.null_file.handle) |handle| return handle; | 16335 | if (t.null_file.handle) |handle| return handle; |
| 16143 | } | 16336 | } |
| ... | @@ -16163,7 +16356,7 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE { | ... | @@ -16163,7 +16356,7 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE { |
| 16163 | )) { | 16356 | )) { |
| 16164 | .SUCCESS => { | 16357 | .SUCCESS => { |
| 16165 | syscall.finish(); | 16358 | syscall.finish(); |
| 16166 | mutexLock(&t.mutex); // Another thread might have won the race. | 16359 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 16167 | defer mutexUnlock(&t.mutex); | 16360 | defer mutexUnlock(&t.mutex); |
| 16168 | if (t.null_file.handle) |prev_handle| { | 16361 | if (t.null_file.handle) |prev_handle| { |
| 16169 | windows.CloseHandle(fresh_handle); | 16362 | windows.CloseHandle(fresh_handle); |
| ... | @@ -16196,7 +16389,7 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE { | ... | @@ -16196,7 +16389,7 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE { |
| 16196 | 16389 | ||
| 16197 | fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { | 16390 | fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { |
| 16198 | { | 16391 | { |
| 16199 | mutexLock(&t.mutex); | 16392 | try mutexLock(&t.mutex); |
| 16200 | defer mutexUnlock(&t.mutex); | 16393 | defer mutexUnlock(&t.mutex); |
| 16201 | if (t.pipe_file.handle) |handle| return handle; | 16394 | if (t.pipe_file.handle) |handle| return handle; |
| 16202 | } | 16395 | } |
| ... | @@ -16218,7 +16411,7 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { | ... | @@ -16218,7 +16411,7 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { |
| 16218 | )) { | 16411 | )) { |
| 16219 | .SUCCESS => { | 16412 | .SUCCESS => { |
| 16220 | syscall.finish(); | 16413 | syscall.finish(); |
| 16221 | mutexLock(&t.mutex); // Another thread might have won the race. | 16414 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 16222 | defer mutexUnlock(&t.mutex); | 16415 | defer mutexUnlock(&t.mutex); |
| 16223 | if (t.pipe_file.handle) |prev_handle| { | 16416 | if (t.pipe_file.handle) |prev_handle| { |
| 16224 | windows.CloseHandle(fresh_handle); | 16417 | windows.CloseHandle(fresh_handle); |
| ... | @@ -16503,7 +16696,15 @@ fn windowsCreateProcessPathExt( | ... | @@ -16503,7 +16696,15 @@ fn windowsCreateProcessPathExt( |
| 16503 | else | 16696 | else |
| 16504 | full_app_name; | 16697 | full_app_name; |
| 16505 | 16698 | ||
| 16506 | if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, env_block, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| { | 16699 | if (windowsCreateProcess( |
| 16700 | app_name_w.ptr, | ||
| 16701 | cmd_line_w.ptr, | ||
| 16702 | env_block, | ||
| 16703 | cwd_ptr, | ||
| 16704 | flags, | ||
| 16705 | lpStartupInfo, | ||
| 16706 | lpProcessInformation, | ||
| 16707 | )) |_| { | ||
| 16507 | return; | 16708 | return; |
| 16508 | } else |err| switch (err) { | 16709 | } else |err| switch (err) { |
| 16509 | error.FileNotFound => continue, | 16710 | error.FileNotFound => continue, |
| ... | @@ -17037,61 +17238,112 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c | ... | @@ -17037,61 +17238,112 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c |
| 17037 | try std.testing.expectEqualStrings(expected_cmd_line, cmd_line); | 17238 | try std.testing.expectEqualStrings(expected_cmd_line, cmd_line); |
| 17038 | } | 17239 | } |
| 17039 | 17240 | ||
| 17040 | fn posixExecv( | 17241 | fn posixExec( |
| 17041 | arg0_expand: process.ArgExpansion, | 17242 | exe: process.ReplaceOptions.Exe, |
| 17042 | file: [*:0]const u8, | 17243 | argv: [*:null]?[*:0]const u8, |
| 17043 | child_argv: [*:null]?[*:0]const u8, | 17244 | expand_arg0: process.ArgExpansion, |
| 17044 | env_block: process.Environ.PosixBlock, | 17245 | env_block: process.Environ.PosixBlock, |
| 17045 | PATH: []const u8, | 17246 | PATH: []const u8, |
| 17046 | ) process.ReplaceError { | 17247 | ) process.ReplaceError { |
| 17047 | const file_slice = std.mem.sliceTo(file, 0); | 17248 | const arg0_slice = std.mem.span(argv[0].?); |
| 17048 | if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, env_block); | 17249 | exe: switch (exe) { |
| 17049 | 17250 | .detect => continue :exe if (std.mem.findScalar(u8, arg0_slice, '/')) |_| .{ | |
| 17050 | // Use of PATH_MAX here is valid as the path_buf will be passed | 17251 | .path = .cwd(), |
| 17051 | // directly to the operating system in posixExecvPath. | 17252 | } else .search, |
| 17052 | var path_buf: [posix.PATH_MAX]u8 = undefined; | 17253 | .search => { |
| 17053 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); | 17254 | if (Dir.path.isAbsolute(arg0_slice)) continue :exe .{ .path = .cwd() }; |
| 17054 | var seen_eacces = false; | 17255 | // Use of PATH_MAX here is valid as the path_buf will be passed |
| 17055 | var err: process.ReplaceError = error.FileNotFound; | 17256 | // directly to the operating system in posixExecveat. |
| 17056 | 17257 | var path_buf: [posix.PATH_MAX]u8 = undefined; | |
| 17057 | // In case of expanding arg0 we must put it back if we return with an error. | 17258 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); |
| 17058 | const prev_arg0 = child_argv[0]; | 17259 | var err: error{ AccessDenied, FileNotFound, NotDir } = error.FileNotFound; |
| 17059 | defer switch (arg0_expand) { | ||
| 17060 | .expand => child_argv[0] = prev_arg0, | ||
| 17061 | .no_expand => {}, | ||
| 17062 | }; | ||
| 17063 | 17260 | ||
| 17064 | while (it.next()) |search_path| { | 17261 | while (it.next()) |search_path| { |
| 17065 | const path_len = search_path.len + file_slice.len + 1; | 17262 | const path_len = search_path.len + arg0_slice.len + 1; |
| 17066 | if (path_buf.len < path_len + 1) return error.NameTooLong; | 17263 | if (path_buf.len < path_len + 1) return error.NameTooLong; |
| 17067 | @memcpy(path_buf[0..search_path.len], search_path); | 17264 | @memcpy(path_buf[0..search_path.len], search_path); |
| 17068 | path_buf[search_path.len] = '/'; | 17265 | path_buf[search_path.len] = Dir.path.sep; |
| 17069 | @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice); | 17266 | @memcpy(path_buf[search_path.len + 1 ..][0..arg0_slice.len], arg0_slice); |
| 17070 | path_buf[path_len] = 0; | 17267 | path_buf[path_len] = 0; |
| 17071 | const full_path = path_buf[0..path_len :0].ptr; | 17268 | const full_path = path_buf[0..path_len :0].ptr; |
| 17072 | switch (arg0_expand) { | 17269 | switch (expand_arg0) { |
| 17073 | .expand => child_argv[0] = full_path, | 17270 | .expand => argv[0] = full_path, |
| 17074 | .no_expand => {}, | 17271 | .no_expand => {}, |
| 17075 | } | 17272 | } |
| 17076 | err = posixExecvPath(full_path, child_argv, env_block); | 17273 | switch (posixExecveat(posix.AT.FDCWD, full_path, argv, env_block)) { |
| 17077 | switch (err) { | 17274 | error.AccessDenied, error.FileNotFound, error.NotDir => |e| switch (err) { |
| 17078 | error.AccessDenied => seen_eacces = true, | 17275 | error.AccessDenied => {}, |
| 17079 | error.FileNotFound, error.NotDir => {}, | 17276 | error.FileNotFound, error.NotDir => err = e, |
| 17080 | else => |e| return e, | 17277 | }, |
| 17081 | } | 17278 | else => |e| return e, |
| 17279 | } | ||
| 17280 | } | ||
| 17281 | return err; | ||
| 17282 | }, | ||
| 17283 | .path => |path| return posixExecveat(path.handle, arg0_slice, argv, env_block), | ||
| 17284 | .file => |file| return posixExecveat(file.handle, null, argv, env_block), | ||
| 17285 | .explicit => |explicit| { | ||
| 17286 | var path_buffer: [posix.PATH_MAX]u8 = undefined; | ||
| 17287 | const path_posix = try pathToPosix(explicit.path, &path_buffer); | ||
| 17288 | return posixExecveat(explicit.dir.handle, path_posix, argv, env_block); | ||
| 17289 | }, | ||
| 17082 | } | 17290 | } |
| 17083 | if (seen_eacces) return error.AccessDenied; | ||
| 17084 | return err; | ||
| 17085 | } | 17291 | } |
| 17086 | 17292 | ||
| 17087 | /// This function ignores PATH environment variable. | 17293 | /// This function ignores PATH environment variable. |
| 17088 | pub fn posixExecvPath( | 17294 | pub fn posixExecveat( |
| 17089 | path: [*:0]const u8, | 17295 | dir: posix.fd_t, |
| 17090 | child_argv: [*:null]const ?[*:0]const u8, | 17296 | path: ?[*:0]const u8, |
| 17297 | argv: [*:null]const ?[*:0]const u8, | ||
| 17091 | env_block: process.Environ.PosixBlock, | 17298 | env_block: process.Environ.PosixBlock, |
| 17092 | ) process.ReplaceError { | 17299 | ) process.ReplaceError { |
| 17093 | try Thread.checkCancel(); | 17300 | try Thread.checkCancel(); |
| 17094 | switch (posix.errno(posix.system.execve(path, child_argv, env_block.slice.ptr))) { | 17301 | switch (err: { |
| 17302 | if (path) |p| if (dir == posix.AT.FDCWD or Dir.path.isAbsoluteZ(p)) { | ||
| 17303 | break :err posix.errno(posix.system.execve(p, argv, env_block.slice.ptr)); | ||
| 17304 | }; | ||
| 17305 | if (comptime native_os == .linux and (!builtin.link_libc or | ||
| 17306 | (builtin.target.isGnuLibC() and builtin.target.os.version_range.linux.glibc.order( | ||
| 17307 | .{ .major = 2, .minor = 34, .patch = 0 }, | ||
| 17308 | ).compare(.gte)))) | ||
| 17309 | { | ||
| 17310 | switch (posix.errno(posix.system.execveat(dir, path orelse "", argv, env_block.slice.ptr, .{ | ||
| 17311 | .EMPTY_PATH = path == null, | ||
| 17312 | .SYMLINK_NOFOLLOW = false, | ||
| 17313 | }))) { | ||
| 17314 | else => |err| break :err err, | ||
| 17315 | .NOSYS => {}, | ||
| 17316 | } | ||
| 17317 | } | ||
| 17318 | var path_buffer: [posix.PATH_MAX]u8 = undefined; | ||
| 17319 | var path_len = realPathPosix(dir, &path_buffer) catch |err| switch (err) { | ||
| 17320 | else => |e| return e, | ||
| 17321 | error.InputOutput, | ||
| 17322 | error.FileTooBig, | ||
| 17323 | error.NoSpaceLeft, | ||
| 17324 | error.PathAlreadyExists, | ||
| 17325 | error.SymLinkLoop, | ||
| 17326 | error.NetworkNotFound, | ||
| 17327 | error.PipeBusy, | ||
| 17328 | error.AntivirusInterference, | ||
| 17329 | => return error.FileNotFound, | ||
| 17330 | error.DeviceBusy, | ||
| 17331 | error.NoDevice, | ||
| 17332 | error.UnrecognizedVolume, | ||
| 17333 | => return error.FileSystem, | ||
| 17334 | }; | ||
| 17335 | if (path) |p| { | ||
| 17336 | const path_slice = std.mem.span(p); | ||
| 17337 | path_buffer[path_len] = Dir.path.sep; | ||
| 17338 | path_len += 1; | ||
| 17339 | @memcpy(path_buffer[path_len..][0..path_slice.len], path_slice); | ||
| 17340 | path_len += path_slice.len; | ||
| 17341 | } | ||
| 17342 | path_buffer[path_len] = 0; | ||
| 17343 | break :err posix.errno( | ||
| 17344 | posix.system.execve(path_buffer[0..path_len :0], argv, env_block.slice.ptr), | ||
| 17345 | ); | ||
| 17346 | }) { | ||
| 17095 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. | 17347 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. |
| 17096 | .@"2BIG" => return error.SystemResources, | 17348 | .@"2BIG" => return error.SystemResources, |
| 17097 | .MFILE => return error.ProcessFdQuotaExceeded, | 17349 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | @@ -17123,6 +17375,334 @@ pub fn posixExecvPath( | ... | @@ -17123,6 +17375,334 @@ pub fn posixExecvPath( |
| 17123 | } | 17375 | } |
| 17124 | } | 17376 | } |
| 17125 | 17377 | ||
| 17378 | fn spawnDarwin( | ||
| 17379 | t: *Threaded, | ||
| 17380 | mode: enum { spawn, replace }, | ||
| 17381 | exe: process.ReplaceOptions.Exe, | ||
| 17382 | argv: [*:null]?[*:0]const u8, | ||
| 17383 | cwd: process.Child.Cwd, | ||
| 17384 | env_block: process.Environ.PosixBlock, | ||
| 17385 | expand_arg0: process.ArgExpansion, | ||
| 17386 | progress_node: std.Progress.Node, | ||
| 17387 | stdin: process.SpawnOptions.StdIo, | ||
| 17388 | stdout: process.SpawnOptions.StdIo, | ||
| 17389 | stderr: process.SpawnOptions.StdIo, | ||
| 17390 | inherit_dirs: []const Dir, | ||
| 17391 | inherit_files: []const File, | ||
| 17392 | pgid: ?posix.pid_t, | ||
| 17393 | start_suspended: bool, | ||
| 17394 | PATH: []const u8, | ||
| 17395 | ) (process.SpawnError || process.ReplaceError)!Spawned { | ||
| 17396 | var fa: std.c.posix_spawn_file_actions_t = undefined; | ||
| 17397 | switch (@as(std.c.E, @fromBackingInt(@intCast(std.c.posix_spawn_file_actions_init(&fa))))) { | ||
| 17398 | .SUCCESS => {}, | ||
| 17399 | .NOMEM => return error.SystemResources, | ||
| 17400 | else => |err| return posix.unexpectedErrno(err), | ||
| 17401 | } | ||
| 17402 | defer switch (@as(std.c.E, @fromBackingInt(@intCast( | ||
| 17403 | std.c.posix_spawn_file_actions_destroy(&fa), | ||
| 17404 | )))) { | ||
| 17405 | .SUCCESS => {}, | ||
| 17406 | .INVAL => recoverableOsBugDetected(), | ||
| 17407 | else => recoverableOsBugDetected(), | ||
| 17408 | }; | ||
| 17409 | |||
| 17410 | var attr: std.c.posix_spawnattr_t = undefined; | ||
| 17411 | switch (@as(std.c.E, @fromBackingInt(@intCast(std.c.posix_spawnattr_init(&attr))))) { | ||
| 17412 | .SUCCESS => {}, | ||
| 17413 | .NOMEM => return error.SystemResources, | ||
| 17414 | else => |err| return posix.unexpectedErrno(err), | ||
| 17415 | } | ||
| 17416 | defer switch (@as(std.c.E, @fromBackingInt(@intCast(std.c.posix_spawnattr_destroy(&attr))))) { | ||
| 17417 | .SUCCESS => {}, | ||
| 17418 | .INVAL => recoverableOsBugDetected(), | ||
| 17419 | else => recoverableOsBugDetected(), | ||
| 17420 | }; | ||
| 17421 | |||
| 17422 | // The child process does need to access (one end of) these pipes. However, | ||
| 17423 | // we must initially set CLOEXEC to avoid a race condition. If another thread | ||
| 17424 | // is racing to spawn a different child process, we don't want it to inherit | ||
| 17425 | // these FDs in any scenario; that would mean that, for instance, calls to | ||
| 17426 | // `poll` from the parent would not report the child's stdout as closing when | ||
| 17427 | // expected, since the other child may retain a reference to the write end of | ||
| 17428 | // the pipe. So, we create the pipes with CLOEXEC initially. After fork, we | ||
| 17429 | // need to do something in the new child to make sure we preserve the reference | ||
| 17430 | // we want. We could use `fcntl` to remove CLOEXEC from the FD, but as it | ||
| 17431 | // turns out, we `dup2` everything anyway, so there's no need! | ||
| 17432 | const pipe_flags: posix.O = .{ .CLOEXEC = true }; | ||
| 17433 | |||
| 17434 | const stdin_pipe = if (stdin == .pipe) try pipe2(pipe_flags) else undefined; | ||
| 17435 | errdefer if (stdin == .pipe) { | ||
| 17436 | destroyPipe(stdin_pipe); | ||
| 17437 | }; | ||
| 17438 | |||
| 17439 | const stdout_pipe = if (stdout == .pipe) try pipe2(pipe_flags) else undefined; | ||
| 17440 | errdefer if (stdout == .pipe) { | ||
| 17441 | destroyPipe(stdout_pipe); | ||
| 17442 | }; | ||
| 17443 | |||
| 17444 | const stderr_pipe = if (stderr == .pipe) try pipe2(pipe_flags) else undefined; | ||
| 17445 | errdefer if (stderr == .pipe) { | ||
| 17446 | destroyPipe(stderr_pipe); | ||
| 17447 | }; | ||
| 17448 | |||
| 17449 | const any_ignore = (stdin == .ignore or stdout == .ignore or stderr == .ignore); | ||
| 17450 | const dev_null_fd = if (any_ignore) try t.getDevNullFd() else undefined; | ||
| 17451 | |||
| 17452 | const prog_pipe: [2]posix.fd_t = if (progress_node.index != .none) pipe: { | ||
| 17453 | // We use CLOEXEC for the same reason as in `pipe_flags`. | ||
| 17454 | break :pipe try pipe2(.{ .NONBLOCK = true, .CLOEXEC = true }); | ||
| 17455 | } else .{ -1, -1 }; | ||
| 17456 | errdefer destroyPipe(prog_pipe); | ||
| 17457 | |||
| 17458 | var cwd_buffer: [posix.PATH_MAX]u8 = undefined; | ||
| 17459 | switch (cwd) { | ||
| 17460 | .inherit => {}, | ||
| 17461 | .dir => |dir| switch (@as(std.c.E, @fromBackingInt(@intCast( | ||
| 17462 | std.c.posix_spawn_file_actions_addfchdir_np(&fa, dir.handle), | ||
| 17463 | )))) { | ||
| 17464 | .SUCCESS => {}, | ||
| 17465 | .BADF => |err| return errnoBug(err), | ||
| 17466 | .INVAL => |err| return errnoBug(err), | ||
| 17467 | .NOMEM => return error.SystemResources, | ||
| 17468 | else => |err| return posix.unexpectedErrno(err), | ||
| 17469 | }, | ||
| 17470 | .path => |path| switch (@as(std.c.E, @fromBackingInt(@intCast( | ||
| 17471 | std.c.posix_spawn_file_actions_addchdir_np(&fa, try pathToPosix(path, &cwd_buffer)), | ||
| 17472 | )))) { | ||
| 17473 | .SUCCESS => {}, | ||
| 17474 | .BADF => |err| return errnoBug(err), | ||
| 17475 | .INVAL => |err| return errnoBug(err), | ||
| 17476 | .NAMETOOLONG => return error.NameTooLong, | ||
| 17477 | .NOMEM => return error.SystemResources, | ||
| 17478 | else => |err| return posix.unexpectedErrno(err), | ||
| 17479 | }, | ||
| 17480 | } | ||
| 17481 | |||
| 17482 | for (inherit_dirs) |inherit_dir| switch (@as(std.c.E, @fromBackingInt(@intCast( | ||
| 17483 | std.c.posix_spawn_file_actions_addinherit_np(&fa, inherit_dir.handle), | ||
| 17484 | )))) { | ||
| 17485 | .SUCCESS => {}, | ||
| 17486 | .BADF => |err| return errnoBug(err), | ||
| 17487 | .INVAL => |err| return errnoBug(err), | ||
| 17488 | .NOMEM => return error.SystemResources, | ||
| 17489 | else => |err| return posix.unexpectedErrno(err), | ||
| 17490 | }; | ||
| 17491 | for (inherit_files) |inherit_file| switch (@as(std.c.E, @fromBackingInt(@intCast( | ||
| 17492 | std.c.posix_spawn_file_actions_addinherit_np(&fa, inherit_file.handle), | ||
| 17493 | )))) { | ||
| 17494 | .SUCCESS => {}, | ||
| 17495 | .BADF => |err| return errnoBug(err), | ||
| 17496 | .INVAL => |err| return errnoBug(err), | ||
| 17497 | .NOMEM => return error.SystemResources, | ||
| 17498 | else => |err| return posix.unexpectedErrno(err), | ||
| 17499 | }; | ||
| 17500 | if (prog_pipe[1] != -1) switch (@as(std.c.E, @fromBackingInt(@intCast( | ||
| 17501 | std.c.posix_spawn_file_actions_addinherit_np(&fa, prog_pipe[1]), | ||
| 17502 | )))) { | ||
| 17503 | .SUCCESS => {}, | ||
| 17504 | .BADF => |err| return errnoBug(err), | ||
| 17505 | .INVAL => |err| return errnoBug(err), | ||
| 17506 | .NOMEM => return error.SystemResources, | ||
| 17507 | else => |err| return posix.unexpectedErrno(err), | ||
| 17508 | }; | ||
| 17509 | for ( | ||
| 17510 | [_]process.SpawnOptions.StdIo{ stdin, stdout, stderr }, | ||
| 17511 | [_]posix.fd_t{ stdin_pipe[0], stdout_pipe[1], stderr_pipe[1] }, | ||
| 17512 | [_]posix.fd_t{ posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO }, | ||
| 17513 | ) |stdio, pipe_fd, std_fd| switch (@as(std.c.E, @fromBackingInt(@intCast(switch (stdio) { | ||
| 17514 | .pipe => std.c.posix_spawn_file_actions_adddup2(&fa, pipe_fd, std_fd), | ||
| 17515 | .close => std.c.posix_spawn_file_actions_addclose(&fa, std_fd), | ||
| 17516 | .inherit => std.c.posix_spawn_file_actions_addinherit_np(&fa, std_fd), | ||
| 17517 | .ignore => std.c.posix_spawn_file_actions_adddup2(&fa, dev_null_fd, std_fd), | ||
| 17518 | .file => |file| std.c.posix_spawn_file_actions_adddup2(&fa, file.handle, std_fd), | ||
| 17519 | })))) { | ||
| 17520 | .SUCCESS => {}, | ||
| 17521 | .BADF => |err| return errnoBug(err), | ||
| 17522 | .INVAL => |err| return errnoBug(err), | ||
| 17523 | .NOMEM => return error.SystemResources, | ||
| 17524 | else => |err| return posix.unexpectedErrno(err), | ||
| 17525 | }; | ||
| 17526 | |||
| 17527 | if (pgid) |pid| switch (@as(std.c.E, @fromBackingInt(@intCast( | ||
| 17528 | std.c.posix_spawnattr_setpgroup(&attr, pid), | ||
| 17529 | )))) { | ||
| 17530 | .SUCCESS => {}, | ||
| 17531 | .INVAL => return error.InvalidProcessGroupId, | ||
| 17532 | else => |err| return posix.unexpectedErrno(err), | ||
| 17533 | }; | ||
| 17534 | |||
| 17535 | switch (@as(std.c.E, @fromBackingInt(@intCast(std.c.posix_spawnattr_setflags(&attr, .{ | ||
| 17536 | .SETPGROUP = pgid != null, | ||
| 17537 | .SETEXEC = switch (mode) { | ||
| 17538 | .spawn => false, | ||
| 17539 | .replace => true, | ||
| 17540 | }, | ||
| 17541 | .START_SUSPENDED = start_suspended, | ||
| 17542 | }))))) { | ||
| 17543 | .SUCCESS => {}, | ||
| 17544 | .INVAL => |err| return errnoBug(err), | ||
| 17545 | else => |err| return posix.unexpectedErrno(err), | ||
| 17546 | } | ||
| 17547 | |||
| 17548 | var pid: posix.pid_t = undefined; | ||
| 17549 | const arg0_slice = std.mem.span(argv[0].?); | ||
| 17550 | var path_buffer: [posix.PATH_MAX]u8 = undefined; | ||
| 17551 | switch (err: { | ||
| 17552 | break :err @as(std.c.E, @fromBackingInt(@intCast(exe: switch (exe) { | ||
| 17553 | .detect => if (std.mem.findScalar(u8, arg0_slice, '/')) |_| | ||
| 17554 | std.c.posix_spawn(&pid, arg0_slice, &fa, &attr, argv, env_block.slice.ptr) | ||
| 17555 | else | ||
| 17556 | continue :exe .search, | ||
| 17557 | .search => if (!Dir.path.isAbsolute(arg0_slice)) { | ||
| 17558 | // Use of PATH_MAX here is valid as the path_buf will be passed | ||
| 17559 | // directly to the operating system in posixExecveat. | ||
| 17560 | var path_buf: [posix.PATH_MAX]u8 = undefined; | ||
| 17561 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); | ||
| 17562 | var err: error{ AccessDenied, FileNotFound, NotDir } = error.FileNotFound; | ||
| 17563 | |||
| 17564 | while (it.next()) |search_path| { | ||
| 17565 | const path_len = search_path.len + arg0_slice.len + 1; | ||
| 17566 | if (path_buf.len < path_len + 1) return error.NameTooLong; | ||
| 17567 | @memcpy(path_buf[0..search_path.len], search_path); | ||
| 17568 | path_buf[search_path.len] = Dir.path.sep; | ||
| 17569 | @memcpy(path_buf[search_path.len + 1 ..][0..arg0_slice.len], arg0_slice); | ||
| 17570 | path_buf[path_len] = 0; | ||
| 17571 | const full_path = path_buf[0..path_len :0].ptr; | ||
| 17572 | switch (expand_arg0) { | ||
| 17573 | .expand => argv[0] = full_path, | ||
| 17574 | .no_expand => {}, | ||
| 17575 | } | ||
| 17576 | switch (@as(std.c.E, @fromBackingInt(@intCast( | ||
| 17577 | std.c.posix_spawn(&pid, full_path, &fa, &attr, argv, env_block.slice.ptr), | ||
| 17578 | )))) { | ||
| 17579 | else => |e| break :err e, | ||
| 17580 | .ACCES, .NOENT, .NOTDIR => |e| switch (err) { | ||
| 17581 | error.AccessDenied => {}, | ||
| 17582 | error.FileNotFound, error.NotDir => err = switch (e) { | ||
| 17583 | else => unreachable, | ||
| 17584 | .ACCES => error.AccessDenied, | ||
| 17585 | .NOENT => error.FileNotFound, | ||
| 17586 | .NOTDIR => error.NotDir, | ||
| 17587 | }, | ||
| 17588 | }, | ||
| 17589 | } | ||
| 17590 | } | ||
| 17591 | return err; | ||
| 17592 | } else std.c.posix_spawn(&pid, arg0_slice, &fa, &attr, argv, env_block.slice.ptr), | ||
| 17593 | .path => |path| if (path.handle != posix.AT.FDCWD and !Dir.path.isAbsolute(arg0_slice)) { | ||
| 17594 | var path_len = realPathPosix(path.handle, &path_buffer) catch |err| switch (err) { | ||
| 17595 | else => |e| return e, | ||
| 17596 | error.InputOutput => return error.FileNotFound, | ||
| 17597 | }; | ||
| 17598 | path_buffer[path_len] = Dir.path.sep; | ||
| 17599 | path_len += 1; | ||
| 17600 | @memcpy(path_buffer[path_len..][0..arg0_slice.len], arg0_slice); | ||
| 17601 | path_len += arg0_slice.len; | ||
| 17602 | path_buffer[path_len] = 0; | ||
| 17603 | break :exe std.c.posix_spawn( | ||
| 17604 | &pid, | ||
| 17605 | path_buffer[0..path_len :0], | ||
| 17606 | &fa, | ||
| 17607 | &attr, | ||
| 17608 | argv, | ||
| 17609 | env_block.slice.ptr, | ||
| 17610 | ); | ||
| 17611 | } else std.c.posix_spawn(&pid, arg0_slice, &fa, &attr, argv, env_block.slice.ptr), | ||
| 17612 | .file => |file| { | ||
| 17613 | const path_len = realPathPosix(file.handle, &path_buffer) catch |err| switch (err) { | ||
| 17614 | else => |e| return e, | ||
| 17615 | error.InputOutput => return error.FileNotFound, | ||
| 17616 | }; | ||
| 17617 | path_buffer[path_len] = 0; | ||
| 17618 | break :exe std.c.posix_spawn( | ||
| 17619 | &pid, | ||
| 17620 | path_buffer[0..path_len :0], | ||
| 17621 | &fa, | ||
| 17622 | &attr, | ||
| 17623 | argv, | ||
| 17624 | env_block.slice.ptr, | ||
| 17625 | ); | ||
| 17626 | }, | ||
| 17627 | .explicit => |explicit| if (explicit.dir.handle != posix.AT.FDCWD and | ||
| 17628 | !Dir.path.isAbsolute(explicit.path)) | ||
| 17629 | { | ||
| 17630 | var path_len = realPathPosix( | ||
| 17631 | explicit.dir.handle, | ||
| 17632 | &path_buffer, | ||
| 17633 | ) catch |err| switch (err) { | ||
| 17634 | else => |e| return e, | ||
| 17635 | error.InputOutput => return error.FileNotFound, | ||
| 17636 | }; | ||
| 17637 | path_buffer[path_len] = Dir.path.sep; | ||
| 17638 | path_len += 1; | ||
| 17639 | @memcpy(path_buffer[path_len..][0..explicit.path.len], explicit.path); | ||
| 17640 | path_len += explicit.path.len; | ||
| 17641 | path_buffer[path_len] = 0; | ||
| 17642 | break :exe std.c.posix_spawn( | ||
| 17643 | &pid, | ||
| 17644 | path_buffer[0..path_len :0], | ||
| 17645 | &fa, | ||
| 17646 | &attr, | ||
| 17647 | argv, | ||
| 17648 | env_block.slice.ptr, | ||
| 17649 | ); | ||
| 17650 | } else std.c.posix_spawn( | ||
| 17651 | &pid, | ||
| 17652 | try pathToPosix(explicit.path, &path_buffer), | ||
| 17653 | &fa, | ||
| 17654 | &attr, | ||
| 17655 | argv, | ||
| 17656 | env_block.slice.ptr, | ||
| 17657 | ), | ||
| 17658 | }))); | ||
| 17659 | }) { | ||
| 17660 | .SUCCESS => {}, | ||
| 17661 | .AGAIN => return error.SystemResources, | ||
| 17662 | .INVAL => |err| return errnoBug(err), | ||
| 17663 | .@"2BIG" => return error.SystemResources, | ||
| 17664 | .ACCES => return error.AccessDenied, | ||
| 17665 | .FAULT => |err| return errnoBug(err), | ||
| 17666 | .IO => return error.FileSystem, | ||
| 17667 | .LOOP => return error.FileSystem, | ||
| 17668 | .NAMETOOLONG => return error.NameTooLong, | ||
| 17669 | .NOENT => return error.FileNotFound, | ||
| 17670 | .NOEXEC => return error.InvalidExe, | ||
| 17671 | .NOMEM => return error.SystemResources, | ||
| 17672 | .NOTDIR => return error.NotDir, | ||
| 17673 | .TXTBSY => return error.FileBusy, | ||
| 17674 | .BADARCH => return error.InvalidExe, | ||
| 17675 | .BADF => |err| return errnoBug(err), | ||
| 17676 | else => |err| return posix.unexpectedErrno(err), | ||
| 17677 | } | ||
| 17678 | |||
| 17679 | errdefer comptime unreachable; // The child is spawned; we must not error from now on | ||
| 17680 | |||
| 17681 | if (stdin == .pipe) closeFd(stdin_pipe[0]); | ||
| 17682 | if (stdout == .pipe) closeFd(stdout_pipe[1]); | ||
| 17683 | if (stderr == .pipe) closeFd(stderr_pipe[1]); | ||
| 17684 | |||
| 17685 | if (prog_pipe[1] != -1) closeFd(prog_pipe[1]); | ||
| 17686 | progress_node.setIpcFile(t, .{ .handle = prog_pipe[0], .flags = .{ .nonblocking = true } }); | ||
| 17687 | |||
| 17688 | return .{ | ||
| 17689 | .pid = pid, | ||
| 17690 | .err_fd = -1, | ||
| 17691 | .stdin = switch (stdin) { | ||
| 17692 | .pipe => .{ .handle = stdin_pipe[1], .flags = .{ .nonblocking = false } }, | ||
| 17693 | else => null, | ||
| 17694 | }, | ||
| 17695 | .stdout = switch (stdout) { | ||
| 17696 | .pipe => .{ .handle = stdout_pipe[0], .flags = .{ .nonblocking = false } }, | ||
| 17697 | else => null, | ||
| 17698 | }, | ||
| 17699 | .stderr = switch (stderr) { | ||
| 17700 | .pipe => .{ .handle = stderr_pipe[0], .flags = .{ .nonblocking = false } }, | ||
| 17701 | else => null, | ||
| 17702 | }, | ||
| 17703 | }; | ||
| 17704 | } | ||
| 17705 | |||
| 17126 | pub const CreatePipeOptions = struct { | 17706 | pub const CreatePipeOptions = struct { |
| 17127 | server: End, | 17707 | server: End, |
| 17128 | client: End, | 17708 | client: End, |
| ... | @@ -17224,6 +17804,53 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { | ... | @@ -17224,6 +17804,53 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { |
| 17224 | return t.environ.zig_progress_file; | 17804 | return t.environ.zig_progress_file; |
| 17225 | } | 17805 | } |
| 17226 | 17806 | ||
| 17807 | fn inheritParentHandle(handle: posix.fd_t) Io.InheritParentHandleError!void { | ||
| 17808 | switch (native_os) { | ||
| 17809 | .linux, | ||
| 17810 | .dragonfly, | ||
| 17811 | .freebsd, | ||
| 17812 | .netbsd, | ||
| 17813 | .openbsd, | ||
| 17814 | .illumos, | ||
| 17815 | .driverkit, | ||
| 17816 | .ios, | ||
| 17817 | .maccatalyst, | ||
| 17818 | .macos, | ||
| 17819 | .tvos, | ||
| 17820 | .visionos, | ||
| 17821 | .watchos, | ||
| 17822 | => return setCloexec(handle), | ||
| 17823 | .windows => switch (windows.ntdll.NtSetInformationObject( | ||
| 17824 | handle, | ||
| 17825 | .HandleFlag, | ||
| 17826 | &windows.OBJECT.HANDLE_FLAG{ .INHERIT = false }, | ||
| 17827 | @sizeOf(windows.OBJECT.HANDLE_FLAG), | ||
| 17828 | )) { | ||
| 17829 | .SUCCESS => return, | ||
| 17830 | else => |status| return windows.unexpectedStatus(status), | ||
| 17831 | }, | ||
| 17832 | else => return error.UnsupportedOperation, | ||
| 17833 | } | ||
| 17834 | } | ||
| 17835 | |||
| 17836 | fn inheritParentDir(userdata: ?*anyopaque, handle: Dir.Handle) Io.InheritParentHandleError!Dir { | ||
| 17837 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | ||
| 17838 | _ = t; | ||
| 17839 | try inheritParentHandle(handle); | ||
| 17840 | return .{ .handle = handle }; | ||
| 17841 | } | ||
| 17842 | |||
| 17843 | fn inheritParentFile( | ||
| 17844 | userdata: ?*anyopaque, | ||
| 17845 | handle: File.Handle, | ||
| 17846 | flags: File.Flags, | ||
| 17847 | ) Io.InheritParentHandleError!File { | ||
| 17848 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | ||
| 17849 | _ = t; | ||
| 17850 | try inheritParentHandle(handle); | ||
| 17851 | return .{ .handle = handle, .flags = flags }; | ||
| 17852 | } | ||
| 17853 | |||
| 17227 | pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 { | 17854 | pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 { |
| 17228 | t.scanEnviron(); | 17855 | t.scanEnviron(); |
| 17229 | return @field(t.environ.string, name); | 17856 | return @field(t.environ.string, name); |
| ... | @@ -17242,7 +17869,7 @@ fn random(userdata: ?*anyopaque, buffer: []u8) void { | ... | @@ -17242,7 +17869,7 @@ fn random(userdata: ?*anyopaque, buffer: []u8) void { |
| 17242 | } | 17869 | } |
| 17243 | 17870 | ||
| 17244 | fn randomMainThread(t: *Threaded, buffer: []u8) void { | 17871 | fn randomMainThread(t: *Threaded, buffer: []u8) void { |
| 17245 | mutexLock(&t.mutex); | 17872 | mutexLockUncancelable(&t.mutex); |
| 17246 | defer mutexUnlock(&t.mutex); | 17873 | defer mutexUnlock(&t.mutex); |
| 17247 | 17874 | ||
| 17248 | if (!t.csprng.isInitialized()) { | 17875 | if (!t.csprng.isInitialized()) { |
| ... | @@ -17250,7 +17877,7 @@ fn randomMainThread(t: *Threaded, buffer: []u8) void { | ... | @@ -17250,7 +17877,7 @@ fn randomMainThread(t: *Threaded, buffer: []u8) void { |
| 17250 | var seed: [Csprng.seed_len]u8 = undefined; | 17877 | var seed: [Csprng.seed_len]u8 = undefined; |
| 17251 | { | 17878 | { |
| 17252 | mutexUnlock(&t.mutex); | 17879 | mutexUnlock(&t.mutex); |
| 17253 | defer mutexLock(&t.mutex); | 17880 | defer mutexLockUncancelable(&t.mutex); |
| 17254 | 17881 | ||
| 17255 | const prev = swapCancelProtection(t, .blocked); | 17882 | const prev = swapCancelProtection(t, .blocked); |
| 17256 | defer _ = swapCancelProtection(t, prev); | 17883 | defer _ = swapCancelProtection(t, prev); |
| ... | @@ -17437,7 +18064,7 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { | ... | @@ -17437,7 +18064,7 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { |
| 17437 | 18064 | ||
| 17438 | fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { | 18065 | fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 17439 | { | 18066 | { |
| 17440 | mutexLock(&t.mutex); | 18067 | try mutexLock(&t.mutex); |
| 17441 | defer mutexUnlock(&t.mutex); | 18068 | defer mutexUnlock(&t.mutex); |
| 17442 | 18069 | ||
| 17443 | if (t.random_file.fd == -2) return error.EntropyUnavailable; | 18070 | if (t.random_file.fd == -2) return error.EntropyUnavailable; |
| ... | @@ -17478,7 +18105,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { | ... | @@ -17478,7 +18105,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 17478 | .SUCCESS => { | 18105 | .SUCCESS => { |
| 17479 | syscall.finish(); | 18106 | syscall.finish(); |
| 17480 | if (!statx.mask.TYPE) return error.EntropyUnavailable; | 18107 | if (!statx.mask.TYPE) return error.EntropyUnavailable; |
| 17481 | mutexLock(&t.mutex); // Another thread might have won the race. | 18108 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 17482 | defer mutexUnlock(&t.mutex); | 18109 | defer mutexUnlock(&t.mutex); |
| 17483 | if (t.random_file.fd >= 0) { | 18110 | if (t.random_file.fd >= 0) { |
| 17484 | closeFd(fd); | 18111 | closeFd(fd); |
| ... | @@ -17506,7 +18133,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { | ... | @@ -17506,7 +18133,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 17506 | switch (posix.errno(fstat_sym(fd, &stat))) { | 18133 | switch (posix.errno(fstat_sym(fd, &stat))) { |
| 17507 | .SUCCESS => { | 18134 | .SUCCESS => { |
| 17508 | syscall.finish(); | 18135 | syscall.finish(); |
| 17509 | mutexLock(&t.mutex); // Another thread might have won the race. | 18136 | mutexLockUncancelable(&t.mutex); // Another thread might have won the race. |
| 17510 | defer mutexUnlock(&t.mutex); | 18137 | defer mutexUnlock(&t.mutex); |
| 17511 | if (t.random_file.fd >= 0) { | 18138 | if (t.random_file.fd >= 0) { |
| 17512 | closeFd(fd); | 18139 | closeFd(fd); |
| ... | @@ -18434,7 +19061,7 @@ const CreateFileMapError = error{ | ... | @@ -18434,7 +19061,7 @@ const CreateFileMapError = error{ |
| 18434 | 19061 | ||
| 18435 | fn createFileMap( | 19062 | fn createFileMap( |
| 18436 | file: File, | 19063 | file: File, |
| 18437 | protection: std.process.MemoryProtection, | 19064 | protection: process.MemoryProtection, |
| 18438 | offset: u64, | 19065 | offset: u64, |
| 18439 | populate: bool, | 19066 | populate: bool, |
| 18440 | len: usize, | 19067 | len: usize, |
| ... | @@ -19062,7 +19689,7 @@ fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void { | ... | @@ -19062,7 +19689,7 @@ fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void { |
| 19062 | } | 19689 | } |
| 19063 | 19690 | ||
| 19064 | mutexUnlock(mutex); | 19691 | mutexUnlock(mutex); |
| 19065 | defer mutexLock(mutex); | 19692 | defer mutexLockUncancelable(mutex); |
| 19066 | 19693 | ||
| 19067 | while (true) { | 19694 | while (true) { |
| 19068 | Thread.futexWaitUncancelable(&cond.epoch.raw, epoch, null); | 19695 | Thread.futexWaitUncancelable(&cond.epoch.raw, epoch, null); |
| ... | @@ -19082,8 +19709,27 @@ fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void { | ... | @@ -19082,8 +19709,27 @@ fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void { |
| 19082 | } | 19709 | } |
| 19083 | } | 19710 | } |
| 19084 | 19711 | ||
| 19712 | /// Same as `Io.Mutex.lock` but avoids the VTable. | ||
| 19713 | pub fn mutexLock(m: *Io.Mutex) Io.Cancelable!void { | ||
| 19714 | const initial_state = m.state.cmpxchgStrong( | ||
| 19715 | .unlocked, | ||
| 19716 | .locked_once, | ||
| 19717 | .acquire, | ||
| 19718 | .monotonic, | ||
| 19719 | ) orelse { | ||
| 19720 | @branchHint(.likely); | ||
| 19721 | return; | ||
| 19722 | }; | ||
| 19723 | if (initial_state == .contended) { | ||
| 19724 | try Thread.futexWait(@ptrCast(&m.state.raw), @backingInt(Io.Mutex.State.contended), null); | ||
| 19725 | } | ||
| 19726 | while (m.state.swap(.contended, .acquire) != .unlocked) { | ||
| 19727 | try Thread.futexWait(@ptrCast(&m.state.raw), @backingInt(Io.Mutex.State.contended), null); | ||
| 19728 | } | ||
| 19729 | } | ||
| 19730 | |||
| 19085 | /// Same as `Io.Mutex.lockUncancelable` but avoids the VTable. | 19731 | /// Same as `Io.Mutex.lockUncancelable` but avoids the VTable. |
| 19086 | pub fn mutexLock(m: *Io.Mutex) void { | 19732 | pub fn mutexLockUncancelable(m: *Io.Mutex) void { |
| 19087 | const initial_state = m.state.cmpxchgStrong( | 19733 | const initial_state = m.state.cmpxchgStrong( |
| 19088 | .unlocked, | 19734 | .unlocked, |
| 19089 | .locked_once, | 19735 | .locked_once, |
lib/std/bit_set.zig+2-7| ... | @@ -723,13 +723,8 @@ pub const Dynamic = struct { | ... | @@ -723,13 +723,8 @@ pub const Dynamic = struct { |
| 723 | // That slot holds the size of the true allocation, which | 723 | // That slot holds the size of the true allocation, which |
| 724 | // is needed by Zig's allocator interface in case a shrink | 724 | // is needed by Zig's allocator interface in case a shrink |
| 725 | // fails. | 725 | // fails. |
| 726 | 726 | const empty_masks_data = [_]MaskInt{ 0, undefined }; | |
| 727 | // Don't modify this value. Ideally it would go in const data so | 727 | const empty_masks_ptr = @constCast(empty_masks_data[1..2]); |
| 728 | // modifications would cause a bus error, but the only way | ||
| 729 | // to discard a const qualifier is through intFromPtr, which | ||
| 730 | // cannot currently round trip at comptime. | ||
| 731 | var empty_masks_data = [_]MaskInt{ 0, undefined }; | ||
| 732 | const empty_masks_ptr = empty_masks_data[1..2]; | ||
| 733 | 728 | ||
| 734 | /// Creates a bit set with no elements present. | 729 | /// Creates a bit set with no elements present. |
| 735 | /// If bit_length is not zero, deinit must eventually be called. | 730 | /// If bit_length is not zero, deinit must eventually be called. |
lib/std/c.zig+8| ... | @@ -8809,6 +8809,11 @@ pub const O = switch (native_os) { | ... | @@ -8809,6 +8809,11 @@ pub const O = switch (native_os) { |
| 8809 | else => void, | 8809 | else => void, |
| 8810 | }; | 8810 | }; |
| 8811 | 8811 | ||
| 8812 | pub const EXECVEAT = switch (native_os) { | ||
| 8813 | .linux => linux.EXECVEAT, | ||
| 8814 | else => void, | ||
| 8815 | }; | ||
| 8816 | |||
| 8812 | pub const MAP = switch (native_os) { | 8817 | pub const MAP = switch (native_os) { |
| 8813 | .linux => linux.MAP, | 8818 | .linux => linux.MAP, |
| 8814 | .emscripten => packed struct(u32) { | 8819 | .emscripten => packed struct(u32) { |
| ... | @@ -11045,6 +11050,7 @@ pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, n | ... | @@ -11045,6 +11050,7 @@ pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, n |
| 11045 | pub extern "c" fn chdir(path: [*:0]const u8) c_int; | 11050 | pub extern "c" fn chdir(path: [*:0]const u8) c_int; |
| 11046 | pub extern "c" fn fchdir(fd: fd_t) c_int; | 11051 | pub extern "c" fn fchdir(fd: fd_t) c_int; |
| 11047 | pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; | 11052 | pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; |
| 11053 | pub extern "c" fn execveat(dirfd: fd_t, path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8, flags: EXECVEAT) c_int; | ||
| 11048 | pub extern "c" fn dup(fd: fd_t) c_int; | 11054 | pub extern "c" fn dup(fd: fd_t) c_int; |
| 11049 | pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int; | 11055 | pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int; |
| 11050 | pub extern "c" fn dup3(old: c_int, new: c_int, flags: c_uint) c_int; | 11056 | pub extern "c" fn dup3(old: c_int, new: c_int, flags: c_uint) c_int; |
| ... | @@ -11779,8 +11785,10 @@ pub const posix_spawn_file_actions_init = darwin.posix_spawn_file_actions_init; | ... | @@ -11779,8 +11785,10 @@ pub const posix_spawn_file_actions_init = darwin.posix_spawn_file_actions_init; |
| 11779 | pub const posix_spawn_file_actions_t = darwin.posix_spawn_file_actions_t; | 11785 | pub const posix_spawn_file_actions_t = darwin.posix_spawn_file_actions_t; |
| 11780 | pub const posix_spawnattr_destroy = darwin.posix_spawnattr_destroy; | 11786 | pub const posix_spawnattr_destroy = darwin.posix_spawnattr_destroy; |
| 11781 | pub const posix_spawnattr_getflags = darwin.posix_spawnattr_getflags; | 11787 | pub const posix_spawnattr_getflags = darwin.posix_spawnattr_getflags; |
| 11788 | pub const posix_spawnattr_getpgroup = darwin.posix_spawnattr_getpgroup; | ||
| 11782 | pub const posix_spawnattr_init = darwin.posix_spawnattr_init; | 11789 | pub const posix_spawnattr_init = darwin.posix_spawnattr_init; |
| 11783 | pub const posix_spawnattr_setflags = darwin.posix_spawnattr_setflags; | 11790 | pub const posix_spawnattr_setflags = darwin.posix_spawnattr_setflags; |
| 11791 | pub const posix_spawnattr_setpgroup = darwin.posix_spawnattr_setpgroup; | ||
| 11784 | pub const posix_spawnattr_t = darwin.posix_spawnattr_t; | 11792 | pub const posix_spawnattr_t = darwin.posix_spawnattr_t; |
| 11785 | pub const posix_spawnp = darwin.posix_spawnp; | 11793 | pub const posix_spawnp = darwin.posix_spawnp; |
| 11786 | pub const pthread_attr_get_qos_class_np = darwin.pthread_attr_get_qos_class_np; | 11794 | pub const pthread_attr_get_qos_class_np = darwin.pthread_attr_get_qos_class_np; |
lib/std/c/darwin.zig+3-1| ... | @@ -1253,8 +1253,10 @@ pub const posix_spawnattr_t = *opaque {}; | ... | @@ -1253,8 +1253,10 @@ pub const posix_spawnattr_t = *opaque {}; |
| 1253 | pub const posix_spawn_file_actions_t = *opaque {}; | 1253 | pub const posix_spawn_file_actions_t = *opaque {}; |
| 1254 | pub extern "c" fn posix_spawnattr_init(attr: *posix_spawnattr_t) c_int; | 1254 | pub extern "c" fn posix_spawnattr_init(attr: *posix_spawnattr_t) c_int; |
| 1255 | pub extern "c" fn posix_spawnattr_destroy(attr: *posix_spawnattr_t) c_int; | 1255 | pub extern "c" fn posix_spawnattr_destroy(attr: *posix_spawnattr_t) c_int; |
| 1256 | pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: POSIX_SPAWN) c_int; | ||
| 1257 | pub extern "c" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *POSIX_SPAWN) c_int; | 1256 | pub extern "c" fn posix_spawnattr_getflags(attr: *const posix_spawnattr_t, flags: *POSIX_SPAWN) c_int; |
| 1257 | pub extern "c" fn posix_spawnattr_getpgroup(attr: *const posix_spawnattr_t, pgroup: *pid_t) c_int; | ||
| 1258 | pub extern "c" fn posix_spawnattr_setflags(attr: *posix_spawnattr_t, flags: POSIX_SPAWN) c_int; | ||
| 1259 | pub extern "c" fn posix_spawnattr_setpgroup(attr: *posix_spawnattr_t, pgroup: pid_t) c_int; | ||
| 1258 | pub extern "c" fn posix_spawn_file_actions_init(actions: *posix_spawn_file_actions_t) c_int; | 1260 | pub extern "c" fn posix_spawn_file_actions_init(actions: *posix_spawn_file_actions_t) c_int; |
| 1259 | pub extern "c" fn posix_spawn_file_actions_destroy(actions: *posix_spawn_file_actions_t) c_int; | 1261 | pub extern "c" fn posix_spawn_file_actions_destroy(actions: *posix_spawn_file_actions_t) c_int; |
| 1260 | pub extern "c" fn posix_spawn_file_actions_addclose(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int; | 1262 | pub extern "c" fn posix_spawn_file_actions_addclose(actions: *posix_spawn_file_actions_t, filedes: fd_t) c_int; |
lib/std/fs/path.zig+32-32| ... | @@ -1438,7 +1438,7 @@ pub fn dirnamePosix(path: []const u8) ?[]const u8 { | ... | @@ -1438,7 +1438,7 @@ pub fn dirnamePosix(path: []const u8) ?[]const u8 { |
| 1438 | } | 1438 | } |
| 1439 | 1439 | ||
| 1440 | fn dirnameInner(comptime path_type: PathType, path: []const u8) ?[]const u8 { | 1440 | fn dirnameInner(comptime path_type: PathType, path: []const u8) ?[]const u8 { |
| 1441 | var it = ComponentIterator(path_type, u8).init(path); | 1441 | var it: ComponentIterator(path_type, u8) = .init(path); |
| 1442 | _ = it.last() orelse return null; | 1442 | _ = it.last() orelse return null; |
| 1443 | const up = it.previous() orelse return it.root(); | 1443 | const up = it.previous() orelse return it.root(); |
| 1444 | return up.path; | 1444 | return up.path; |
| ... | @@ -1539,7 +1539,7 @@ pub fn basenameWindows(path: []const u8) []const u8 { | ... | @@ -1539,7 +1539,7 @@ pub fn basenameWindows(path: []const u8) []const u8 { |
| 1539 | } | 1539 | } |
| 1540 | 1540 | ||
| 1541 | fn basenameInner(comptime path_type: PathType, path: []const u8) []const u8 { | 1541 | fn basenameInner(comptime path_type: PathType, path: []const u8) []const u8 { |
| 1542 | var it = ComponentIterator(path_type, u8).init(path); | 1542 | var it: ComponentIterator(path_type, u8) = .init(path); |
| 1543 | const last = it.last() orelse return &[_]u8{}; | 1543 | const last = it.last() orelse return &[_]u8{}; |
| 1544 | return last.name; | 1544 | return last.name; |
| 1545 | } | 1545 | } |
| ... | @@ -2338,14 +2338,14 @@ pub const NativeComponentIterator = ComponentIterator(switch (native_os) { | ... | @@ -2338,14 +2338,14 @@ pub const NativeComponentIterator = ComponentIterator(switch (native_os) { |
| 2338 | }, u8); | 2338 | }, u8); |
| 2339 | 2339 | ||
| 2340 | pub fn componentIterator(path: []const u8) NativeComponentIterator { | 2340 | pub fn componentIterator(path: []const u8) NativeComponentIterator { |
| 2341 | return NativeComponentIterator.init(path); | 2341 | return .init(path); |
| 2342 | } | 2342 | } |
| 2343 | 2343 | ||
| 2344 | test "ComponentIterator posix" { | 2344 | test "ComponentIterator posix" { |
| 2345 | const PosixComponentIterator = ComponentIterator(.posix, u8); | 2345 | const PosixComponentIterator = ComponentIterator(.posix, u8); |
| 2346 | { | 2346 | { |
| 2347 | const path = "a/b/c/"; | 2347 | const path = "a/b/c/"; |
| 2348 | var it = PosixComponentIterator.init(path); | 2348 | var it: PosixComponentIterator = .init(path); |
| 2349 | try std.testing.expectEqual(0, it.root_len); | 2349 | try std.testing.expectEqual(0, it.root_len); |
| 2350 | try std.testing.expectEqual(0, it.root_end_index); | 2350 | try std.testing.expectEqual(0, it.root_end_index); |
| 2351 | try std.testing.expect(null == it.root()); | 2351 | try std.testing.expect(null == it.root()); |
| ... | @@ -2393,7 +2393,7 @@ test "ComponentIterator posix" { | ... | @@ -2393,7 +2393,7 @@ test "ComponentIterator posix" { |
| 2393 | 2393 | ||
| 2394 | { | 2394 | { |
| 2395 | const path = "/a/b/c/"; | 2395 | const path = "/a/b/c/"; |
| 2396 | var it = PosixComponentIterator.init(path); | 2396 | var it: PosixComponentIterator = .init(path); |
| 2397 | try std.testing.expectEqual(1, it.root_len); | 2397 | try std.testing.expectEqual(1, it.root_len); |
| 2398 | try std.testing.expectEqual(1, it.root_end_index); | 2398 | try std.testing.expectEqual(1, it.root_end_index); |
| 2399 | try std.testing.expectEqualStrings("/", it.root().?); | 2399 | try std.testing.expectEqualStrings("/", it.root().?); |
| ... | @@ -2441,7 +2441,7 @@ test "ComponentIterator posix" { | ... | @@ -2441,7 +2441,7 @@ test "ComponentIterator posix" { |
| 2441 | 2441 | ||
| 2442 | { | 2442 | { |
| 2443 | const path = "////a///b///c////"; | 2443 | const path = "////a///b///c////"; |
| 2444 | var it = PosixComponentIterator.init(path); | 2444 | var it: PosixComponentIterator = .init(path); |
| 2445 | try std.testing.expectEqual(1, it.root_len); | 2445 | try std.testing.expectEqual(1, it.root_len); |
| 2446 | try std.testing.expectEqual(4, it.root_end_index); | 2446 | try std.testing.expectEqual(4, it.root_end_index); |
| 2447 | try std.testing.expectEqualStrings("/", it.root().?); | 2447 | try std.testing.expectEqualStrings("/", it.root().?); |
| ... | @@ -2489,7 +2489,7 @@ test "ComponentIterator posix" { | ... | @@ -2489,7 +2489,7 @@ test "ComponentIterator posix" { |
| 2489 | 2489 | ||
| 2490 | { | 2490 | { |
| 2491 | const path = "/"; | 2491 | const path = "/"; |
| 2492 | var it = PosixComponentIterator.init(path); | 2492 | var it: PosixComponentIterator = .init(path); |
| 2493 | try std.testing.expectEqual(1, it.root_len); | 2493 | try std.testing.expectEqual(1, it.root_len); |
| 2494 | try std.testing.expectEqual(1, it.root_end_index); | 2494 | try std.testing.expectEqual(1, it.root_end_index); |
| 2495 | try std.testing.expectEqualStrings("/", it.root().?); | 2495 | try std.testing.expectEqualStrings("/", it.root().?); |
| ... | @@ -2507,7 +2507,7 @@ test "ComponentIterator posix" { | ... | @@ -2507,7 +2507,7 @@ test "ComponentIterator posix" { |
| 2507 | 2507 | ||
| 2508 | { | 2508 | { |
| 2509 | const path = ""; | 2509 | const path = ""; |
| 2510 | var it = PosixComponentIterator.init(path); | 2510 | var it: PosixComponentIterator = .init(path); |
| 2511 | try std.testing.expectEqual(0, it.root_len); | 2511 | try std.testing.expectEqual(0, it.root_len); |
| 2512 | try std.testing.expectEqual(0, it.root_end_index); | 2512 | try std.testing.expectEqual(0, it.root_end_index); |
| 2513 | try std.testing.expect(null == it.root()); | 2513 | try std.testing.expect(null == it.root()); |
| ... | @@ -2528,7 +2528,7 @@ test "ComponentIterator windows" { | ... | @@ -2528,7 +2528,7 @@ test "ComponentIterator windows" { |
| 2528 | const WindowsComponentIterator = ComponentIterator(.windows, u8); | 2528 | const WindowsComponentIterator = ComponentIterator(.windows, u8); |
| 2529 | { | 2529 | { |
| 2530 | const path = "a/b\\c//"; | 2530 | const path = "a/b\\c//"; |
| 2531 | var it = WindowsComponentIterator.init(path); | 2531 | var it: WindowsComponentIterator = .init(path); |
| 2532 | try std.testing.expectEqual(0, it.root_len); | 2532 | try std.testing.expectEqual(0, it.root_len); |
| 2533 | try std.testing.expectEqual(0, it.root_end_index); | 2533 | try std.testing.expectEqual(0, it.root_end_index); |
| 2534 | try std.testing.expect(null == it.root()); | 2534 | try std.testing.expect(null == it.root()); |
| ... | @@ -2576,7 +2576,7 @@ test "ComponentIterator windows" { | ... | @@ -2576,7 +2576,7 @@ test "ComponentIterator windows" { |
| 2576 | 2576 | ||
| 2577 | { | 2577 | { |
| 2578 | const path = "C:\\a/b/c/"; | 2578 | const path = "C:\\a/b/c/"; |
| 2579 | var it = WindowsComponentIterator.init(path); | 2579 | var it: WindowsComponentIterator = .init(path); |
| 2580 | try std.testing.expectEqual(3, it.root_len); | 2580 | try std.testing.expectEqual(3, it.root_len); |
| 2581 | try std.testing.expectEqual(3, it.root_end_index); | 2581 | try std.testing.expectEqual(3, it.root_end_index); |
| 2582 | try std.testing.expectEqualStrings("C:\\", it.root().?); | 2582 | try std.testing.expectEqualStrings("C:\\", it.root().?); |
| ... | @@ -2614,7 +2614,7 @@ test "ComponentIterator windows" { | ... | @@ -2614,7 +2614,7 @@ test "ComponentIterator windows" { |
| 2614 | 2614 | ||
| 2615 | { | 2615 | { |
| 2616 | const path = "C:\\\\//a/\\/\\b///c////"; | 2616 | const path = "C:\\\\//a/\\/\\b///c////"; |
| 2617 | var it = WindowsComponentIterator.init(path); | 2617 | var it: WindowsComponentIterator = .init(path); |
| 2618 | try std.testing.expectEqual(3, it.root_len); | 2618 | try std.testing.expectEqual(3, it.root_len); |
| 2619 | try std.testing.expectEqual(6, it.root_end_index); | 2619 | try std.testing.expectEqual(6, it.root_end_index); |
| 2620 | try std.testing.expectEqualStrings("C:\\", it.root().?); | 2620 | try std.testing.expectEqualStrings("C:\\", it.root().?); |
| ... | @@ -2652,7 +2652,7 @@ test "ComponentIterator windows" { | ... | @@ -2652,7 +2652,7 @@ test "ComponentIterator windows" { |
| 2652 | 2652 | ||
| 2653 | { | 2653 | { |
| 2654 | const path = "/"; | 2654 | const path = "/"; |
| 2655 | var it = WindowsComponentIterator.init(path); | 2655 | var it: WindowsComponentIterator = .init(path); |
| 2656 | try std.testing.expectEqual(1, it.root_len); | 2656 | try std.testing.expectEqual(1, it.root_len); |
| 2657 | try std.testing.expectEqual(1, it.root_end_index); | 2657 | try std.testing.expectEqual(1, it.root_end_index); |
| 2658 | try std.testing.expectEqualStrings("/", it.root().?); | 2658 | try std.testing.expectEqualStrings("/", it.root().?); |
| ... | @@ -2670,7 +2670,7 @@ test "ComponentIterator windows" { | ... | @@ -2670,7 +2670,7 @@ test "ComponentIterator windows" { |
| 2670 | 2670 | ||
| 2671 | { | 2671 | { |
| 2672 | const path = ""; | 2672 | const path = ""; |
| 2673 | var it = WindowsComponentIterator.init(path); | 2673 | var it: WindowsComponentIterator = .init(path); |
| 2674 | try std.testing.expectEqual(0, it.root_len); | 2674 | try std.testing.expectEqual(0, it.root_len); |
| 2675 | try std.testing.expectEqual(0, it.root_end_index); | 2675 | try std.testing.expectEqual(0, it.root_end_index); |
| 2676 | try std.testing.expect(null == it.root()); | 2676 | try std.testing.expect(null == it.root()); |
| ... | @@ -2692,7 +2692,7 @@ test "ComponentIterator windows WTF-16" { | ... | @@ -2692,7 +2692,7 @@ test "ComponentIterator windows WTF-16" { |
| 2692 | const L = std.unicode.utf8ToUtf16LeStringLiteral; | 2692 | const L = std.unicode.utf8ToUtf16LeStringLiteral; |
| 2693 | 2693 | ||
| 2694 | const path = L("C:\\a/b/c/"); | 2694 | const path = L("C:\\a/b/c/"); |
| 2695 | var it = WindowsComponentIterator.init(path); | 2695 | var it: WindowsComponentIterator = .init(path); |
| 2696 | try std.testing.expectEqual(3, it.root_len); | 2696 | try std.testing.expectEqual(3, it.root_len); |
| 2697 | try std.testing.expectEqual(3, it.root_end_index); | 2697 | try std.testing.expectEqual(3, it.root_end_index); |
| 2698 | try std.testing.expectEqualSlices(u16, L("C:\\"), it.root().?); | 2698 | try std.testing.expectEqualSlices(u16, L("C:\\"), it.root().?); |
| ... | @@ -2731,59 +2731,59 @@ test "ComponentIterator windows WTF-16" { | ... | @@ -2731,59 +2731,59 @@ test "ComponentIterator windows WTF-16" { |
| 2731 | test "ComponentIterator roots" { | 2731 | test "ComponentIterator roots" { |
| 2732 | // UEFI | 2732 | // UEFI |
| 2733 | { | 2733 | { |
| 2734 | var it = ComponentIterator(.uefi, u8).init("\\\\a"); | 2734 | var it: ComponentIterator(.uefi, u8) = .init("\\\\a"); |
| 2735 | try std.testing.expectEqualStrings("\\", it.root().?); | 2735 | try std.testing.expectEqualStrings("\\", it.root().?); |
| 2736 | 2736 | ||
| 2737 | it = ComponentIterator(.uefi, u8).init("//a"); | 2737 | it = .init("//a"); |
| 2738 | try std.testing.expect(null == it.root()); | 2738 | try std.testing.expect(null == it.root()); |
| 2739 | } | 2739 | } |
| 2740 | // POSIX | 2740 | // POSIX |
| 2741 | { | 2741 | { |
| 2742 | var it = ComponentIterator(.posix, u8).init("//a"); | 2742 | var it: ComponentIterator(.posix, u8) = .init("//a"); |
| 2743 | try std.testing.expectEqualStrings("/", it.root().?); | 2743 | try std.testing.expectEqualStrings("/", it.root().?); |
| 2744 | 2744 | ||
| 2745 | it = ComponentIterator(.posix, u8).init("\\\\a"); | 2745 | it = .init("\\\\a"); |
| 2746 | try std.testing.expect(null == it.root()); | 2746 | try std.testing.expect(null == it.root()); |
| 2747 | } | 2747 | } |
| 2748 | // Windows | 2748 | // Windows |
| 2749 | { | 2749 | { |
| 2750 | // Drive relative | 2750 | // Drive relative |
| 2751 | var it = ComponentIterator(.windows, u8).init("C:a"); | 2751 | var it: ComponentIterator(.windows, u8) = .init("C:a"); |
| 2752 | try std.testing.expectEqualStrings("C:", it.root().?); | 2752 | try std.testing.expectEqualStrings("C:", it.root().?); |
| 2753 | 2753 | ||
| 2754 | // Drive absolute | 2754 | // Drive absolute |
| 2755 | it = ComponentIterator(.windows, u8).init("C:/a"); | 2755 | it = .init("C:/a"); |
| 2756 | try std.testing.expectEqualStrings("C:/", it.root().?); | 2756 | try std.testing.expectEqualStrings("C:/", it.root().?); |
| 2757 | it = ComponentIterator(.windows, u8).init("C:\\a"); | 2757 | it = .init("C:\\a"); |
| 2758 | try std.testing.expectEqualStrings("C:\\", it.root().?); | 2758 | try std.testing.expectEqualStrings("C:\\", it.root().?); |
| 2759 | it = ComponentIterator(.windows, u8).init("C:///a"); | 2759 | it = .init("C:///a"); |
| 2760 | try std.testing.expectEqualStrings("C:/", it.root().?); | 2760 | try std.testing.expectEqualStrings("C:/", it.root().?); |
| 2761 | 2761 | ||
| 2762 | // Rooted | 2762 | // Rooted |
| 2763 | it = ComponentIterator(.windows, u8).init("\\a"); | 2763 | it = .init("\\a"); |
| 2764 | try std.testing.expectEqualStrings("\\", it.root().?); | 2764 | try std.testing.expectEqualStrings("\\", it.root().?); |
| 2765 | it = ComponentIterator(.windows, u8).init("/a"); | 2765 | it = .init("/a"); |
| 2766 | try std.testing.expectEqualStrings("/", it.root().?); | 2766 | try std.testing.expectEqualStrings("/", it.root().?); |
| 2767 | 2767 | ||
| 2768 | // Root local device | 2768 | // Root local device |
| 2769 | it = ComponentIterator(.windows, u8).init("\\\\."); | 2769 | it = .init("\\\\."); |
| 2770 | try std.testing.expectEqualStrings("\\\\.", it.root().?); | 2770 | try std.testing.expectEqualStrings("\\\\.", it.root().?); |
| 2771 | it = ComponentIterator(.windows, u8).init("//?"); | 2771 | it = .init("//?"); |
| 2772 | try std.testing.expectEqualStrings("//?", it.root().?); | 2772 | try std.testing.expectEqualStrings("//?", it.root().?); |
| 2773 | 2773 | ||
| 2774 | // UNC absolute | 2774 | // UNC absolute |
| 2775 | it = ComponentIterator(.windows, u8).init("//"); | 2775 | it = .init("//"); |
| 2776 | try std.testing.expectEqualStrings("//", it.root().?); | 2776 | try std.testing.expectEqualStrings("//", it.root().?); |
| 2777 | it = ComponentIterator(.windows, u8).init("\\\\a"); | 2777 | it = .init("\\\\a"); |
| 2778 | try std.testing.expectEqualStrings("\\\\a", it.root().?); | 2778 | try std.testing.expectEqualStrings("\\\\a", it.root().?); |
| 2779 | it = ComponentIterator(.windows, u8).init("\\\\a\\b\\\\c"); | 2779 | it = .init("\\\\a\\b\\\\c"); |
| 2780 | try std.testing.expectEqualStrings("\\\\a\\b\\", it.root().?); | 2780 | try std.testing.expectEqualStrings("\\\\a\\b\\", it.root().?); |
| 2781 | it = ComponentIterator(.windows, u8).init("//a"); | 2781 | it = .init("//a"); |
| 2782 | try std.testing.expectEqualStrings("//a", it.root().?); | 2782 | try std.testing.expectEqualStrings("//a", it.root().?); |
| 2783 | it = ComponentIterator(.windows, u8).init("//a/b//c"); | 2783 | it = .init("//a/b//c"); |
| 2784 | try std.testing.expectEqualStrings("//a/b/", it.root().?); | 2784 | try std.testing.expectEqualStrings("//a/b/", it.root().?); |
| 2785 | // Malformed UNC path with empty server name | 2785 | // Malformed UNC path with empty server name |
| 2786 | it = ComponentIterator(.windows, u8).init("\\\\\\a\\b\\c"); | 2786 | it = .init("\\\\\\a\\b\\c"); |
| 2787 | try std.testing.expectEqualStrings("\\\\\\a\\", it.root().?); | 2787 | try std.testing.expectEqualStrings("\\\\\\a\\", it.root().?); |
| 2788 | } | 2788 | } |
| 2789 | } | 2789 | } |
lib/std/heap/debug_allocator.zig+4-4| ... | @@ -718,7 +718,7 @@ pub fn DebugAllocator(comptime config: Config) type { | ... | @@ -718,7 +718,7 @@ pub fn DebugAllocator(comptime config: Config) type { |
| 718 | 718 | ||
| 719 | fn alloc(context: *anyopaque, len: usize, alignment: mem.Alignment, ret_addr: usize) ?[*]u8 { | 719 | fn alloc(context: *anyopaque, len: usize, alignment: mem.Alignment, ret_addr: usize) ?[*]u8 { |
| 720 | const self: *Self = @ptrCast(@alignCast(context)); | 720 | const self: *Self = @ptrCast(@alignCast(context)); |
| 721 | if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex); | 721 | if (have_mutex) std.Io.Threaded.mutexLockUncancelable(&self.mutex); |
| 722 | defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex); | 722 | defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex); |
| 723 | 723 | ||
| 724 | if (config.enable_memory_limit) { | 724 | if (config.enable_memory_limit) { |
| ... | @@ -831,7 +831,7 @@ pub fn DebugAllocator(comptime config: Config) type { | ... | @@ -831,7 +831,7 @@ pub fn DebugAllocator(comptime config: Config) type { |
| 831 | return_address: usize, | 831 | return_address: usize, |
| 832 | ) bool { | 832 | ) bool { |
| 833 | const self: *Self = @ptrCast(@alignCast(context)); | 833 | const self: *Self = @ptrCast(@alignCast(context)); |
| 834 | if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex); | 834 | if (have_mutex) std.Io.Threaded.mutexLockUncancelable(&self.mutex); |
| 835 | defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex); | 835 | defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex); |
| 836 | 836 | ||
| 837 | const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(memory.len - 1), @backingInt(alignment)); | 837 | const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(memory.len - 1), @backingInt(alignment)); |
| ... | @@ -850,7 +850,7 @@ pub fn DebugAllocator(comptime config: Config) type { | ... | @@ -850,7 +850,7 @@ pub fn DebugAllocator(comptime config: Config) type { |
| 850 | return_address: usize, | 850 | return_address: usize, |
| 851 | ) ?[*]u8 { | 851 | ) ?[*]u8 { |
| 852 | const self: *Self = @ptrCast(@alignCast(context)); | 852 | const self: *Self = @ptrCast(@alignCast(context)); |
| 853 | if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex); | 853 | if (have_mutex) std.Io.Threaded.mutexLockUncancelable(&self.mutex); |
| 854 | defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex); | 854 | defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex); |
| 855 | 855 | ||
| 856 | const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(memory.len - 1), @backingInt(alignment)); | 856 | const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(memory.len - 1), @backingInt(alignment)); |
| ... | @@ -868,7 +868,7 @@ pub fn DebugAllocator(comptime config: Config) type { | ... | @@ -868,7 +868,7 @@ pub fn DebugAllocator(comptime config: Config) type { |
| 868 | return_address: usize, | 868 | return_address: usize, |
| 869 | ) void { | 869 | ) void { |
| 870 | const self: *Self = @ptrCast(@alignCast(context)); | 870 | const self: *Self = @ptrCast(@alignCast(context)); |
| 871 | if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex); | 871 | if (have_mutex) std.Io.Threaded.mutexLockUncancelable(&self.mutex); |
| 872 | defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex); | 872 | defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex); |
| 873 | 873 | ||
| 874 | const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(old_memory.len - 1), @backingInt(alignment)); | 874 | const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(old_memory.len - 1), @backingInt(alignment)); |
lib/std/lang.zig+1-1| ... | @@ -971,7 +971,7 @@ pub const Signedness = enum(u1) { | ... | @@ -971,7 +971,7 @@ pub const Signedness = enum(u1) { |
| 971 | 971 | ||
| 972 | /// This data structure is used by the Zig language code generation and | 972 | /// This data structure is used by the Zig language code generation and |
| 973 | /// therefore must be kept in sync with the compiler implementation. | 973 | /// therefore must be kept in sync with the compiler implementation. |
| 974 | pub const OutputMode = enum { | 974 | pub const OutputMode = enum(u2) { |
| 975 | Exe, | 975 | Exe, |
| 976 | Lib, | 976 | Lib, |
| 977 | Obj, | 977 | Obj, |
lib/std/os/windows.zig+14| ... | @@ -147,9 +147,23 @@ pub const OBJECT = struct { | ... | @@ -147,9 +147,23 @@ pub const OBJECT = struct { |
| 147 | pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".field_names.len; | 147 | pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".field_names.len; |
| 148 | }; | 148 | }; |
| 149 | 149 | ||
| 150 | pub const BASIC_INFORMATION = extern struct { | ||
| 151 | Attributes: ATTRIBUTES.Flags, | ||
| 152 | GrantedAccess: ACCESS_MASK, | ||
| 153 | HandleCount: ULONG, | ||
| 154 | PointerCount: ULONG, | ||
| 155 | Reserved: [10]ULONG, | ||
| 156 | }; | ||
| 157 | |||
| 150 | pub const NAME_INFORMATION = extern struct { | 158 | pub const NAME_INFORMATION = extern struct { |
| 151 | Name: UNICODE_STRING, | 159 | Name: UNICODE_STRING, |
| 152 | }; | 160 | }; |
| 161 | |||
| 162 | pub const HANDLE_FLAG = packed struct(USHORT) { | ||
| 163 | INHERIT: bool = false, | ||
| 164 | PROTECT_FROM_CLOSE: bool = false, | ||
| 165 | Reserved1: u14 = 0, | ||
| 166 | }; | ||
| 153 | }; | 167 | }; |
| 154 | 168 | ||
| 155 | pub const FILE = struct { | 169 | pub const FILE = struct { |
lib/std/os/windows/ntdll.zig+7| ... | @@ -461,6 +461,13 @@ pub extern "ntdll" fn LdrUnregisterDllNotification( | ... | @@ -461,6 +461,13 @@ pub extern "ntdll" fn LdrUnregisterDllNotification( |
| 461 | Cookie: LDR.DLL_NOTIFICATION.COOKIE, | 461 | Cookie: LDR.DLL_NOTIFICATION.COOKIE, |
| 462 | ) callconv(.winapi) NTSTATUS; | 462 | ) callconv(.winapi) NTSTATUS; |
| 463 | 463 | ||
| 464 | pub extern "ntdll" fn NtSetInformationObject( | ||
| 465 | Handle: HANDLE, | ||
| 466 | ObjectInformationClass: OBJECT.INFORMATION_CLASS, | ||
| 467 | ObjectInformation: *const anyopaque, | ||
| 468 | ObjectInformationLength: ULONG, | ||
| 469 | ) callconv(.winapi) NTSTATUS; | ||
| 470 | |||
| 464 | pub extern "ntdll" fn NtQueryAttributesFile( | 471 | pub extern "ntdll" fn NtQueryAttributesFile( |
| 465 | ObjectAttributes: *const OBJECT.ATTRIBUTES, | 472 | ObjectAttributes: *const OBJECT.ATTRIBUTES, |
| 466 | FileAttributes: *FILE.BASIC_INFORMATION, | 473 | FileAttributes: *FILE.BASIC_INFORMATION, |
lib/std/process.zig+36-30| ... | @@ -282,37 +282,50 @@ pub const ReplaceError = error{ | ... | @@ -282,37 +282,50 @@ pub const ReplaceError = error{ |
| 282 | } || Allocator.Error || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError; | 282 | } || Allocator.Error || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError; |
| 283 | 283 | ||
| 284 | pub const ReplaceOptions = struct { | 284 | pub const ReplaceOptions = struct { |
| 285 | exe: Exe = .detect, | ||
| 285 | argv: []const []const u8, | 286 | argv: []const []const u8, |
| 286 | expand_arg0: ArgExpansion = .no_expand, | 287 | |
| 288 | /// Set to change the current working directory when spawning the child process. | ||
| 289 | cwd: Child.Cwd = .inherit, | ||
| 287 | /// Replaces the environment when provided. The PATH value from here is | 290 | /// Replaces the environment when provided. The PATH value from here is |
| 288 | /// never used to resolve `argv[0]`. | 291 | /// never used to resolve `argv[0]`. |
| 289 | environ_map: ?*const Environ.Map = null, | 292 | environ_map: ?*const Environ.Map = null, |
| 293 | expand_arg0: ArgExpansion = .no_expand, | ||
| 294 | |||
| 295 | inherit_dirs: []const Dir = &.{}, | ||
| 296 | inherit_files: []const File = &.{}, | ||
| 297 | |||
| 298 | /// Start child process in suspended state. | ||
| 299 | /// For Posix systems it's started as if SIGSTOP was sent. | ||
| 300 | start_suspended: bool = false, | ||
| 301 | |||
| 302 | pub const Exe = union(enum) { | ||
| 303 | /// `argv[0]` is the name of the program to execute. If it is not already a | ||
| 304 | /// file path (i.e. it contains '/'), it is resolved into a file path based on | ||
| 305 | /// PATH from the parent environment. | ||
| 306 | detect, | ||
| 307 | /// `argv[0]` is the name of the program to execute, resolved into a file path | ||
| 308 | /// based on PATH from the parent environment. | ||
| 309 | search, | ||
| 310 | /// `argv[0]` is the file path of the program to execute, relative to `Dir` payload. | ||
| 311 | /// It is *always* treated as a file path, even if it does not contain '/'. | ||
| 312 | path: Dir, | ||
| 313 | /// `File` payload is the program to execute. | ||
| 314 | file: File, | ||
| 315 | /// `path` is the file path of the program to execute, relative to `dir`. | ||
| 316 | /// It is *always* treated as a file path, even if it does not contain '/'. | ||
| 317 | explicit: struct { dir: Dir, path: []const u8 }, | ||
| 318 | }; | ||
| 290 | }; | 319 | }; |
| 291 | 320 | ||
| 292 | /// Replaces the current process image with the executed process. If this | 321 | /// Replaces the current process image with the executed process. If this |
| 293 | /// function succeeds, it does not return. | 322 | /// function succeeds, it does not return. |
| 294 | /// | 323 | /// |
| 295 | /// `argv[0]` is the name of the process to replace the current one with. If it | ||
| 296 | /// is not already a file path (i.e. it contains '/'), it is resolved into a | ||
| 297 | /// file path based on PATH from the parent environment. | ||
| 298 | /// | ||
| 299 | /// It is illegal to call this function in a fork() child. | 324 | /// It is illegal to call this function in a fork() child. |
| 300 | pub fn replace(io: Io, options: ReplaceOptions) ReplaceError { | 325 | pub fn replace(io: Io, options: ReplaceOptions) ReplaceError { |
| 301 | return io.vtable.processReplace(io.userdata, options); | 326 | return io.vtable.processReplace(io.userdata, options); |
| 302 | } | 327 | } |
| 303 | 328 | ||
| 304 | /// Replaces the current process image with the executed process. If this | ||
| 305 | /// function succeeds, it does not return. | ||
| 306 | /// | ||
| 307 | /// `argv[0]` is the file path of the process to replace the current one with, | ||
| 308 | /// relative to `dir`. It is *always* treated as a file path, even if it does | ||
| 309 | /// not contain '/'. | ||
| 310 | /// | ||
| 311 | /// It is illegal to call this function in a fork() child. | ||
| 312 | pub fn replacePath(io: Io, dir: Io.Dir, options: ReplaceOptions) ReplaceError { | ||
| 313 | return io.vtable.processReplacePath(io.userdata, dir, options); | ||
| 314 | } | ||
| 315 | |||
| 316 | pub const ArgExpansion = enum { expand, no_expand }; | 329 | pub const ArgExpansion = enum { expand, no_expand }; |
| 317 | 330 | ||
| 318 | /// File name extensions supported natively by `CreateProcess()` on Windows. | 331 | /// File name extensions supported natively by `CreateProcess()` on Windows. |
| ... | @@ -368,6 +381,7 @@ pub const SpawnError = error{ | ... | @@ -368,6 +381,7 @@ pub const SpawnError = error{ |
| 368 | } || Io.File.OpenError || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError; | 381 | } || Io.File.OpenError || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError; |
| 369 | 382 | ||
| 370 | pub const SpawnOptions = struct { | 383 | pub const SpawnOptions = struct { |
| 384 | exe: ReplaceOptions.Exe = .detect, | ||
| 371 | argv: []const []const u8, | 385 | argv: []const []const u8, |
| 372 | 386 | ||
| 373 | /// Set to change the current working directory when spawning the child process. | 387 | /// Set to change the current working directory when spawning the child process. |
| ... | @@ -385,11 +399,13 @@ pub const SpawnOptions = struct { | ... | @@ -385,11 +399,13 @@ pub const SpawnOptions = struct { |
| 385 | /// | 399 | /// |
| 386 | /// The child's progress tree will be grafted into the parent's progress tree, | 400 | /// The child's progress tree will be grafted into the parent's progress tree, |
| 387 | /// by substituting this node with the child's root node. | 401 | /// by substituting this node with the child's root node. |
| 388 | progress_node: std.Progress.Node = std.Progress.Node.none, | 402 | progress_node: std.Progress.Node = .none, |
| 389 | 403 | ||
| 390 | stdin: StdIo = .inherit, | 404 | stdin: StdIo = .inherit, |
| 391 | stdout: StdIo = .inherit, | 405 | stdout: StdIo = .inherit, |
| 392 | stderr: StdIo = .inherit, | 406 | stderr: StdIo = .inherit, |
| 407 | inherit_dirs: []const Dir = &.{}, | ||
| 408 | inherit_files: []const File = &.{}, | ||
| 393 | 409 | ||
| 394 | /// Set to true to obtain rusage information for the child process. | 410 | /// Set to true to obtain rusage information for the child process. |
| 395 | /// Depending on the target platform and implementation status, the | 411 | /// Depending on the target platform and implementation status, the |
| ... | @@ -443,27 +459,16 @@ pub const SpawnOptions = struct { | ... | @@ -443,27 +459,16 @@ pub const SpawnOptions = struct { |
| 443 | }; | 459 | }; |
| 444 | 460 | ||
| 445 | /// Creates a child process. | 461 | /// Creates a child process. |
| 446 | /// | ||
| 447 | /// `argv[0]` is the name of the program to execute. If it is not already a | ||
| 448 | /// file path (i.e. it contains '/'), it is resolved into a file path based on | ||
| 449 | /// PATH from the parent environment. | ||
| 450 | pub fn spawn(io: Io, options: SpawnOptions) SpawnError!Child { | 462 | pub fn spawn(io: Io, options: SpawnOptions) SpawnError!Child { |
| 451 | return io.vtable.processSpawn(io.userdata, options); | 463 | return io.vtable.processSpawn(io.userdata, options); |
| 452 | } | 464 | } |
| 453 | 465 | ||
| 454 | /// Creates a child process. | ||
| 455 | /// | ||
| 456 | /// `argv[0]` is the file path of the program to execute, relative to `dir`. It | ||
| 457 | /// is *always* treated as a file path, even if it does not contain '/'. | ||
| 458 | pub fn spawnPath(io: Io, dir: Io.Dir, options: SpawnOptions) SpawnError!Child { | ||
| 459 | return io.vtable.processSpawnPath(io.userdata, dir, options); | ||
| 460 | } | ||
| 461 | |||
| 462 | pub const RunError = error{ | 466 | pub const RunError = error{ |
| 463 | StreamTooLong, | 467 | StreamTooLong, |
| 464 | } || SpawnError || Io.File.MultiReader.UnendingError || Io.Timeout.Error; | 468 | } || SpawnError || Io.File.MultiReader.UnendingError || Io.Timeout.Error; |
| 465 | 469 | ||
| 466 | pub const RunOptions = struct { | 470 | pub const RunOptions = struct { |
| 471 | exe: ReplaceOptions.Exe = .detect, | ||
| 467 | argv: []const []const u8, | 472 | argv: []const []const u8, |
| 468 | stderr_limit: Io.Limit = .unlimited, | 473 | stderr_limit: Io.Limit = .unlimited, |
| 469 | stdout_limit: Io.Limit = .unlimited, | 474 | stdout_limit: Io.Limit = .unlimited, |
| ... | @@ -501,6 +506,7 @@ pub const RunResult = struct { | ... | @@ -501,6 +506,7 @@ pub const RunResult = struct { |
| 501 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. | 506 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. |
| 502 | pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult { | 507 | pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult { |
| 503 | var child = try spawn(io, .{ | 508 | var child = try spawn(io, .{ |
| 509 | .exe = options.exe, | ||
| 504 | .argv = options.argv, | 510 | .argv = options.argv, |
| 505 | .cwd = options.cwd, | 511 | .cwd = options.cwd, |
| 506 | .environ_map = options.environ_map, | 512 | .environ_map = options.environ_map, |
lib/std/zig.zig+1-2| ... | @@ -727,8 +727,7 @@ pub fn readSourceFileToEndAlloc(gpa: Allocator, file_reader: *Io.File.Reader) ![ | ... | @@ -727,8 +727,7 @@ pub fn readSourceFileToEndAlloc(gpa: Allocator, file_reader: *Io.File.Reader) ![ |
| 727 | } | 727 | } |
| 728 | 728 | ||
| 729 | pub fn printAstErrorsToStderr(gpa: Allocator, io: Io, tree: Ast, path: []const u8, color: Color) !void { | 729 | pub fn printAstErrorsToStderr(gpa: Allocator, io: Io, tree: Ast, path: []const u8, color: Color) !void { |
| 730 | var wip_errors: ErrorBundle.Wip = undefined; | 730 | var wip_errors: ErrorBundle.Wip = try .init(gpa); |
| 731 | try wip_errors.init(gpa); | ||
| 732 | defer wip_errors.deinit(); | 731 | defer wip_errors.deinit(); |
| 733 | 732 | ||
| 734 | try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors); | 733 | try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors); |
lib/std/zig/Client.zig+29| ... | @@ -60,6 +60,8 @@ pub const Message = struct { | ... | @@ -60,6 +60,8 @@ pub const Message = struct { |
| 60 | start_fuzzing, | 60 | start_fuzzing, |
| 61 | /// The message body has the same format as in Server. | 61 | /// The message body has the same format as in Server. |
| 62 | new_fuzz_input, | 62 | new_fuzz_input, |
| 63 | /// Body is a sequence of Arg | ||
| 64 | args, | ||
| 63 | 65 | ||
| 64 | /// Asks the server to run a list of steps. | 66 | /// Asks the server to run a list of steps. |
| 65 | /// Body is a `BuildSteps`. | 67 | /// Body is a `BuildSteps`. |
| ... | @@ -69,6 +71,33 @@ pub const Message = struct { | ... | @@ -69,6 +71,33 @@ pub const Message = struct { |
| 69 | _, | 71 | _, |
| 70 | }; | 72 | }; |
| 71 | 73 | ||
| 74 | pub const Arg = enum(u8) { | ||
| 75 | /// Trailing: | ||
| 76 | /// * string: [:0]u8, | ||
| 77 | string, | ||
| 78 | /// Trailing: | ||
| 79 | /// * string: [:0]u8, | ||
| 80 | prefix, | ||
| 81 | /// Trailing: | ||
| 82 | /// * string: [:0]u8, | ||
| 83 | suffix, | ||
| 84 | /// Trailing: | ||
| 85 | /// * handle: Handle, | ||
| 86 | input_file, | ||
| 87 | /// Trailing: | ||
| 88 | /// * handle: Handle, | ||
| 89 | input_dir, | ||
| 90 | /// Trailing: | ||
| 91 | /// * handle: Handle, | ||
| 92 | input_file_content, | ||
| 93 | /// Trailing: | ||
| 94 | /// * handle: Handle, | ||
| 95 | output_file, | ||
| 96 | /// Trailing: | ||
| 97 | /// * handle: Handle, | ||
| 98 | output_dir, | ||
| 99 | }; | ||
| 100 | |||
| 72 | /// Trailing: | 101 | /// Trailing: |
| 73 | /// * step_indices: [step_count]std.Build.Configuration.Step.Index, | 102 | /// * step_indices: [step_count]std.Build.Configuration.Step.Index, |
| 74 | pub const BuildSteps = extern struct { | 103 | pub const BuildSteps = extern struct { |
lib/std/zig/ErrorBundle.zig+7-4| ... | @@ -360,22 +360,25 @@ pub const Wip = struct { | ... | @@ -360,22 +360,25 @@ pub const Wip = struct { |
| 360 | extra: std.ArrayList(u32), | 360 | extra: std.ArrayList(u32), |
| 361 | root_list: std.ArrayList(MessageIndex), | 361 | root_list: std.ArrayList(MessageIndex), |
| 362 | 362 | ||
| 363 | pub fn init(wip: *Wip, gpa: Allocator) !void { | 363 | pub fn init(gpa: Allocator) !Wip { |
| 364 | wip.* = .{ | 364 | var wip: Wip = .{ |
| 365 | .gpa = gpa, | 365 | .gpa = gpa, |
| 366 | .string_bytes = .empty, | 366 | .string_bytes = .empty, |
| 367 | .extra = .empty, | 367 | .extra = .empty, |
| 368 | .root_list = .empty, | 368 | .root_list = .empty, |
| 369 | }; | 369 | }; |
| 370 | errdefer wip.deinit(); | ||
| 370 | 371 | ||
| 371 | // So that 0 can be used to indicate a null string. | 372 | // So that 0 can be used to indicate a null string. |
| 372 | try wip.string_bytes.append(gpa, 0); | 373 | try wip.string_bytes.append(gpa, 0); |
| 373 | 374 | ||
| 374 | assert(0 == try addExtra(wip, ErrorMessageList{ | 375 | assert(0 == try wip.addExtra(ErrorMessageList{ |
| 375 | .len = 0, | 376 | .len = 0, |
| 376 | .start = 0, | 377 | .start = 0, |
| 377 | .compile_log_text = 0, | 378 | .compile_log_text = 0, |
| 378 | })); | 379 | })); |
| 380 | |||
| 381 | return wip; | ||
| 379 | } | 382 | } |
| 380 | 383 | ||
| 381 | pub fn deinit(wip: *Wip) void { | 384 | pub fn deinit(wip: *Wip) void { |
| ... | @@ -773,7 +776,7 @@ pub const Wip = struct { | ... | @@ -773,7 +776,7 @@ pub const Wip = struct { |
| 773 | u32 => @field(extra, field_name), | 776 | u32 => @field(extra, field_name), |
| 774 | MessageIndex => @backingInt(@field(extra, field_name)), | 777 | MessageIndex => @backingInt(@field(extra, field_name)), |
| 775 | SourceLocationIndex => @backingInt(@field(extra, field_name)), | 778 | SourceLocationIndex => @backingInt(@field(extra, field_name)), |
| 776 | else => @compileError("bad field type"), | 779 | else => @compileError("bad field type: " ++ @typeName(field_type)), |
| 777 | }; | 780 | }; |
| 778 | i += 1; | 781 | i += 1; |
| 779 | } | 782 | } |
lib/std/zig/Server.zig+40-2| ... | @@ -74,6 +74,12 @@ pub const Message = struct { | ... | @@ -74,6 +74,12 @@ pub const Message = struct { |
| 74 | broadcast_fuzz_input, | 74 | broadcast_fuzz_input, |
| 75 | /// Body is a TimeReport. | 75 | /// Body is a TimeReport. |
| 76 | time_report, | 76 | time_report, |
| 77 | /// Body is a sequence of: | ||
| 78 | /// - root_dir: InputDir, | ||
| 79 | /// - sub_path: [:0]u8, | ||
| 80 | discovered_inputs, | ||
| 81 | /// Body is a Config. | ||
| 82 | config, | ||
| 77 | 83 | ||
| 78 | /// The first message sent by the server over the build system protocol. | 84 | /// The first message sent by the server over the build system protocol. |
| 79 | /// Body is a `Handshake`. | 85 | /// Body is a `Handshake`. |
| ... | @@ -112,7 +118,7 @@ pub const Message = struct { | ... | @@ -112,7 +118,7 @@ pub const Message = struct { |
| 112 | 118 | ||
| 113 | pub const Flags = packed struct(u32) { | 119 | pub const Flags = packed struct(u32) { |
| 114 | file_system_watch_supported: bool, | 120 | file_system_watch_supported: bool, |
| 115 | _: u31 = 0, | 121 | unused: u31 = 0, |
| 116 | }; | 122 | }; |
| 117 | }; | 123 | }; |
| 118 | 124 | ||
| ... | @@ -198,7 +204,7 @@ pub const Message = struct { | ... | @@ -198,7 +204,7 @@ pub const Message = struct { |
| 198 | flags: Flags, | 204 | flags: Flags, |
| 199 | pub const Flags = packed struct(u32) { | 205 | pub const Flags = packed struct(u32) { |
| 200 | use_llvm: bool, | 206 | use_llvm: bool, |
| 201 | _: u31 = 0, | 207 | unused: u31 = 0, |
| 202 | }; | 208 | }; |
| 203 | }; | 209 | }; |
| 204 | 210 | ||
| ... | @@ -212,6 +218,29 @@ pub const Message = struct { | ... | @@ -212,6 +218,29 @@ pub const Message = struct { |
| 212 | reserved: u7 = 0, | 218 | reserved: u7 = 0, |
| 213 | }; | 219 | }; |
| 214 | }; | 220 | }; |
| 221 | |||
| 222 | /// A reference to one of the `.input_dir`s from the `.args` message. | ||
| 223 | pub const InputDir = enum(u32) { | ||
| 224 | /// Server cwd relative path, or absolute path. | ||
| 225 | cwd, | ||
| 226 | /// Numbered in the order that `.input_dir`s appear in the `.args` message. | ||
| 227 | _, | ||
| 228 | }; | ||
| 229 | |||
| 230 | /// Properties of the output binary target that are decided by the compiler. | ||
| 231 | pub const Config = extern struct { | ||
| 232 | flags: Flags, | ||
| 233 | |||
| 234 | pub const Flags = packed struct(u8) { | ||
| 235 | output_mode: std.lang.OutputMode, | ||
| 236 | link_mode: std.lang.LinkMode, | ||
| 237 | link_libc: bool, | ||
| 238 | link_libcpp: bool, | ||
| 239 | link_libunwind: bool, | ||
| 240 | pie: bool, | ||
| 241 | unused: u1 = 0, | ||
| 242 | }; | ||
| 243 | }; | ||
| 215 | }; | 244 | }; |
| 216 | 245 | ||
| 217 | pub fn receiveMessage(s: *Server) !InMessage.Header { | 246 | pub fn receiveMessage(s: *Server) !InMessage.Header { |
| ... | @@ -365,3 +394,12 @@ pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void { | ... | @@ -365,3 +394,12 @@ pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void { |
| 365 | try s.out.writeAll(test_metadata.string_bytes); | 394 | try s.out.writeAll(test_metadata.string_bytes); |
| 366 | try s.out.flush(); | 395 | try s.out.flush(); |
| 367 | } | 396 | } |
| 397 | |||
| 398 | pub fn serveConfig(s: *Server, config: OutMessage.Config) !void { | ||
| 399 | try s.serveMessageHeader(.{ | ||
| 400 | .tag = .config, | ||
| 401 | .bytes_len = @intCast(@sizeOf(OutMessage.Config)), | ||
| 402 | }); | ||
| 403 | try s.out.writeStruct(config, .little); | ||
| 404 | try s.out.flush(); | ||
| 405 | } |
lib/std/zig/system.zig+22-34| ... | @@ -33,13 +33,9 @@ pub const Executor = union(enum) { | ... | @@ -33,13 +33,9 @@ pub const Executor = union(enum) { |
| 33 | pub const GetExternalExecutorOptions = struct { | 33 | pub const GetExternalExecutorOptions = struct { |
| 34 | host_cpu_arch: std.Target.Cpu.Arch, | 34 | host_cpu_arch: std.Target.Cpu.Arch, |
| 35 | host_os_tag: std.Target.Os.Tag, | 35 | host_os_tag: std.Target.Os.Tag, |
| 36 | allow_darling: bool = true, | ||
| 37 | allow_qemu: bool = true, | ||
| 38 | allow_rosetta: bool = true, | ||
| 39 | allow_wasmtime: bool = true, | ||
| 40 | allow_wine: bool = true, | ||
| 41 | qemu_fixes_dl: bool = false, | 36 | qemu_fixes_dl: bool = false, |
| 42 | link_libc: bool = false, | 37 | link_mode: std.lang.LinkMode, |
| 38 | link_libc: bool, | ||
| 43 | }; | 39 | }; |
| 44 | 40 | ||
| 45 | /// Return whether or not the given host is capable of running executables of | 41 | /// Return whether or not the given host is capable of running executables of |
| ... | @@ -71,7 +67,7 @@ pub fn getExternalExecutor(io: Io, candidate: *const std.Target, options: GetExt | ... | @@ -71,7 +67,7 @@ pub fn getExternalExecutor(io: Io, candidate: *const std.Target, options: GetExt |
| 71 | var bad_result: Executor = .bad_os_or_cpu; | 67 | var bad_result: Executor = .bad_os_or_cpu; |
| 72 | 68 | ||
| 73 | if (os_match and cpu_ok) native: { | 69 | if (os_match and cpu_ok) native: { |
| 74 | if (options.link_libc) { | 70 | if (options.link_libc and options.link_mode == .dynamic) { |
| 75 | if (candidate.dynamic_linker.get()) |candidate_dl| { | 71 | if (candidate.dynamic_linker.get()) |candidate_dl| { |
| 76 | Io.Dir.cwd().access(io, candidate_dl, .{}) catch { | 72 | Io.Dir.cwd().access(io, candidate_dl, .{}) catch { |
| 77 | bad_result = .{ .bad_dl = candidate_dl }; | 73 | bad_result = .{ .bad_dl = candidate_dl }; |
| ... | @@ -84,9 +80,7 @@ pub fn getExternalExecutor(io: Io, candidate: *const std.Target, options: GetExt | ... | @@ -84,9 +80,7 @@ pub fn getExternalExecutor(io: Io, candidate: *const std.Target, options: GetExt |
| 84 | 80 | ||
| 85 | // If the OS match and OS is macOS and CPU is arm64, we can use Rosetta 2 | 81 | // If the OS match and OS is macOS and CPU is arm64, we can use Rosetta 2 |
| 86 | // to emulate the foreign architecture. | 82 | // to emulate the foreign architecture. |
| 87 | if (options.allow_rosetta and os_match and | 83 | if (os_match and (host_os_tag == .maccatalyst or host_os_tag == .macos) and host_cpu_arch == .aarch64) { |
| 88 | (host_os_tag == .maccatalyst or host_os_tag == .macos) and host_cpu_arch == .aarch64) | ||
| 89 | { | ||
| 90 | switch (candidate.cpu.arch) { | 84 | switch (candidate.cpu.arch) { |
| 91 | .x86_64 => return .rosetta, | 85 | .x86_64 => return .rosetta, |
| 92 | else => return bad_result, | 86 | else => return bad_result, |
| ... | @@ -94,7 +88,7 @@ pub fn getExternalExecutor(io: Io, candidate: *const std.Target, options: GetExt | ... | @@ -94,7 +88,7 @@ pub fn getExternalExecutor(io: Io, candidate: *const std.Target, options: GetExt |
| 94 | } | 88 | } |
| 95 | 89 | ||
| 96 | // If the OS matches, we can use QEMU to emulate a foreign architecture. | 90 | // If the OS matches, we can use QEMU to emulate a foreign architecture. |
| 97 | if (options.allow_qemu and os_match and (!cpu_ok or options.qemu_fixes_dl)) { | 91 | if (os_match and (!cpu_ok or options.qemu_fixes_dl)) { |
| 98 | return switch (candidate.cpu.arch) { | 92 | return switch (candidate.cpu.arch) { |
| 99 | inline .aarch64, | 93 | inline .aarch64, |
| 100 | .arm, | 94 | .arm, |
| ... | @@ -167,38 +161,32 @@ pub fn getExternalExecutor(io: Io, candidate: *const std.Target, options: GetExt | ... | @@ -167,38 +161,32 @@ pub fn getExternalExecutor(io: Io, candidate: *const std.Target, options: GetExt |
| 167 | }; | 161 | }; |
| 168 | } | 162 | } |
| 169 | 163 | ||
| 170 | if (options.allow_wasmtime and candidate.cpu.arch.isWasm()) { | 164 | if (candidate.cpu.arch.isWasm()) { |
| 171 | return .{ .wasmtime = "wasmtime" }; | 165 | return .{ .wasmtime = "wasmtime" }; |
| 172 | } | 166 | } |
| 173 | 167 | ||
| 174 | switch (candidate.os.tag) { | 168 | switch (candidate.os.tag) { |
| 175 | .windows => { | 169 | .windows => { |
| 176 | if (options.allow_wine) { | 170 | const wine_supported = switch (candidate.cpu.arch) { |
| 177 | const wine_supported = switch (candidate.cpu.arch) { | 171 | .thumb => switch (host_cpu_arch) { |
| 178 | .thumb => switch (host_cpu_arch) { | 172 | .arm, .thumb, .aarch64 => true, |
| 179 | .arm, .thumb, .aarch64 => true, | ||
| 180 | else => false, | ||
| 181 | }, | ||
| 182 | .aarch64 => host_cpu_arch == .aarch64, | ||
| 183 | .x86 => host_cpu_arch.isX86(), | ||
| 184 | .x86_64 => host_cpu_arch == .x86_64, | ||
| 185 | else => false, | 173 | else => false, |
| 186 | }; | 174 | }, |
| 187 | return if (wine_supported) .{ .wine = "wine" } else bad_result; | 175 | .aarch64 => host_cpu_arch == .aarch64, |
| 188 | } | 176 | .x86 => host_cpu_arch.isX86(), |
| 189 | return bad_result; | 177 | .x86_64 => host_cpu_arch == .x86_64, |
| 178 | else => false, | ||
| 179 | }; | ||
| 180 | return if (wine_supported) .{ .wine = "wine" } else bad_result; | ||
| 190 | }, | 181 | }, |
| 191 | .driverkit, .macos => { | 182 | .driverkit, .macos => { |
| 192 | if (options.allow_darling) { | 183 | // This check can be loosened once darling adds a QEMU-based emulation |
| 193 | // This check can be loosened once darling adds a QEMU-based emulation | 184 | // layer for non-host architectures: |
| 194 | // layer for non-host architectures: | 185 | // https://github.com/darlinghq/darling/issues/863 |
| 195 | // https://github.com/darlinghq/darling/issues/863 | 186 | if (candidate.cpu.arch != host_cpu_arch) { |
| 196 | if (candidate.cpu.arch != host_cpu_arch) { | 187 | return bad_result; |
| 197 | return bad_result; | ||
| 198 | } | ||
| 199 | return .{ .darling = "darling" }; | ||
| 200 | } | 188 | } |
| 201 | return bad_result; | 189 | return .{ .darling = "darling" }; |
| 202 | }, | 190 | }, |
| 203 | else => return bad_result, | 191 | else => return bad_result, |
| 204 | } | 192 | } |
src/Compilation.zig+3-6| ... | @@ -3842,8 +3842,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { | ... | @@ -3842,8 +3842,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { |
| 3842 | const gpa = comp.gpa; | 3842 | const gpa = comp.gpa; |
| 3843 | const io = comp.io; | 3843 | const io = comp.io; |
| 3844 | 3844 | ||
| 3845 | var bundle: ErrorBundle.Wip = undefined; | 3845 | var bundle: ErrorBundle.Wip = try .init(gpa); |
| 3846 | try bundle.init(gpa); | ||
| 3847 | defer bundle.deinit(); | 3846 | defer bundle.deinit(); |
| 3848 | 3847 | ||
| 3849 | for (comp.failed_c_objects.values()) |diag_bundle| { | 3848 | for (comp.failed_c_objects.values()) |diag_bundle| { |
| ... | @@ -5447,8 +5446,7 @@ fn reportRetryableWin32ResourceError( | ... | @@ -5447,8 +5446,7 @@ fn reportRetryableWin32ResourceError( |
| 5447 | 5446 | ||
| 5448 | win32_resource.status = .failure_retryable; | 5447 | win32_resource.status = .failure_retryable; |
| 5449 | 5448 | ||
| 5450 | var bundle: ErrorBundle.Wip = undefined; | 5449 | var bundle: ErrorBundle.Wip = try .init(comp.gpa); |
| 5451 | try bundle.init(comp.gpa); | ||
| 5452 | errdefer bundle.deinit(); | 5450 | errdefer bundle.deinit(); |
| 5453 | try bundle.addRootErrorMessage(.{ | 5451 | try bundle.addRootErrorMessage(.{ |
| 5454 | .msg = try bundle.printString("{s}", .{@errorName(err)}), | 5452 | .msg = try bundle.printString("{s}", .{@errorName(err)}), |
| ... | @@ -6835,8 +6833,7 @@ fn failCObjWithOwnedDiagBundle( | ... | @@ -6835,8 +6833,7 @@ fn failCObjWithOwnedDiagBundle( |
| 6835 | 6833 | ||
| 6836 | fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { | 6834 | fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { |
| 6837 | @branchHint(.cold); | 6835 | @branchHint(.cold); |
| 6838 | var bundle: ErrorBundle.Wip = undefined; | 6836 | var bundle: ErrorBundle.Wip = try .init(comp.gpa); |
| 6839 | try bundle.init(comp.gpa); | ||
| 6840 | errdefer bundle.deinit(); | 6837 | errdefer bundle.deinit(); |
| 6841 | try bundle.addRootErrorMessage(.{ | 6838 | try bundle.addRootErrorMessage(.{ |
| 6842 | .msg = try bundle.printString(format, args), | 6839 | .msg = try bundle.printString(format, args), |
src/Sema.zig+1-2| ... | @@ -2705,8 +2705,7 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg | ... | @@ -2705,8 +2705,7 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg |
| 2705 | assert(sema.err == null); | 2705 | assert(sema.err == null); |
| 2706 | 2706 | ||
| 2707 | if (build_options.enable_debug_extensions and comp.debug_compile_errors) { | 2707 | if (build_options.enable_debug_extensions and comp.debug_compile_errors) { |
| 2708 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 2708 | var wip_errors = std.zig.ErrorBundle.Wip.init(gpa) catch @panic("out of memory"); |
| 2709 | wip_errors.init(gpa) catch @panic("out of memory"); | ||
| 2710 | Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory"); | 2709 | Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory"); |
| 2711 | std.debug.print("compile error during Sema:\n", .{}); | 2710 | std.debug.print("compile error during Sema:\n", .{}); |
| 2712 | var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory"); | 2711 | var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory"); |
src/fmt.zig+4-8| ... | @@ -130,8 +130,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! | ... | @@ -130,8 +130,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 130 | defer zir.deinit(gpa); | 130 | defer zir.deinit(gpa); |
| 131 | 131 | ||
| 132 | if (zir.hasCompileErrors()) { | 132 | if (zir.hasCompileErrors()) { |
| 133 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 133 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa); |
| 134 | try wip_errors.init(gpa); | ||
| 135 | defer wip_errors.deinit(); | 134 | defer wip_errors.deinit(); |
| 136 | try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>"); | 135 | try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>"); |
| 137 | var error_bundle = try wip_errors.toOwnedBundle(""); | 136 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| ... | @@ -144,8 +143,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! | ... | @@ -144,8 +143,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 144 | defer zoir.deinit(gpa); | 143 | defer zoir.deinit(gpa); |
| 145 | 144 | ||
| 146 | if (zoir.hasCompileErrors()) { | 145 | if (zoir.hasCompileErrors()) { |
| 147 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 146 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa); |
| 148 | try wip_errors.init(gpa); | ||
| 149 | defer wip_errors.deinit(); | 147 | defer wip_errors.deinit(); |
| 150 | try wip_errors.addZoirErrorMessages(zoir, tree, source_code, "<stdin>"); | 148 | try wip_errors.addZoirErrorMessages(zoir, tree, source_code, "<stdin>"); |
| 151 | var error_bundle = try wip_errors.toOwnedBundle(""); | 149 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| ... | @@ -333,8 +331,7 @@ fn fmtPathFile( | ... | @@ -333,8 +331,7 @@ fn fmtPathFile( |
| 333 | defer zir.deinit(gpa); | 331 | defer zir.deinit(gpa); |
| 334 | 332 | ||
| 335 | if (zir.hasCompileErrors()) { | 333 | if (zir.hasCompileErrors()) { |
| 336 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 334 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa); |
| 337 | try wip_errors.init(gpa); | ||
| 338 | defer wip_errors.deinit(); | 335 | defer wip_errors.deinit(); |
| 339 | try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path); | 336 | try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path); |
| 340 | var error_bundle = try wip_errors.toOwnedBundle(""); | 337 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| ... | @@ -348,8 +345,7 @@ fn fmtPathFile( | ... | @@ -348,8 +345,7 @@ fn fmtPathFile( |
| 348 | defer zoir.deinit(gpa); | 345 | defer zoir.deinit(gpa); |
| 349 | 346 | ||
| 350 | if (zoir.hasCompileErrors()) { | 347 | if (zoir.hasCompileErrors()) { |
| 351 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 348 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa); |
| 352 | try wip_errors.init(gpa); | ||
| 353 | defer wip_errors.deinit(); | 349 | defer wip_errors.deinit(); |
| 354 | try wip_errors.addZoirErrorMessages(zoir, tree, source_code, file_path); | 350 | try wip_errors.addZoirErrorMessages(zoir, tree, source_code, file_path); |
| 355 | var error_bundle = try wip_errors.toOwnedBundle(""); | 351 | var error_bundle = try wip_errors.toOwnedBundle(""); |
src/link.zig+1-2| ... | @@ -2891,8 +2891,7 @@ fn resolvePathInputLib( | ... | @@ -2891,8 +2891,7 @@ fn resolvePathInputLib( |
| 2891 | 2891 | ||
| 2892 | const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items); | 2892 | const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items); |
| 2893 | if (diags.hasErrors()) { | 2893 | if (diags.hasErrors()) { |
| 2894 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 2894 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa); |
| 2895 | try wip_errors.init(gpa); | ||
| 2896 | defer wip_errors.deinit(); | 2895 | defer wip_errors.deinit(); |
| 2897 | 2896 | ||
| 2898 | try diags.addMessagesToBundle(&wip_errors, null); | 2897 | try diags.addMessagesToBundle(&wip_errors, null); |
src/main.zig+44-36| ... | @@ -3971,11 +3971,9 @@ fn buildOutputType( | ... | @@ -3971,11 +3971,9 @@ fn buildOutputType( |
| 3971 | test_exec_args.items, | 3971 | test_exec_args.items, |
| 3972 | self_exe_path_or_argv0, | 3972 | self_exe_path_or_argv0, |
| 3973 | arg_mode, | 3973 | arg_mode, |
| 3974 | target, | ||
| 3975 | &comp_destroyed, | 3974 | &comp_destroyed, |
| 3976 | all_args, | 3975 | all_args, |
| 3977 | runtime_args_start, | 3976 | runtime_args_start, |
| 3978 | create_module.resolved_options.link_libc, | ||
| 3979 | test_execve, | 3977 | test_execve, |
| 3980 | environ_map, | 3978 | environ_map, |
| 3981 | ); | 3979 | ); |
| ... | @@ -4410,6 +4408,15 @@ fn serve( | ... | @@ -4410,6 +4408,15 @@ fn serve( |
| 4410 | var server: Server = .{ .in = in, .out = out }; | 4408 | var server: Server = .{ .in = in, .out = out }; |
| 4411 | try server.serveStringMessage(.zig_version, build_options.version); | 4409 | try server.serveStringMessage(.zig_version, build_options.version); |
| 4412 | 4410 | ||
| 4411 | try server.serveConfig(.{ .flags = .{ | ||
| 4412 | .output_mode = comp.config.output_mode, | ||
| 4413 | .link_mode = comp.config.link_mode, | ||
| 4414 | .link_libc = comp.config.link_libc, | ||
| 4415 | .link_libcpp = comp.config.link_libcpp, | ||
| 4416 | .link_libunwind = comp.config.link_libunwind, | ||
| 4417 | .pie = comp.config.pie, | ||
| 4418 | } }); | ||
| 4419 | |||
| 4413 | var child_pid: ?std.process.Child.Id = null; | 4420 | var child_pid: ?std.process.Child.Id = null; |
| 4414 | 4421 | ||
| 4415 | const main_progress_node = std.Progress.start(io, .{}); | 4422 | const main_progress_node = std.Progress.start(io, .{}); |
| ... | @@ -4482,8 +4489,6 @@ fn serve( | ... | @@ -4482,8 +4489,6 @@ fn serve( |
| 4482 | // test_exec_args, | 4489 | // test_exec_args, |
| 4483 | // self_exe_path.?, | 4490 | // self_exe_path.?, |
| 4484 | // arg_mode, | 4491 | // arg_mode, |
| 4485 | // target, | ||
| 4486 | // true, | ||
| 4487 | // &comp_destroyed, | 4492 | // &comp_destroyed, |
| 4488 | // all_args, | 4493 | // all_args, |
| 4489 | // runtime_args_start, | 4494 | // runtime_args_start, |
| ... | @@ -4634,11 +4639,9 @@ fn runOrTest( | ... | @@ -4634,11 +4639,9 @@ fn runOrTest( |
| 4634 | test_exec_args: []const ?[]const u8, | 4639 | test_exec_args: []const ?[]const u8, |
| 4635 | self_exe_path: []const u8, | 4640 | self_exe_path: []const u8, |
| 4636 | arg_mode: ArgMode, | 4641 | arg_mode: ArgMode, |
| 4637 | target: *const std.Target, | ||
| 4638 | comp_destroyed: *bool, | 4642 | comp_destroyed: *bool, |
| 4639 | all_args: []const []const u8, | 4643 | all_args: []const []const u8, |
| 4640 | runtime_args_start: ?usize, | 4644 | runtime_args_start: ?usize, |
| 4641 | link_libc: bool, | ||
| 4642 | test_execve: bool, | 4645 | test_execve: bool, |
| 4643 | environ_map: *process.Environ.Map, | 4646 | environ_map: *process.Environ.Map, |
| 4644 | ) !void { | 4647 | ) !void { |
| ... | @@ -4686,7 +4689,12 @@ fn runOrTest( | ... | @@ -4686,7 +4689,12 @@ fn runOrTest( |
| 4686 | _ = try io.lockStderr(&.{}, .no_color); | 4689 | _ = try io.lockStderr(&.{}, .no_color); |
| 4687 | const err = process.replace(io, .{ .argv = argv.items, .environ_map = environ_map }); | 4690 | const err = process.replace(io, .{ .argv = argv.items, .environ_map = environ_map }); |
| 4688 | io.unlockStderr(); | 4691 | io.unlockStderr(); |
| 4689 | try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc); | 4692 | try warnAboutForeignBinaries(io, arena, .{ |
| 4693 | .arg_mode = arg_mode, | ||
| 4694 | .target = comp.getTarget(), | ||
| 4695 | .link_mode = comp.config.link_mode, | ||
| 4696 | .link_libc = comp.config.link_libc, | ||
| 4697 | }); | ||
| 4690 | const cmd = try std.mem.join(arena, " ", argv.items); | 4698 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 4691 | fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd }); | 4699 | fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd }); |
| 4692 | } else if (!process.can_spawn) { | 4700 | } else if (!process.can_spawn) { |
| ... | @@ -4716,7 +4724,12 @@ fn runOrTest( | ... | @@ -4716,7 +4724,12 @@ fn runOrTest( |
| 4716 | }); | 4724 | }); |
| 4717 | 4725 | ||
| 4718 | const term = term_result catch |err| { | 4726 | const term = term_result catch |err| { |
| 4719 | try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc); | 4727 | try warnAboutForeignBinaries(io, arena, .{ |
| 4728 | .arg_mode = arg_mode, | ||
| 4729 | .target = comp.getTarget(), | ||
| 4730 | .link_mode = comp.config.link_mode, | ||
| 4731 | .link_libc = comp.config.link_libc, | ||
| 4732 | }); | ||
| 4720 | const cmd = try std.mem.join(arena, " ", argv.items); | 4733 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 4721 | fatal("the following command failed with {t}:\n{s}", .{ err, cmd }); | 4734 | fatal("the following command failed with {t}:\n{s}", .{ err, cmd }); |
| 4722 | }; | 4735 | }; |
| ... | @@ -5647,8 +5660,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map: | ... | @@ -5647,8 +5660,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map: |
| 5647 | const zir = try AstGen.generate(arena, tree); | 5660 | const zir = try AstGen.generate(arena, tree); |
| 5648 | 5661 | ||
| 5649 | if (zir.hasCompileErrors()) { | 5662 | if (zir.hasCompileErrors()) { |
| 5650 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 5663 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena); |
| 5651 | try wip_errors.init(arena); | ||
| 5652 | try wip_errors.addZirErrorMessages(zir, tree, source, display_path); | 5664 | try wip_errors.addZirErrorMessages(zir, tree, source, display_path); |
| 5653 | var error_bundle = try wip_errors.toOwnedBundle(""); | 5665 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 5654 | try error_bundle.renderToStderr(io, .{}, color); | 5666 | try error_bundle.renderToStderr(io, .{}, color); |
| ... | @@ -5718,8 +5730,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map: | ... | @@ -5718,8 +5730,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map: |
| 5718 | .zon => { | 5730 | .zon => { |
| 5719 | const zoir = try ZonGen.generate(arena, tree, .{}); | 5731 | const zoir = try ZonGen.generate(arena, tree, .{}); |
| 5720 | if (zoir.hasCompileErrors()) { | 5732 | if (zoir.hasCompileErrors()) { |
| 5721 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 5733 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena); |
| 5722 | try wip_errors.init(arena); | ||
| 5723 | try wip_errors.addZoirErrorMessages(zoir, tree, source, display_path); | 5734 | try wip_errors.addZoirErrorMessages(zoir, tree, source, display_path); |
| 5724 | var error_bundle = try wip_errors.toOwnedBundle(""); | 5735 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 5725 | error_bundle.renderToStderr(io, .{}, color) catch {}; | 5736 | error_bundle.renderToStderr(io, .{}, color) catch {}; |
| ... | @@ -5812,8 +5823,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8, environ_map | ... | @@ -5812,8 +5823,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8, environ_map |
| 5812 | const old_zir = try AstGen.generate(arena, old_tree); | 5823 | const old_zir = try AstGen.generate(arena, old_tree); |
| 5813 | 5824 | ||
| 5814 | if (old_zir.loweringFailed()) { | 5825 | if (old_zir.loweringFailed()) { |
| 5815 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 5826 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena); |
| 5816 | try wip_errors.init(arena); | ||
| 5817 | try wip_errors.addZirErrorMessages(old_zir, old_tree, old_source, old_source_path); | 5827 | try wip_errors.addZirErrorMessages(old_zir, old_tree, old_source, old_source_path); |
| 5818 | var error_bundle = try wip_errors.toOwnedBundle(""); | 5828 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 5819 | error_bundle.renderToStderr(io, .{}, color) catch {}; | 5829 | error_bundle.renderToStderr(io, .{}, color) catch {}; |
| ... | @@ -5824,8 +5834,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8, environ_map | ... | @@ -5824,8 +5834,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8, environ_map |
| 5824 | const new_zir = try AstGen.generate(arena, new_tree); | 5834 | const new_zir = try AstGen.generate(arena, new_tree); |
| 5825 | 5835 | ||
| 5826 | if (new_zir.loweringFailed()) { | 5836 | if (new_zir.loweringFailed()) { |
| 5827 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 5837 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena); |
| 5828 | try wip_errors.init(arena); | ||
| 5829 | try wip_errors.addZirErrorMessages(new_zir, new_tree, new_source, new_source_path); | 5838 | try wip_errors.addZirErrorMessages(new_zir, new_tree, new_source, new_source_path); |
| 5830 | var error_bundle = try wip_errors.toOwnedBundle(""); | 5839 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 5831 | error_bundle.renderToStderr(io, .{}, color) catch {}; | 5840 | error_bundle.renderToStderr(io, .{}, color) catch {}; |
| ... | @@ -5867,33 +5876,32 @@ fn prefixedIntArg(arg: []const u8, prefix: []const u8) ?u64 { | ... | @@ -5867,33 +5876,32 @@ fn prefixedIntArg(arg: []const u8, prefix: []const u8) ?u64 { |
| 5867 | return std.fmt.parseUnsigned(u64, number, 0) catch |err| fatal("unable to parse {q}: {t}", .{ arg, err }); | 5876 | return std.fmt.parseUnsigned(u64, number, 0) catch |err| fatal("unable to parse {q}: {t}", .{ arg, err }); |
| 5868 | } | 5877 | } |
| 5869 | 5878 | ||
| 5870 | fn warnAboutForeignBinaries( | 5879 | fn warnAboutForeignBinaries(io: Io, arena: Allocator, opts: struct { |
| 5871 | io: Io, | ||
| 5872 | arena: Allocator, | ||
| 5873 | arg_mode: ArgMode, | 5880 | arg_mode: ArgMode, |
| 5874 | target: *const std.Target, | 5881 | target: *const std.Target, |
| 5882 | link_mode: std.lang.LinkMode, | ||
| 5875 | link_libc: bool, | 5883 | link_libc: bool, |
| 5876 | ) !void { | 5884 | }) !void { |
| 5877 | const host_query: std.Target.Query = .{}; | 5885 | const host_target = std.zig.resolveTargetQueryOrFatal(io, .{}); |
| 5878 | const host_target = std.zig.resolveTargetQueryOrFatal(io, host_query); | ||
| 5879 | 5886 | ||
| 5880 | switch (std.zig.system.getExternalExecutor(io, target, .{ | 5887 | switch (std.zig.system.getExternalExecutor(io, opts.target, .{ |
| 5881 | .host_cpu_arch = host_target.cpu.arch, | 5888 | .host_cpu_arch = host_target.cpu.arch, |
| 5882 | .host_os_tag = host_target.os.tag, | 5889 | .host_os_tag = host_target.os.tag, |
| 5883 | .link_libc = link_libc, | 5890 | .link_mode = opts.link_mode, |
| 5891 | .link_libc = opts.link_libc, | ||
| 5884 | })) { | 5892 | })) { |
| 5885 | .native => return, | 5893 | .native => return, |
| 5886 | .rosetta => { | 5894 | .rosetta => { |
| 5887 | const host_name = try host_target.zigTriple(arena); | 5895 | const host_name = try host_target.zigTriple(arena); |
| 5888 | const foreign_name = try target.zigTriple(arena); | 5896 | const foreign_name = try opts.target.zigTriple(arena); |
| 5889 | warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}). Consider installing Rosetta.", .{ | 5897 | warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}). Consider installing Rosetta.", .{ |
| 5890 | host_name, foreign_name, | 5898 | host_name, foreign_name, |
| 5891 | }); | 5899 | }); |
| 5892 | }, | 5900 | }, |
| 5893 | .qemu => |qemu| { | 5901 | .qemu => |qemu| { |
| 5894 | const host_name = try host_target.zigTriple(arena); | 5902 | const host_name = try host_target.zigTriple(arena); |
| 5895 | const foreign_name = try target.zigTriple(arena); | 5903 | const foreign_name = try opts.target.zigTriple(arena); |
| 5896 | switch (arg_mode) { | 5904 | switch (opts.arg_mode) { |
| 5897 | .zig_test => warn( | 5905 | .zig_test => warn( |
| 5898 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | 5906 | "the host system ({s}) does not appear to be capable of executing binaries " ++ |
| 5899 | "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ | 5907 | "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ |
| ... | @@ -5909,8 +5917,8 @@ fn warnAboutForeignBinaries( | ... | @@ -5909,8 +5917,8 @@ fn warnAboutForeignBinaries( |
| 5909 | }, | 5917 | }, |
| 5910 | .wine => |wine| { | 5918 | .wine => |wine| { |
| 5911 | const host_name = try host_target.zigTriple(arena); | 5919 | const host_name = try host_target.zigTriple(arena); |
| 5912 | const foreign_name = try target.zigTriple(arena); | 5920 | const foreign_name = try opts.target.zigTriple(arena); |
| 5913 | switch (arg_mode) { | 5921 | switch (opts.arg_mode) { |
| 5914 | .zig_test => warn( | 5922 | .zig_test => warn( |
| 5915 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | 5923 | "the host system ({s}) does not appear to be capable of executing binaries " ++ |
| 5916 | "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ | 5924 | "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ |
| ... | @@ -5926,8 +5934,8 @@ fn warnAboutForeignBinaries( | ... | @@ -5926,8 +5934,8 @@ fn warnAboutForeignBinaries( |
| 5926 | }, | 5934 | }, |
| 5927 | .wasmtime => |wasmtime| { | 5935 | .wasmtime => |wasmtime| { |
| 5928 | const host_name = try host_target.zigTriple(arena); | 5936 | const host_name = try host_target.zigTriple(arena); |
| 5929 | const foreign_name = try target.zigTriple(arena); | 5937 | const foreign_name = try opts.target.zigTriple(arena); |
| 5930 | switch (arg_mode) { | 5938 | switch (opts.arg_mode) { |
| 5931 | .zig_test => warn( | 5939 | .zig_test => warn( |
| 5932 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | 5940 | "the host system ({s}) does not appear to be capable of executing binaries " ++ |
| 5933 | "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ | 5941 | "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ |
| ... | @@ -5943,8 +5951,8 @@ fn warnAboutForeignBinaries( | ... | @@ -5943,8 +5951,8 @@ fn warnAboutForeignBinaries( |
| 5943 | }, | 5951 | }, |
| 5944 | .darling => |darling| { | 5952 | .darling => |darling| { |
| 5945 | const host_name = try host_target.zigTriple(arena); | 5953 | const host_name = try host_target.zigTriple(arena); |
| 5946 | const foreign_name = try target.zigTriple(arena); | 5954 | const foreign_name = try opts.target.zigTriple(arena); |
| 5947 | switch (arg_mode) { | 5955 | switch (opts.arg_mode) { |
| 5948 | .zig_test => warn( | 5956 | .zig_test => warn( |
| 5949 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | 5957 | "the host system ({s}) does not appear to be capable of executing binaries " ++ |
| 5950 | "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ | 5958 | "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ |
| ... | @@ -5960,7 +5968,7 @@ fn warnAboutForeignBinaries( | ... | @@ -5960,7 +5968,7 @@ fn warnAboutForeignBinaries( |
| 5960 | }, | 5968 | }, |
| 5961 | .bad_dl => |foreign_dl| { | 5969 | .bad_dl => |foreign_dl| { |
| 5962 | const host_dl = host_target.dynamic_linker.get() orelse "(none)"; | 5970 | const host_dl = host_target.dynamic_linker.get() orelse "(none)"; |
| 5963 | const tip_suffix = switch (arg_mode) { | 5971 | const tip_suffix = switch (opts.arg_mode) { |
| 5964 | .zig_test => ", '--test-no-exec', or '--test-cmd'", | 5972 | .zig_test => ", '--test-no-exec', or '--test-cmd'", |
| 5965 | else => "", | 5973 | else => "", |
| 5966 | }; | 5974 | }; |
| ... | @@ -5970,8 +5978,8 @@ fn warnAboutForeignBinaries( | ... | @@ -5970,8 +5978,8 @@ fn warnAboutForeignBinaries( |
| 5970 | }, | 5978 | }, |
| 5971 | .bad_os_or_cpu => { | 5979 | .bad_os_or_cpu => { |
| 5972 | const host_name = try host_target.zigTriple(arena); | 5980 | const host_name = try host_target.zigTriple(arena); |
| 5973 | const foreign_name = try target.zigTriple(arena); | 5981 | const foreign_name = try opts.target.zigTriple(arena); |
| 5974 | const tip_suffix = switch (arg_mode) { | 5982 | const tip_suffix = switch (opts.arg_mode) { |
| 5975 | .zig_test => ". Consider using '--test-no-exec' or '--test-cmd'", | 5983 | .zig_test => ". Consider using '--test-no-exec' or '--test-cmd'", |
| 5976 | else => "", | 5984 | else => "", |
| 5977 | }; | 5985 | }; |
test/incremental/add_decl+45-18| ... | @@ -1,15 +1,21 @@ | ... | @@ -1,15 +1,21 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | pub fn main() !void { | 7 | pub fn main() !void { |
| 5 | try std.Io.File.stdout().writeStreamingAll(io, foo); | 8 | try std.Io.File.stdout().writeStreamingAll(io, foo); |
| 6 | } | 9 | } |
| 7 | const foo = "good morning\n"; | 10 | const foo = "good morning\n"; |
| 8 | const io = std.Io.Threaded.global_single_threaded.io(); | 11 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 9 | #expect_stdout="good morning\n" | 12 | #} |
| 13 | #update | ||
| 14 | #check stdout { | ||
| 15 | good morning | ||
| 16 | #} | ||
| 10 | 17 | ||
| 11 | #update=add new declaration | 18 | #write main.zig { |
| 12 | #file=main.zig | ||
| 13 | const std = @import("std"); | 19 | const std = @import("std"); |
| 14 | pub fn main() !void { | 20 | pub fn main() !void { |
| 15 | try std.Io.File.stdout().writeStreamingAll(io, foo); | 21 | try std.Io.File.stdout().writeStreamingAll(io, foo); |
| ... | @@ -17,10 +23,14 @@ pub fn main() !void { | ... | @@ -17,10 +23,14 @@ pub fn main() !void { |
| 17 | const foo = "good morning\n"; | 23 | const foo = "good morning\n"; |
| 18 | const bar = "good evening\n"; | 24 | const bar = "good evening\n"; |
| 19 | const io = std.Io.Threaded.global_single_threaded.io(); | 25 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 20 | #expect_stdout="good morning\n" | 26 | #} |
| 27 | #update { | ||
| 28 | add new declaration#} | ||
| 29 | #check stdout { | ||
| 30 | good morning | ||
| 31 | #} | ||
| 21 | 32 | ||
| 22 | #update=reference new declaration | 33 | #write main.zig { |
| 23 | #file=main.zig | ||
| 24 | const std = @import("std"); | 34 | const std = @import("std"); |
| 25 | pub fn main() !void { | 35 | pub fn main() !void { |
| 26 | try std.Io.File.stdout().writeStreamingAll(io, bar); | 36 | try std.Io.File.stdout().writeStreamingAll(io, bar); |
| ... | @@ -28,10 +38,14 @@ pub fn main() !void { | ... | @@ -28,10 +38,14 @@ pub fn main() !void { |
| 28 | const foo = "good morning\n"; | 38 | const foo = "good morning\n"; |
| 29 | const bar = "good evening\n"; | 39 | const bar = "good evening\n"; |
| 30 | const io = std.Io.Threaded.global_single_threaded.io(); | 40 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 31 | #expect_stdout="good evening\n" | 41 | #} |
| 42 | #update { | ||
| 43 | reference new declaration#} | ||
| 44 | #check stdout { | ||
| 45 | good evening | ||
| 46 | #} | ||
| 32 | 47 | ||
| 33 | #update=reference missing declaration | 48 | #write main.zig { |
| 34 | #file=main.zig | ||
| 35 | const std = @import("std"); | 49 | const std = @import("std"); |
| 36 | pub fn main() !void { | 50 | pub fn main() !void { |
| 37 | try std.Io.File.stdout().writeStreamingAll(io, qux); | 51 | try std.Io.File.stdout().writeStreamingAll(io, qux); |
| ... | @@ -39,10 +53,14 @@ pub fn main() !void { | ... | @@ -39,10 +53,14 @@ pub fn main() !void { |
| 39 | const foo = "good morning\n"; | 53 | const foo = "good morning\n"; |
| 40 | const bar = "good evening\n"; | 54 | const bar = "good evening\n"; |
| 41 | const io = std.Io.Threaded.global_single_threaded.io(); | 55 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 42 | #expect_error=main.zig:3:52: error: use of undeclared identifier 'qux' | 56 | #} |
| 57 | #update { | ||
| 58 | reference missing declaration#} | ||
| 59 | #check errors { | ||
| 60 | main.zig:3:52: error: use of undeclared identifier 'qux' | ||
| 61 | #} | ||
| 43 | 62 | ||
| 44 | #update=add missing declaration | 63 | #write main.zig { |
| 45 | #file=main.zig | ||
| 46 | const std = @import("std"); | 64 | const std = @import("std"); |
| 47 | pub fn main() !void { | 65 | pub fn main() !void { |
| 48 | try std.Io.File.stdout().writeStreamingAll(io, qux); | 66 | try std.Io.File.stdout().writeStreamingAll(io, qux); |
| ... | @@ -51,14 +69,23 @@ const foo = "good morning\n"; | ... | @@ -51,14 +69,23 @@ const foo = "good morning\n"; |
| 51 | const bar = "good evening\n"; | 69 | const bar = "good evening\n"; |
| 52 | const qux = "good night\n"; | 70 | const qux = "good night\n"; |
| 53 | const io = std.Io.Threaded.global_single_threaded.io(); | 71 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 54 | #expect_stdout="good night\n" | 72 | #} |
| 73 | #update { | ||
| 74 | add missing declaration#} | ||
| 75 | #check stdout { | ||
| 76 | good night | ||
| 77 | #} | ||
| 55 | 78 | ||
| 56 | #update=remove unused declarations | 79 | #write main.zig { |
| 57 | #file=main.zig | ||
| 58 | const std = @import("std"); | 80 | const std = @import("std"); |
| 59 | pub fn main() !void { | 81 | pub fn main() !void { |
| 60 | try std.Io.File.stdout().writeStreamingAll(io, qux); | 82 | try std.Io.File.stdout().writeStreamingAll(io, qux); |
| 61 | } | 83 | } |
| 62 | const qux = "good night\n"; | 84 | const qux = "good night\n"; |
| 63 | const io = std.Io.Threaded.global_single_threaded.io(); | 85 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 64 | #expect_stdout="good night\n" | 86 | #} |
| 87 | #update { | ||
| 88 | remove unused declarations#} | ||
| 89 | #check stdout { | ||
| 90 | good night | ||
| 91 | #} |
test/incremental/add_decl_namespaced+46-19| ... | @@ -1,15 +1,21 @@ | ... | @@ -1,15 +1,21 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | pub fn main() !void { | 7 | pub fn main() !void { |
| 5 | try std.Io.File.stdout().writeStreamingAll(io, @This().foo); | 8 | try std.Io.File.stdout().writeStreamingAll(io, @This().foo); |
| 6 | } | 9 | } |
| 7 | const foo = "good morning\n"; | 10 | const foo = "good morning\n"; |
| 8 | const io = std.Io.Threaded.global_single_threaded.io(); | 11 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 9 | #expect_stdout="good morning\n" | 12 | #} |
| 13 | #update | ||
| 14 | #check stdout { | ||
| 15 | good morning | ||
| 16 | #} | ||
| 10 | 17 | ||
| 11 | #update=add new declaration | 18 | #write main.zig { |
| 12 | #file=main.zig | ||
| 13 | const std = @import("std"); | 19 | const std = @import("std"); |
| 14 | pub fn main() !void { | 20 | pub fn main() !void { |
| 15 | try std.Io.File.stdout().writeStreamingAll(io, @This().foo); | 21 | try std.Io.File.stdout().writeStreamingAll(io, @This().foo); |
| ... | @@ -17,10 +23,14 @@ pub fn main() !void { | ... | @@ -17,10 +23,14 @@ pub fn main() !void { |
| 17 | const foo = "good morning\n"; | 23 | const foo = "good morning\n"; |
| 18 | const bar = "good evening\n"; | 24 | const bar = "good evening\n"; |
| 19 | const io = std.Io.Threaded.global_single_threaded.io(); | 25 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 20 | #expect_stdout="good morning\n" | 26 | #} |
| 27 | #update { | ||
| 28 | add new declaration#} | ||
| 29 | #check stdout { | ||
| 30 | good morning | ||
| 31 | #} | ||
| 21 | 32 | ||
| 22 | #update=reference new declaration | 33 | #write main.zig { |
| 23 | #file=main.zig | ||
| 24 | const std = @import("std"); | 34 | const std = @import("std"); |
| 25 | pub fn main() !void { | 35 | pub fn main() !void { |
| 26 | try std.Io.File.stdout().writeStreamingAll(io, @This().bar); | 36 | try std.Io.File.stdout().writeStreamingAll(io, @This().bar); |
| ... | @@ -28,10 +38,14 @@ pub fn main() !void { | ... | @@ -28,10 +38,14 @@ pub fn main() !void { |
| 28 | const foo = "good morning\n"; | 38 | const foo = "good morning\n"; |
| 29 | const bar = "good evening\n"; | 39 | const bar = "good evening\n"; |
| 30 | const io = std.Io.Threaded.global_single_threaded.io(); | 40 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 31 | #expect_stdout="good evening\n" | 41 | #} |
| 42 | #update { | ||
| 43 | reference new declaration#} | ||
| 44 | #check stdout { | ||
| 45 | good evening | ||
| 46 | #} | ||
| 32 | 47 | ||
| 33 | #update=reference missing declaration | 48 | #write main.zig { |
| 34 | #file=main.zig | ||
| 35 | const std = @import("std"); | 49 | const std = @import("std"); |
| 36 | pub fn main() !void { | 50 | pub fn main() !void { |
| 37 | try std.Io.File.stdout().writeStreamingAll(io, @This().qux); | 51 | try std.Io.File.stdout().writeStreamingAll(io, @This().qux); |
| ... | @@ -39,11 +53,15 @@ pub fn main() !void { | ... | @@ -39,11 +53,15 @@ pub fn main() !void { |
| 39 | const foo = "good morning\n"; | 53 | const foo = "good morning\n"; |
| 40 | const bar = "good evening\n"; | 54 | const bar = "good evening\n"; |
| 41 | const io = std.Io.Threaded.global_single_threaded.io(); | 55 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 42 | #expect_error=main.zig:3:59: error: root source file struct 'main' has no member named 'qux' | 56 | #} |
| 43 | #expect_error=main.zig:1:1: note: struct declared here | 57 | #update { |
| 58 | reference missing declaration#} | ||
| 59 | #check errors { | ||
| 60 | main.zig:3:59: error: root source file struct 'main' has no member named 'qux' | ||
| 61 | main.zig:1:1: note: struct declared here | ||
| 62 | #} | ||
| 44 | 63 | ||
| 45 | #update=add missing declaration | 64 | #write main.zig { |
| 46 | #file=main.zig | ||
| 47 | const std = @import("std"); | 65 | const std = @import("std"); |
| 48 | pub fn main() !void { | 66 | pub fn main() !void { |
| 49 | try std.Io.File.stdout().writeStreamingAll(io, @This().qux); | 67 | try std.Io.File.stdout().writeStreamingAll(io, @This().qux); |
| ... | @@ -52,14 +70,23 @@ const foo = "good morning\n"; | ... | @@ -52,14 +70,23 @@ const foo = "good morning\n"; |
| 52 | const bar = "good evening\n"; | 70 | const bar = "good evening\n"; |
| 53 | const qux = "good night\n"; | 71 | const qux = "good night\n"; |
| 54 | const io = std.Io.Threaded.global_single_threaded.io(); | 72 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 55 | #expect_stdout="good night\n" | 73 | #} |
| 74 | #update { | ||
| 75 | add missing declaration#} | ||
| 76 | #check stdout { | ||
| 77 | good night | ||
| 78 | #} | ||
| 56 | 79 | ||
| 57 | #update=remove unused declarations | 80 | #write main.zig { |
| 58 | #file=main.zig | ||
| 59 | const std = @import("std"); | 81 | const std = @import("std"); |
| 60 | pub fn main() !void { | 82 | pub fn main() !void { |
| 61 | try std.Io.File.stdout().writeStreamingAll(io, @This().qux); | 83 | try std.Io.File.stdout().writeStreamingAll(io, @This().qux); |
| 62 | } | 84 | } |
| 63 | const qux = "good night\n"; | 85 | const qux = "good night\n"; |
| 64 | const io = std.Io.Threaded.global_single_threaded.io(); | 86 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 65 | #expect_stdout="good night\n" | 87 | #} |
| 88 | #update { | ||
| 89 | remove unused declarations#} | ||
| 90 | #check stdout { | ||
| 91 | good night | ||
| 92 | #} |
test/incremental/add_field_and_nested_struct_uses_changed_decl+16-6| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() void { | 6 | pub fn main() void { |
| 4 | _ = @as(S, undefined); | 7 | _ = @as(S, undefined); |
| 5 | } | 8 | } |
| ... | @@ -9,9 +12,12 @@ const SomeType = u8; | ... | @@ -9,9 +12,12 @@ const SomeType = u8; |
| 9 | const S = struct { | 12 | const S = struct { |
| 10 | foo: struct { inner: SomeType }, | 13 | foo: struct { inner: SomeType }, |
| 11 | }; | 14 | }; |
| 12 | #expect_stdout="" | 15 | #} |
| 13 | #update=add field to outer struct, change decl used by inner struct | 16 | #update |
| 14 | #file=main.zig | 17 | #check stdout { |
| 18 | #} | ||
| 19 | |||
| 20 | #write main.zig { | ||
| 15 | pub fn main() void { | 21 | pub fn main() void { |
| 16 | _ = @as(S, undefined); | 22 | _ = @as(S, undefined); |
| 17 | } | 23 | } |
| ... | @@ -22,4 +28,8 @@ const S = struct { | ... | @@ -22,4 +28,8 @@ const S = struct { |
| 22 | foo: struct { inner: SomeType }, | 28 | foo: struct { inner: SomeType }, |
| 23 | bar: u32, | 29 | bar: u32, |
| 24 | }; | 30 | }; |
| 25 | #expect_stdout="" | 31 | #} |
| 32 | #update { | ||
| 33 | add field to outer struct, change decl used by inner struct#} | ||
| 34 | #check stdout { | ||
| 35 | #} |
test/incremental/add_remove_struct_fields deleted-93| ... | @@ -1,93 +0,0 @@ | ||
| 1 | #update=initial version | ||
| 2 | #file=main.zig | ||
| 3 | const S = struct { x: u8 }; | ||
| 4 | pub fn main(init: std.process.Init) !void { | ||
| 5 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 6 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 7 | error.WriteFailed => return stdout_writer.err.?, | ||
| 8 | }; | ||
| 9 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 10 | error.WriteFailed => return stdout_writer.err.?, | ||
| 11 | }; | ||
| 12 | } | ||
| 13 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 14 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 15 | } | ||
| 16 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 17 | const val: S = .{ .x = 100 }; | ||
| 18 | try w.print("{d}\n", .{val.x}); | ||
| 19 | } | ||
| 20 | const std = @import("std"); | ||
| 21 | const Writer = std.Io.Writer; | ||
| 22 | #expect_stdout="1 100\n" | ||
| 23 | |||
| 24 | #update=add a field | ||
| 25 | #file=main.zig | ||
| 26 | const S = struct { x: u8, y: u16 = 200 }; | ||
| 27 | pub fn main(init: std.process.Init) !void { | ||
| 28 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 29 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 30 | error.WriteFailed => return stdout_writer.err.?, | ||
| 31 | }; | ||
| 32 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 33 | error.WriteFailed => return stdout_writer.err.?, | ||
| 34 | }; | ||
| 35 | } | ||
| 36 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 37 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 38 | } | ||
| 39 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 40 | const val: S = .{ .x = 100 }; | ||
| 41 | try w.print("{d}\n", .{val.x}); | ||
| 42 | } | ||
| 43 | const std = @import("std"); | ||
| 44 | const Writer = std.Io.Writer; | ||
| 45 | #expect_stdout="2 100\n" | ||
| 46 | |||
| 47 | #update=remove all fields | ||
| 48 | #file=main.zig | ||
| 49 | const S = struct {}; | ||
| 50 | pub fn main(init: std.process.Init) !void { | ||
| 51 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 52 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 53 | error.WriteFailed => return stdout_writer.err.?, | ||
| 54 | }; | ||
| 55 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 56 | error.WriteFailed => return stdout_writer.err.?, | ||
| 57 | }; | ||
| 58 | } | ||
| 59 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 60 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 61 | } | ||
| 62 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 63 | const val: S = .{ .x = 100 }; | ||
| 64 | try w.print("{d}\n", .{val.x}); | ||
| 65 | } | ||
| 66 | const std = @import("std"); | ||
| 67 | const Writer = std.Io.Writer; | ||
| 68 | #expect_error=main.zig:15:24: error: no field named 'x' in struct 'main.S' | ||
| 69 | #expect_error=main.zig:1:11: note: struct declared here | ||
| 70 | |||
| 71 | #update=remove reference to non-existent field | ||
| 72 | #file=main.zig | ||
| 73 | const S = struct {}; | ||
| 74 | pub fn main(init: std.process.Init) !void { | ||
| 75 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 76 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 77 | error.WriteFailed => return stdout_writer.err.?, | ||
| 78 | }; | ||
| 79 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 80 | error.WriteFailed => return stdout_writer.err.?, | ||
| 81 | }; | ||
| 82 | } | ||
| 83 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 84 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 85 | } | ||
| 86 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 87 | //const val: S = .{ .x = 100 }; | ||
| 88 | //try w.print("{d}\n", .{val.x}); | ||
| 89 | try w.writeAll("<no fields>\n"); | ||
| 90 | } | ||
| 91 | const std = @import("std"); | ||
| 92 | const Writer = std.Io.Writer; | ||
| 93 | #expect_stdout="0 <no fields>\n" | ||
test/incremental/add_remove_struct_fields/0/main.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | const S = struct { x: u8 }; | ||
| 2 | pub fn main(init: std.process.Init) !void { | ||
| 3 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 4 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 5 | error.WriteFailed => return stdout_writer.err.?, | ||
| 6 | }; | ||
| 7 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 8 | error.WriteFailed => return stdout_writer.err.?, | ||
| 9 | }; | ||
| 10 | } | ||
| 11 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 12 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 13 | } | ||
| 14 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 15 | const val: S = .{ .x = 100 }; | ||
| 16 | try w.print("{d}\n", .{val.x}); | ||
| 17 | } | ||
| 18 | const std = @import("std"); | ||
| 19 | const Writer = std.Io.Writer; | ||
test/incremental/add_remove_struct_fields/1/main.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | const S = struct { x: u8, y: u16 = 200 }; | ||
| 2 | pub fn main(init: std.process.Init) !void { | ||
| 3 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 4 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 5 | error.WriteFailed => return stdout_writer.err.?, | ||
| 6 | }; | ||
| 7 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 8 | error.WriteFailed => return stdout_writer.err.?, | ||
| 9 | }; | ||
| 10 | } | ||
| 11 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 12 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 13 | } | ||
| 14 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 15 | const val: S = .{ .x = 100 }; | ||
| 16 | try w.print("{d}\n", .{val.x}); | ||
| 17 | } | ||
| 18 | const std = @import("std"); | ||
| 19 | const Writer = std.Io.Writer; | ||
test/incremental/add_remove_struct_fields/2/main.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | const S = struct {}; | ||
| 2 | pub fn main(init: std.process.Init) !void { | ||
| 3 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 4 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 5 | error.WriteFailed => return stdout_writer.err.?, | ||
| 6 | }; | ||
| 7 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 8 | error.WriteFailed => return stdout_writer.err.?, | ||
| 9 | }; | ||
| 10 | } | ||
| 11 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 12 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 13 | } | ||
| 14 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 15 | const val: S = .{ .x = 100 }; | ||
| 16 | try w.print("{d}\n", .{val.x}); | ||
| 17 | } | ||
| 18 | const std = @import("std"); | ||
| 19 | const Writer = std.Io.Writer; | ||
test/incremental/add_remove_struct_fields/3/main.zig created+20| ... | @@ -0,0 +1,20 @@ | ||
| 1 | const S = struct {}; | ||
| 2 | pub fn main(init: std.process.Init) !void { | ||
| 3 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 4 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 5 | error.WriteFailed => return stdout_writer.err.?, | ||
| 6 | }; | ||
| 7 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 8 | error.WriteFailed => return stdout_writer.err.?, | ||
| 9 | }; | ||
| 10 | } | ||
| 11 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 12 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 13 | } | ||
| 14 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 15 | //const val: S = .{ .x = 100 }; | ||
| 16 | //try w.print("{d}\n", .{val.x}); | ||
| 17 | try w.writeAll("<no fields>\n"); | ||
| 18 | } | ||
| 19 | const std = @import("std"); | ||
| 20 | const Writer = std.Io.Writer; | ||
test/incremental/add_remove_struct_fields/manifest created+31| ... | @@ -0,0 +1,31 @@ | ||
| 1 | #exe { | ||
| 2 | -Mroot=main.zig | ||
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig 0/main.zig | ||
| 6 | #update | ||
| 7 | #check stdout { | ||
| 8 | 1 100 | ||
| 9 | #} | ||
| 10 | |||
| 11 | #write main.zig 1/main.zig | ||
| 12 | #update { | ||
| 13 | add a field#} | ||
| 14 | #check stdout { | ||
| 15 | 2 100 | ||
| 16 | #} | ||
| 17 | |||
| 18 | #write main.zig 2/main.zig | ||
| 19 | #update { | ||
| 20 | remove all fields#} | ||
| 21 | #check errors { | ||
| 22 | main.zig:15:24: error: no field named 'x' in struct 'main.S' | ||
| 23 | main.zig:1:11: note: struct declared here | ||
| 24 | #} | ||
| 25 | |||
| 26 | #write main.zig 3/main.zig | ||
| 27 | #update { | ||
| 28 | remove reference to non-existent field#} | ||
| 29 | #check stdout { | ||
| 30 | 0 <no fields> | ||
| 31 | #} | ||
test/incremental/add_remove_toplevel_fields deleted-96| ... | @@ -1,96 +0,0 @@ | ||
| 1 | #update=initial version | ||
| 2 | #file=main.zig | ||
| 3 | const S = @This(); | ||
| 4 | x: u8, | ||
| 5 | pub fn main(init: std.process.Init) !void { | ||
| 6 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 7 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 8 | error.WriteFailed => return stdout_writer.err.?, | ||
| 9 | }; | ||
| 10 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 11 | error.WriteFailed => return stdout_writer.err.?, | ||
| 12 | }; | ||
| 13 | } | ||
| 14 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 15 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 16 | } | ||
| 17 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 18 | const val: S = .{ .x = 100 }; | ||
| 19 | try w.print("{d}\n", .{val.x}); | ||
| 20 | } | ||
| 21 | const std = @import("std"); | ||
| 22 | const Writer = std.Io.Writer; | ||
| 23 | #expect_stdout="1 100\n" | ||
| 24 | |||
| 25 | #update=add a field | ||
| 26 | #file=main.zig | ||
| 27 | const S = @This(); | ||
| 28 | x: u8, | ||
| 29 | y: u16 = 200, | ||
| 30 | pub fn main(init: std.process.Init) !void { | ||
| 31 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 32 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 33 | error.WriteFailed => return stdout_writer.err.?, | ||
| 34 | }; | ||
| 35 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 36 | error.WriteFailed => return stdout_writer.err.?, | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 40 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 41 | } | ||
| 42 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 43 | const val: S = .{ .x = 100 }; | ||
| 44 | try w.print("{d}\n", .{val.x}); | ||
| 45 | } | ||
| 46 | const std = @import("std"); | ||
| 47 | const Writer = std.Io.Writer; | ||
| 48 | #expect_stdout="2 100\n" | ||
| 49 | |||
| 50 | #update=remove all fields | ||
| 51 | #file=main.zig | ||
| 52 | const S = @This(); | ||
| 53 | pub fn main(init: std.process.Init) !void { | ||
| 54 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 55 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 56 | error.WriteFailed => return stdout_writer.err.?, | ||
| 57 | }; | ||
| 58 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 59 | error.WriteFailed => return stdout_writer.err.?, | ||
| 60 | }; | ||
| 61 | } | ||
| 62 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 63 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 64 | } | ||
| 65 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 66 | const val: S = .{ .x = 100 }; | ||
| 67 | try w.print("{d}\n", .{val.x}); | ||
| 68 | } | ||
| 69 | const std = @import("std"); | ||
| 70 | const Writer = std.Io.Writer; | ||
| 71 | #expect_error=main.zig:15:24: error: no field named 'x' in struct 'main' | ||
| 72 | #expect_error=main.zig:1:1: note: struct declared here | ||
| 73 | |||
| 74 | #update=remove reference to non-existent field | ||
| 75 | #file=main.zig | ||
| 76 | const S = @This(); | ||
| 77 | pub fn main(init: std.process.Init) !void { | ||
| 78 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 79 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 80 | error.WriteFailed => return stdout_writer.err.?, | ||
| 81 | }; | ||
| 82 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 83 | error.WriteFailed => return stdout_writer.err.?, | ||
| 84 | }; | ||
| 85 | } | ||
| 86 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 87 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 88 | } | ||
| 89 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 90 | //const val: S = .{ .x = 100 }; | ||
| 91 | //try w.print("{d}\n", .{val.x}); | ||
| 92 | try w.writeAll("<no fields>\n"); | ||
| 93 | } | ||
| 94 | const std = @import("std"); | ||
| 95 | const Writer = std.Io.Writer; | ||
| 96 | #expect_stdout="0 <no fields>\n" | ||
test/incremental/add_remove_toplevel_fields/0/main.zig created+20| ... | @@ -0,0 +1,20 @@ | ||
| 1 | const S = @This(); | ||
| 2 | x: u8, | ||
| 3 | pub fn main(init: std.process.Init) !void { | ||
| 4 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 5 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 6 | error.WriteFailed => return stdout_writer.err.?, | ||
| 7 | }; | ||
| 8 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 9 | error.WriteFailed => return stdout_writer.err.?, | ||
| 10 | }; | ||
| 11 | } | ||
| 12 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 13 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 14 | } | ||
| 15 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 16 | const val: S = .{ .x = 100 }; | ||
| 17 | try w.print("{d}\n", .{val.x}); | ||
| 18 | } | ||
| 19 | const std = @import("std"); | ||
| 20 | const Writer = std.Io.Writer; | ||
test/incremental/add_remove_toplevel_fields/1/main.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const S = @This(); | ||
| 2 | x: u8, | ||
| 3 | y: u16 = 200, | ||
| 4 | pub fn main(init: std.process.Init) !void { | ||
| 5 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 6 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 7 | error.WriteFailed => return stdout_writer.err.?, | ||
| 8 | }; | ||
| 9 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 10 | error.WriteFailed => return stdout_writer.err.?, | ||
| 11 | }; | ||
| 12 | } | ||
| 13 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 14 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 15 | } | ||
| 16 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 17 | const val: S = .{ .x = 100 }; | ||
| 18 | try w.print("{d}\n", .{val.x}); | ||
| 19 | } | ||
| 20 | const std = @import("std"); | ||
| 21 | const Writer = std.Io.Writer; | ||
test/incremental/add_remove_toplevel_fields/2/main.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | const S = @This(); | ||
| 2 | pub fn main(init: std.process.Init) !void { | ||
| 3 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 4 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 5 | error.WriteFailed => return stdout_writer.err.?, | ||
| 6 | }; | ||
| 7 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 8 | error.WriteFailed => return stdout_writer.err.?, | ||
| 9 | }; | ||
| 10 | } | ||
| 11 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 12 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 13 | } | ||
| 14 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 15 | const val: S = .{ .x = 100 }; | ||
| 16 | try w.print("{d}\n", .{val.x}); | ||
| 17 | } | ||
| 18 | const std = @import("std"); | ||
| 19 | const Writer = std.Io.Writer; | ||
test/incremental/add_remove_toplevel_fields/3/main.zig created+20| ... | @@ -0,0 +1,20 @@ | ||
| 1 | const S = @This(); | ||
| 2 | pub fn main(init: std.process.Init) !void { | ||
| 3 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); | ||
| 4 | printFieldCount(&stdout_writer.interface) catch |err| switch (err) { | ||
| 5 | error.WriteFailed => return stdout_writer.err.?, | ||
| 6 | }; | ||
| 7 | printOneField(&stdout_writer.interface) catch |err| switch (err) { | ||
| 8 | error.WriteFailed => return stdout_writer.err.?, | ||
| 9 | }; | ||
| 10 | } | ||
| 11 | fn printFieldCount(w: *Writer) Writer.Error!void { | ||
| 12 | try w.print("{d} ", .{@typeInfo(S).@"struct".field_names.len}); | ||
| 13 | } | ||
| 14 | fn printOneField(w: *Writer) Writer.Error!void { | ||
| 15 | //const val: S = .{ .x = 100 }; | ||
| 16 | //try w.print("{d}\n", .{val.x}); | ||
| 17 | try w.writeAll("<no fields>\n"); | ||
| 18 | } | ||
| 19 | const std = @import("std"); | ||
| 20 | const Writer = std.Io.Writer; | ||
test/incremental/add_remove_toplevel_fields/manifest created+31| ... | @@ -0,0 +1,31 @@ | ||
| 1 | #exe { | ||
| 2 | -Mroot=main.zig | ||
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig 0/main.zig | ||
| 6 | #update | ||
| 7 | #check stdout { | ||
| 8 | 1 100 | ||
| 9 | #} | ||
| 10 | |||
| 11 | #write main.zig 1/main.zig | ||
| 12 | #update { | ||
| 13 | add a field#} | ||
| 14 | #check stdout { | ||
| 15 | 2 100 | ||
| 16 | #} | ||
| 17 | |||
| 18 | #write main.zig 2/main.zig | ||
| 19 | #update { | ||
| 20 | remove all fields#} | ||
| 21 | #check errors { | ||
| 22 | main.zig:15:24: error: no field named 'x' in struct 'main' | ||
| 23 | main.zig:1:1: note: struct declared here | ||
| 24 | #} | ||
| 25 | |||
| 26 | #write main.zig 3/main.zig | ||
| 27 | #update { | ||
| 28 | remove reference to non-existent field#} | ||
| 29 | #check stdout { | ||
| 30 | 0 <no fields> | ||
| 31 | #} | ||
test/incremental/analysis_error_and_syntax_error+44-18| ... | @@ -1,38 +1,64 @@ | ... | @@ -1,38 +1,64 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() !void { | 6 | pub fn main() !void { |
| 4 | @compileError("uh oh"); | 7 | @compileError("uh oh"); |
| 5 | } | 8 | } |
| 6 | #expect_error=main.zig:2:5: error: uh oh | 9 | #} |
| 10 | #update | ||
| 11 | #check errors { | ||
| 12 | main.zig:2:5: error: uh oh | ||
| 13 | #} | ||
| 7 | 14 | ||
| 8 | #update=add parse error | 15 | #write main.zig { |
| 9 | #file=main.zig | ||
| 10 | pub fn main() !void { | 16 | pub fn main() !void { |
| 11 | @compileError("uh oh"); | 17 | @compileError("uh oh"); |
| 12 | #expect_error=main.zig:3:1: error: expected statement, found 'EOF' | 18 | #} |
| 19 | #update { | ||
| 20 | add parse error#} | ||
| 21 | #check errors { | ||
| 22 | main.zig:3:1: error: expected statement, found 'EOF' | ||
| 23 | #} | ||
| 13 | 24 | ||
| 14 | #update=fix parse error | 25 | #write main.zig { |
| 15 | #file=main.zig | ||
| 16 | pub fn main() !void { | 26 | pub fn main() !void { |
| 17 | @compileError("uh oh"); | 27 | @compileError("uh oh"); |
| 18 | } | 28 | } |
| 19 | #expect_error=main.zig:2:5: error: uh oh | 29 | #} |
| 30 | #update { | ||
| 31 | fix parse error#} | ||
| 32 | #check errors { | ||
| 33 | main.zig:2:5: error: uh oh | ||
| 34 | #} | ||
| 20 | 35 | ||
| 21 | #update=add parse error again | 36 | #write main.zig { |
| 22 | #file=main.zig | ||
| 23 | pub fn main() !void { | 37 | pub fn main() !void { |
| 24 | @compileError("uh oh"); | 38 | @compileError("uh oh"); |
| 25 | #expect_error=main.zig:3:1: error: expected statement, found 'EOF' | 39 | #} |
| 40 | #update { | ||
| 41 | add parse error again#} | ||
| 42 | #check errors { | ||
| 43 | main.zig:3:1: error: expected statement, found 'EOF' | ||
| 44 | #} | ||
| 26 | 45 | ||
| 27 | #update=comment @compileError call | 46 | #write main.zig { |
| 28 | #file=main.zig | ||
| 29 | pub fn main() !void { | 47 | pub fn main() !void { |
| 30 | //@compileError("uh oh"); | 48 | //@compileError("uh oh"); |
| 31 | #expect_error=main.zig:3:1: error: expected statement, found 'EOF' | 49 | #} |
| 50 | #update { | ||
| 51 | comment @compileError call#} | ||
| 52 | #check errors { | ||
| 53 | main.zig:3:1: error: expected statement, found 'EOF' | ||
| 54 | #} | ||
| 32 | 55 | ||
| 33 | #update=fix parse error again | 56 | #write main.zig { |
| 34 | #file=main.zig | ||
| 35 | pub fn main() !void { | 57 | pub fn main() !void { |
| 36 | //@compileError("uh oh"); | 58 | //@compileError("uh oh"); |
| 37 | } | 59 | } |
| 38 | #expect_stdout="" | 60 | #} |
| 61 | #update { | ||
| 62 | fix parse error again#} | ||
| 63 | #check stdout { | ||
| 64 | #} |
test/incremental/bad_import+28-12| ... | @@ -1,32 +1,48 @@ | ... | @@ -1,32 +1,48 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() !void { | 6 | pub fn main() !void { |
| 4 | _ = @import("foo.zig"); | 7 | _ = @import("foo.zig"); |
| 5 | try std.Io.File.stdout().writeStreamingAll(io, "success\n"); | 8 | try std.Io.File.stdout().writeStreamingAll(io, "success\n"); |
| 6 | } | 9 | } |
| 7 | const std = @import("std"); | 10 | const std = @import("std"); |
| 8 | const io = std.Io.Threaded.global_single_threaded.io(); | 11 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 9 | #file=foo.zig | 12 | #} |
| 13 | #write foo.zig { | ||
| 10 | comptime { | 14 | comptime { |
| 11 | _ = @import("bad.zig"); | 15 | _ = @import("bad.zig"); |
| 12 | } | 16 | } |
| 13 | #expect_error=bad.zig:1:1: error: unable to load "bad.zig": FileNotFound | 17 | #} |
| 14 | #expect_error=foo.zig:2:17: note: file imported here | 18 | #update |
| 19 | #check errors { | ||
| 20 | bad.zig:1:1: error: unable to load "bad.zig": FileNotFound | ||
| 21 | foo.zig:2:17: note: file imported here | ||
| 22 | #} | ||
| 15 | 23 | ||
| 16 | #update=change bad import | 24 | #write foo.zig { |
| 17 | #file=foo.zig | ||
| 18 | comptime { | 25 | comptime { |
| 19 | _ = @import("this_is/not_real.zig"); | 26 | _ = @import("this_is/not_real.zig"); |
| 20 | } | 27 | } |
| 21 | #expect_error=this_is/not_real.zig:1:1: error: unable to load "not_real.zig": FileNotFound | 28 | #} |
| 22 | #expect_error=foo.zig:2:17: note: file imported here | 29 | #update { |
| 30 | change bad import#} | ||
| 31 | #check errors { | ||
| 32 | this_is/not_real.zig:1:1: error: unable to load "not_real.zig": FileNotFound | ||
| 33 | foo.zig:2:17: note: file imported here | ||
| 34 | #} | ||
| 23 | 35 | ||
| 24 | #update=remove import of 'foo.zig' | 36 | #write main.zig { |
| 25 | #file=main.zig | ||
| 26 | pub fn main() !void { | 37 | pub fn main() !void { |
| 27 | //_ = @import("foo.zig"); | 38 | //_ = @import("foo.zig"); |
| 28 | try std.Io.File.stdout().writeStreamingAll(io, "success\n"); | 39 | try std.Io.File.stdout().writeStreamingAll(io, "success\n"); |
| 29 | } | 40 | } |
| 30 | const std = @import("std"); | 41 | const std = @import("std"); |
| 31 | const io = std.Io.Threaded.global_single_threaded.io(); | 42 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 32 | #expect_stdout="success\n" | 43 | #} |
| 44 | #update { | ||
| 45 | remove import of 'foo.zig'#} | ||
| 46 | #check stdout { | ||
| 47 | success | ||
| 48 | #} |
test/incremental/change_bitpack_backing_int+53-30| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const Foo = packed struct(u8) { a: u4, b: i4 }; | 6 | const Foo = packed struct(u8) { a: u4, b: i4 }; |
| 4 | const Bar = packed union(u10) { a: u10, b: i10 }; | 7 | const Bar = packed union(u10) { a: u10, b: i10 }; |
| 5 | pub fn main() void {} | 8 | pub fn main() void {} |
| ... | @@ -11,13 +14,18 @@ comptime { | ... | @@ -11,13 +14,18 @@ comptime { |
| 11 | } | 14 | } |
| 12 | const std = @import("std"); | 15 | const std = @import("std"); |
| 13 | const io = std.Io.Threaded.global_single_threaded.io(); | 16 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 14 | #expect_error=main.zig:5:5: error: found compile log statement | 17 | #} |
| 15 | #expect_error=main.zig:8:5: note: also here | 18 | #update |
| 16 | #expect_compile_log=@as(type, u8) | 19 | #check errors { |
| 17 | #expect_compile_log=@as(comptime_int, 10) | 20 | main.zig:5:5: error: found compile log statement |
| 21 | main.zig:8:5: note: also here | ||
| 22 | |||
| 23 | Compile Log Output: | ||
| 24 | @as(type, u8) | ||
| 25 | @as(comptime_int, 10) | ||
| 26 | #} | ||
| 18 | 27 | ||
| 19 | #update=make backing types signed | 28 | #write main.zig { |
| 20 | #file=main.zig | ||
| 21 | const Foo = packed struct(i8) { a: u4, b: i4 }; | 29 | const Foo = packed struct(i8) { a: u4, b: i4 }; |
| 22 | const Bar = packed union(i10) { a: u10, b: i10 }; | 30 | const Bar = packed union(i10) { a: u10, b: i10 }; |
| 23 | pub fn main() void {} | 31 | pub fn main() void {} |
| ... | @@ -29,13 +37,19 @@ comptime { | ... | @@ -29,13 +37,19 @@ comptime { |
| 29 | } | 37 | } |
| 30 | const std = @import("std"); | 38 | const std = @import("std"); |
| 31 | const io = std.Io.Threaded.global_single_threaded.io(); | 39 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 32 | #expect_error=main.zig:5:5: error: found compile log statement | 40 | #} |
| 33 | #expect_error=main.zig:8:5: note: also here | 41 | #update { |
| 34 | #expect_compile_log=@as(type, i8) | 42 | make backing types signed#} |
| 35 | #expect_compile_log=@as(comptime_int, 10) | 43 | #check errors { |
| 44 | main.zig:5:5: error: found compile log statement | ||
| 45 | main.zig:8:5: note: also here | ||
| 46 | |||
| 47 | Compile Log Output: | ||
| 48 | @as(type, i8) | ||
| 49 | @as(comptime_int, 10) | ||
| 50 | #} | ||
| 36 | 51 | ||
| 37 | #update=make backing types too small | 52 | #write main.zig { |
| 38 | #file=main.zig | ||
| 39 | const Foo = packed struct(i5) { a: u4, b: i4 }; | 53 | const Foo = packed struct(i5) { a: u4, b: i4 }; |
| 40 | const Bar = packed union(i8) { a: u10, b: i10 }; | 54 | const Bar = packed union(i8) { a: u10, b: i10 }; |
| 41 | pub fn main() void {} | 55 | pub fn main() void {} |
| ... | @@ -47,16 +61,20 @@ comptime { | ... | @@ -47,16 +61,20 @@ comptime { |
| 47 | } | 61 | } |
| 48 | const std = @import("std"); | 62 | const std = @import("std"); |
| 49 | const io = std.Io.Threaded.global_single_threaded.io(); | 63 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 50 | #expect_error=main.zig:1:20: error: backing integer bit width does not match total bit width of fields | 64 | #} |
| 51 | #expect_error=main.zig:1:27: note: backing integer 'i5' has bit width '5' | 65 | #update { |
| 52 | #expect_error=main.zig:1:20: note: struct fields have total bit width '8' | 66 | make backing types too small#} |
| 53 | #expect_error=main.zig:2:35: error: field bit width does not match backing integer | 67 | #check errors { |
| 54 | #expect_error=main.zig:2:35: note: field type 'u10' has bit width '10' | 68 | main.zig:1:20: error: backing integer bit width does not match total bit width of fields |
| 55 | #expect_error=main.zig:2:26: note: backing integer 'i8' has bit width '8' | 69 | main.zig:1:27: note: backing integer 'i5' has bit width '5' |
| 56 | #expect_error=main.zig:2:35: note: all fields in a packed union must have the same bit width | 70 | main.zig:1:20: note: struct fields have total bit width '8' |
| 71 | main.zig:2:35: error: field bit width does not match backing integer | ||
| 72 | main.zig:2:35: note: field type 'u10' has bit width '10' | ||
| 73 | main.zig:2:26: note: backing integer 'i8' has bit width '8' | ||
| 74 | main.zig:2:35: note: all fields in a packed union must have the same bit width | ||
| 75 | #} | ||
| 57 | 76 | ||
| 58 | #update=make backing types too big | 77 | #write main.zig { |
| 59 | #file=main.zig | ||
| 60 | const Foo = packed struct(u10) { a: u4, b: i4 }; | 78 | const Foo = packed struct(u10) { a: u4, b: i4 }; |
| 61 | const Bar = packed union(u32) { a: u10, b: i10 }; | 79 | const Bar = packed union(u32) { a: u10, b: i10 }; |
| 62 | pub fn main() void {} | 80 | pub fn main() void {} |
| ... | @@ -68,10 +86,15 @@ comptime { | ... | @@ -68,10 +86,15 @@ comptime { |
| 68 | } | 86 | } |
| 69 | const std = @import("std"); | 87 | const std = @import("std"); |
| 70 | const io = std.Io.Threaded.global_single_threaded.io(); | 88 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 71 | #expect_error=main.zig:1:20: error: backing integer bit width does not match total bit width of fields | 89 | #} |
| 72 | #expect_error=main.zig:1:27: note: backing integer 'u10' has bit width '10' | 90 | #update { |
| 73 | #expect_error=main.zig:1:20: note: struct fields have total bit width '8' | 91 | make backing types too big#} |
| 74 | #expect_error=main.zig:2:36: error: field bit width does not match backing integer | 92 | #check errors { |
| 75 | #expect_error=main.zig:2:36: note: field type 'u10' has bit width '10' | 93 | main.zig:1:20: error: backing integer bit width does not match total bit width of fields |
| 76 | #expect_error=main.zig:2:26: note: backing integer 'u32' has bit width '32' | 94 | main.zig:1:27: note: backing integer 'u10' has bit width '10' |
| 77 | #expect_error=main.zig:2:36: note: all fields in a packed union must have the same bit width | 95 | main.zig:1:20: note: struct fields have total bit width '8' |
| 96 | main.zig:2:36: error: field bit width does not match backing integer | ||
| 97 | main.zig:2:36: note: field type 'u10' has bit width '10' | ||
| 98 | main.zig:2:26: note: backing integer 'u32' has bit width '32' | ||
| 99 | main.zig:2:36: note: all fields in a packed union must have the same bit width | ||
| 100 | #} |
test/incremental/change_container_kind+94-41| ... | @@ -1,61 +1,114 @@ | ... | @@ -1,61 +1,114 @@ |
| 1 | #update=initial version, T is struct | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const T = @import("foo.zig").T; | 6 | const T = @import("foo.zig").T; |
| 4 | pub fn main() void { | 7 | pub fn main() void { |
| 5 | @compileLog(T); | 8 | @compileLog(T); |
| 6 | @compileLog(@tagName(@typeInfo(T))); | 9 | @compileLog(@tagName(@typeInfo(T))); |
| 7 | } | 10 | } |
| 8 | #file=foo.zig | 11 | #} |
| 12 | #write foo.zig { | ||
| 9 | pub const T = struct { x: u8 }; | 13 | pub const T = struct { x: u8 }; |
| 10 | #expect_error=main.zig:3:5: error: found compile log statement | 14 | #} |
| 11 | #expect_compile_log=@as(type, foo.T) | 15 | #update { |
| 12 | #expect_compile_log=@as(*const [6:0]u8, "struct") | 16 | T is struct#} |
| 17 | #check errors { | ||
| 18 | main.zig:3:5: error: found compile log statement | ||
| 19 | |||
| 20 | Compile Log Output: | ||
| 21 | @as(type, foo.T) | ||
| 22 | @as(*const [6:0]u8, "struct") | ||
| 23 | #} | ||
| 13 | 24 | ||
| 14 | #update=T is union | 25 | #write foo.zig { |
| 15 | #file=foo.zig | ||
| 16 | pub const T = union {}; | 26 | pub const T = union {}; |
| 17 | #expect_error=main.zig:3:5: error: found compile log statement | 27 | #} |
| 18 | #expect_compile_log=@as(type, foo.T) | 28 | #update { |
| 19 | #expect_compile_log=@as(*const [5:0]u8, "union") | 29 | T is union#} |
| 30 | #check errors { | ||
| 31 | main.zig:3:5: error: found compile log statement | ||
| 32 | |||
| 33 | Compile Log Output: | ||
| 34 | @as(type, foo.T) | ||
| 35 | @as(*const [5:0]u8, "union") | ||
| 36 | #} | ||
| 20 | 37 | ||
| 21 | #update=T is enum | 38 | #write foo.zig { |
| 22 | #file=foo.zig | ||
| 23 | pub const T = enum {}; | 39 | pub const T = enum {}; |
| 24 | #expect_error=main.zig:3:5: error: found compile log statement | 40 | #} |
| 25 | #expect_compile_log=@as(type, foo.T) | 41 | #update { |
| 26 | #expect_compile_log=@as(*const [4:0]u8, "enum") | 42 | T is enum#} |
| 43 | #check errors { | ||
| 44 | main.zig:3:5: error: found compile log statement | ||
| 27 | 45 | ||
| 28 | #update=T is opaque | 46 | Compile Log Output: |
| 29 | #file=foo.zig | 47 | @as(type, foo.T) |
| 48 | @as(*const [4:0]u8, "enum") | ||
| 49 | #} | ||
| 50 | |||
| 51 | #write foo.zig { | ||
| 30 | pub const T = opaque {}; | 52 | pub const T = opaque {}; |
| 31 | #expect_error=main.zig:3:5: error: found compile log statement | 53 | #} |
| 32 | #expect_compile_log=@as(type, foo.T) | 54 | #update { |
| 33 | #expect_compile_log=@as(*const [6:0]u8, "opaque") | 55 | T is opaque#} |
| 56 | #check errors { | ||
| 57 | main.zig:3:5: error: found compile log statement | ||
| 58 | |||
| 59 | Compile Log Output: | ||
| 60 | @as(type, foo.T) | ||
| 61 | @as(*const [6:0]u8, "opaque") | ||
| 62 | #} | ||
| 34 | 63 | ||
| 35 | #update=T is packed struct | 64 | #write foo.zig { |
| 36 | #file=foo.zig | ||
| 37 | pub const T = packed struct {}; | 65 | pub const T = packed struct {}; |
| 38 | #expect_error=main.zig:3:5: error: found compile log statement | 66 | #} |
| 39 | #expect_compile_log=@as(type, foo.T) | 67 | #update { |
| 40 | #expect_compile_log=@as(*const [6:0]u8, "struct") | 68 | T is packed struct#} |
| 69 | #check errors { | ||
| 70 | main.zig:3:5: error: found compile log statement | ||
| 41 | 71 | ||
| 42 | #update=T is packed union | 72 | Compile Log Output: |
| 43 | #file=foo.zig | 73 | @as(type, foo.T) |
| 74 | @as(*const [6:0]u8, "struct") | ||
| 75 | #} | ||
| 76 | |||
| 77 | #write foo.zig { | ||
| 44 | pub const T = packed union { x: void }; // packed unions need at least 1 field | 78 | pub const T = packed union { x: void }; // packed unions need at least 1 field |
| 45 | #expect_error=main.zig:3:5: error: found compile log statement | 79 | #} |
| 46 | #expect_compile_log=@as(type, foo.T) | 80 | #update { |
| 47 | #expect_compile_log=@as(*const [5:0]u8, "union") | 81 | T is packed union#} |
| 82 | #check errors { | ||
| 83 | main.zig:3:5: error: found compile log statement | ||
| 84 | |||
| 85 | Compile Log Output: | ||
| 86 | @as(type, foo.T) | ||
| 87 | @as(*const [5:0]u8, "union") | ||
| 88 | #} | ||
| 48 | 89 | ||
| 49 | #update=T is extern struct | 90 | #write foo.zig { |
| 50 | #file=foo.zig | ||
| 51 | pub const T = extern struct {}; | 91 | pub const T = extern struct {}; |
| 52 | #expect_error=main.zig:3:5: error: found compile log statement | 92 | #} |
| 53 | #expect_compile_log=@as(type, foo.T) | 93 | #update { |
| 54 | #expect_compile_log=@as(*const [6:0]u8, "struct") | 94 | T is extern struct#} |
| 95 | #check errors { | ||
| 96 | main.zig:3:5: error: found compile log statement | ||
| 55 | 97 | ||
| 56 | #update=T is extern union | 98 | Compile Log Output: |
| 57 | #file=foo.zig | 99 | @as(type, foo.T) |
| 100 | @as(*const [6:0]u8, "struct") | ||
| 101 | #} | ||
| 102 | |||
| 103 | #write foo.zig { | ||
| 58 | pub const T = extern union { x: void }; // extern unions need at least 1 field | 104 | pub const T = extern union { x: void }; // extern unions need at least 1 field |
| 59 | #expect_error=main.zig:3:5: error: found compile log statement | 105 | #} |
| 60 | #expect_compile_log=@as(type, foo.T) | 106 | #update { |
| 61 | #expect_compile_log=@as(*const [5:0]u8, "union") | 107 | T is extern union#} |
| 108 | #check errors { | ||
| 109 | main.zig:3:5: error: found compile log statement | ||
| 110 | |||
| 111 | Compile Log Output: | ||
| 112 | @as(type, foo.T) | ||
| 113 | @as(*const [5:0]u8, "union") | ||
| 114 | #} |
test/incremental/change_embed_file+46-19| ... | @@ -1,45 +1,72 @@ | ... | @@ -1,45 +1,72 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | const string = @embedFile("string.txt"); | 7 | const string = @embedFile("string.txt"); |
| 5 | pub fn main() !void { | 8 | pub fn main() !void { |
| 6 | try std.Io.File.stdout().writeStreamingAll(io, string); | 9 | try std.Io.File.stdout().writeStreamingAll(io, string); |
| 7 | } | 10 | } |
| 8 | const io = std.Io.Threaded.global_single_threaded.io(); | 11 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 9 | #file=string.txt | 12 | #} |
| 13 | #write string.txt { | ||
| 14 | Hello, World! | ||
| 15 | #} | ||
| 16 | #update | ||
| 17 | #check stdout { | ||
| 10 | Hello, World! | 18 | Hello, World! |
| 11 | #expect_stdout="Hello, World!\n" | 19 | #} |
| 12 | 20 | ||
| 13 | #update=change file contents | 21 | #write string.txt { |
| 14 | #file=string.txt | ||
| 15 | Hello again, World! | 22 | Hello again, World! |
| 16 | #expect_stdout="Hello again, World!\n" | 23 | #} |
| 24 | #update { | ||
| 25 | change file contents#} | ||
| 26 | #check stdout { | ||
| 27 | Hello again, World! | ||
| 28 | #} | ||
| 17 | 29 | ||
| 18 | #update=delete file | 30 | #delete string.txt |
| 19 | #rm_file=string.txt | 31 | #update { |
| 20 | #expect_error=main.zig:2:27: error: failed opening "string.txt": FileNotFound | 32 | delete file#} |
| 33 | #check errors { | ||
| 34 | main.zig:2:27: error: failed opening "string.txt": FileNotFound | ||
| 35 | #} | ||
| 21 | 36 | ||
| 22 | #update=remove reference to file | 37 | #write main.zig { |
| 23 | #file=main.zig | ||
| 24 | const std = @import("std"); | 38 | const std = @import("std"); |
| 25 | const string = @embedFile("string.txt"); | 39 | const string = @embedFile("string.txt"); |
| 26 | pub fn main() !void { | 40 | pub fn main() !void { |
| 27 | try std.Io.File.stdout().writeStreamingAll(io, "a hardcoded string\n"); | 41 | try std.Io.File.stdout().writeStreamingAll(io, "a hardcoded string\n"); |
| 28 | } | 42 | } |
| 29 | const io = std.Io.Threaded.global_single_threaded.io(); | 43 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 30 | #expect_stdout="a hardcoded string\n" | 44 | #} |
| 45 | #update { | ||
| 46 | remove reference to file#} | ||
| 47 | #check stdout { | ||
| 48 | a hardcoded string | ||
| 49 | #} | ||
| 31 | 50 | ||
| 32 | #update=re-introduce reference to file | 51 | #write main.zig { |
| 33 | #file=main.zig | ||
| 34 | const std = @import("std"); | 52 | const std = @import("std"); |
| 35 | const string = @embedFile("string.txt"); | 53 | const string = @embedFile("string.txt"); |
| 36 | pub fn main() !void { | 54 | pub fn main() !void { |
| 37 | try std.Io.File.stdout().writeStreamingAll(io, string); | 55 | try std.Io.File.stdout().writeStreamingAll(io, string); |
| 38 | } | 56 | } |
| 39 | const io = std.Io.Threaded.global_single_threaded.io(); | 57 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 40 | #expect_error=main.zig:2:27: error: failed opening "string.txt": FileNotFound | 58 | #} |
| 59 | #update { | ||
| 60 | re-introduce reference to file#} | ||
| 61 | #check errors { | ||
| 62 | main.zig:2:27: error: failed opening "string.txt": FileNotFound | ||
| 63 | #} | ||
| 41 | 64 | ||
| 42 | #update=recreate file | 65 | #write string.txt { |
| 43 | #file=string.txt | 66 | We're back, World! |
| 67 | #} | ||
| 68 | #update { | ||
| 69 | recreate file#} | ||
| 70 | #check stdout { | ||
| 44 | We're back, World! | 71 | We're back, World! |
| 45 | #expect_stdout="We're back, World!\n" | 72 | #} |
test/incremental/change_enum_tag_type deleted-93| ... | @@ -1,93 +0,0 @@ | ||
| 1 | #update=initial version | ||
| 2 | #file=main.zig | ||
| 3 | const Tag = u2; | ||
| 4 | const Foo = enum(Tag) { | ||
| 5 | a, | ||
| 6 | b, | ||
| 7 | c, | ||
| 8 | d, | ||
| 9 | }; | ||
| 10 | pub fn main() !void { | ||
| 11 | var val: Foo = undefined; | ||
| 12 | val = .a; | ||
| 13 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 14 | try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); | ||
| 15 | } | ||
| 16 | const std = @import("std"); | ||
| 17 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 18 | #expect_stdout="a\n" | ||
| 19 | #update=too many enum fields | ||
| 20 | #file=main.zig | ||
| 21 | const Tag = u2; | ||
| 22 | const Foo = enum(Tag) { | ||
| 23 | a, | ||
| 24 | b, | ||
| 25 | c, | ||
| 26 | d, | ||
| 27 | e, | ||
| 28 | }; | ||
| 29 | pub fn main() !void { | ||
| 30 | var val: Foo = undefined; | ||
| 31 | val = .a; | ||
| 32 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 33 | try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); | ||
| 34 | } | ||
| 35 | comptime { | ||
| 36 | // These can't be true at the same time; analysis should stop as soon as it sees `Foo` | ||
| 37 | std.debug.assert(@intFromEnum(Foo.e) == 4); | ||
| 38 | std.debug.assert(@TypeOf(@intFromEnum(Foo.e)) == Tag); | ||
| 39 | } | ||
| 40 | const std = @import("std"); | ||
| 41 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 42 | #expect_error=main.zig:7:5: error: enum tag value '4' too large for type 'u2' | ||
| 43 | #update=increase tag size | ||
| 44 | #file=main.zig | ||
| 45 | const Tag = u3; | ||
| 46 | const Foo = enum(Tag) { | ||
| 47 | a, | ||
| 48 | b, | ||
| 49 | c, | ||
| 50 | d, | ||
| 51 | e, | ||
| 52 | }; | ||
| 53 | pub fn main() !void { | ||
| 54 | var val: Foo = undefined; | ||
| 55 | val = .a; | ||
| 56 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 57 | try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); | ||
| 58 | } | ||
| 59 | const std = @import("std"); | ||
| 60 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 61 | #expect_stdout="a\n" | ||
| 62 | #update=specify tag directly | ||
| 63 | #file=main.zig | ||
| 64 | const Foo = enum(u3) { | ||
| 65 | a, | ||
| 66 | b, | ||
| 67 | c, | ||
| 68 | d, | ||
| 69 | e, | ||
| 70 | }; | ||
| 71 | pub fn main() !void { | ||
| 72 | @compileLog(@typeInfo(Foo).@"enum".tag_type); | ||
| 73 | } | ||
| 74 | const std = @import("std"); | ||
| 75 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 76 | #expect_error=main.zig:9:5: error: found compile log statement | ||
| 77 | #expect_compile_log=@as(type, u3) | ||
| 78 | #update=change directly-specified tag type | ||
| 79 | #file=main.zig | ||
| 80 | const Foo = enum(u8) { | ||
| 81 | a, | ||
| 82 | b, | ||
| 83 | c, | ||
| 84 | d, | ||
| 85 | e, | ||
| 86 | }; | ||
| 87 | pub fn main() !void { | ||
| 88 | @compileLog(@typeInfo(Foo).@"enum".tag_type); | ||
| 89 | } | ||
| 90 | const std = @import("std"); | ||
| 91 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 92 | #expect_error=main.zig:9:5: error: found compile log statement | ||
| 93 | #expect_compile_log=@as(type, u8) | ||
test/incremental/change_enum_tag_type/0/main.zig created+15| ... | @@ -0,0 +1,15 @@ | ||
| 1 | const Tag = u2; | ||
| 2 | const Foo = enum(Tag) { | ||
| 3 | a, | ||
| 4 | b, | ||
| 5 | c, | ||
| 6 | d, | ||
| 7 | }; | ||
| 8 | pub fn main() !void { | ||
| 9 | var val: Foo = undefined; | ||
| 10 | val = .a; | ||
| 11 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 12 | try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); | ||
| 13 | } | ||
| 14 | const std = @import("std"); | ||
| 15 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_enum_tag_type/1/main.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const Tag = u2; | ||
| 2 | const Foo = enum(Tag) { | ||
| 3 | a, | ||
| 4 | b, | ||
| 5 | c, | ||
| 6 | d, | ||
| 7 | e, | ||
| 8 | }; | ||
| 9 | pub fn main() !void { | ||
| 10 | var val: Foo = undefined; | ||
| 11 | val = .a; | ||
| 12 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 13 | try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); | ||
| 14 | } | ||
| 15 | comptime { | ||
| 16 | // These can't be true at the same time; analysis should stop as soon as it sees `Foo` | ||
| 17 | std.debug.assert(@backingInt(Foo.e) == 4); | ||
| 18 | std.debug.assert(@TypeOf(@backingInt(Foo.e)) == Tag); | ||
| 19 | } | ||
| 20 | const std = @import("std"); | ||
| 21 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_enum_tag_type/2/main.zig created+16| ... | @@ -0,0 +1,16 @@ | ||
| 1 | const Tag = u3; | ||
| 2 | const Foo = enum(Tag) { | ||
| 3 | a, | ||
| 4 | b, | ||
| 5 | c, | ||
| 6 | d, | ||
| 7 | e, | ||
| 8 | }; | ||
| 9 | pub fn main() !void { | ||
| 10 | var val: Foo = undefined; | ||
| 11 | val = .a; | ||
| 12 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 13 | try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); | ||
| 14 | } | ||
| 15 | const std = @import("std"); | ||
| 16 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_enum_tag_type/3/main.zig created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | const Foo = enum(u3) { | ||
| 2 | a, | ||
| 3 | b, | ||
| 4 | c, | ||
| 5 | d, | ||
| 6 | e, | ||
| 7 | }; | ||
| 8 | pub fn main() !void { | ||
| 9 | @compileLog(@typeInfo(Foo).@"enum".tag_type); | ||
| 10 | } | ||
| 11 | const std = @import("std"); | ||
| 12 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_enum_tag_type/4/main.zig created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | const Foo = enum(u8) { | ||
| 2 | a, | ||
| 3 | b, | ||
| 4 | c, | ||
| 5 | d, | ||
| 6 | e, | ||
| 7 | }; | ||
| 8 | pub fn main() !void { | ||
| 9 | @compileLog(@typeInfo(Foo).@"enum".tag_type); | ||
| 10 | } | ||
| 11 | const std = @import("std"); | ||
| 12 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_enum_tag_type/manifest created+43| ... | @@ -0,0 +1,43 @@ | ||
| 1 | #exe { | ||
| 2 | -Mroot=main.zig | ||
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig 0/main.zig | ||
| 6 | #update | ||
| 7 | #check stdout { | ||
| 8 | a | ||
| 9 | #} | ||
| 10 | |||
| 11 | #write main.zig 1/main.zig | ||
| 12 | #update { | ||
| 13 | too many enum fields#} | ||
| 14 | #check errors { | ||
| 15 | main.zig:7:5: error: enum tag value '4' too large for type 'u2' | ||
| 16 | #} | ||
| 17 | |||
| 18 | #write main.zig 2/main.zig | ||
| 19 | #update { | ||
| 20 | increase tag size#} | ||
| 21 | #check stdout { | ||
| 22 | a | ||
| 23 | #} | ||
| 24 | |||
| 25 | #write main.zig 3/main.zig | ||
| 26 | #update { | ||
| 27 | specify tag directly#} | ||
| 28 | #check errors { | ||
| 29 | main.zig:9:5: error: found compile log statement | ||
| 30 | |||
| 31 | Compile Log Output: | ||
| 32 | @as(type, u3) | ||
| 33 | #} | ||
| 34 | |||
| 35 | #write main.zig 4/main.zig | ||
| 36 | #update { | ||
| 37 | change directly-specified tag type#} | ||
| 38 | #check errors { | ||
| 39 | main.zig:9:5: error: found compile log statement | ||
| 40 | |||
| 41 | Compile Log Output: | ||
| 42 | @as(type, u8) | ||
| 43 | #} | ||
test/incremental/change_exports deleted-169| ... | @@ -1,169 +0,0 @@ | ||
| 1 | #update=initial version | ||
| 2 | #file=main.zig | ||
| 3 | export fn foo() void {} | ||
| 4 | const bar: u32 = 123; | ||
| 5 | const other: u32 = 456; | ||
| 6 | comptime { | ||
| 7 | @export(&bar, .{ .name = "bar" }); | ||
| 8 | } | ||
| 9 | pub fn main() !void { | ||
| 10 | const S = struct { | ||
| 11 | extern fn foo() void; | ||
| 12 | extern const bar: u32; | ||
| 13 | }; | ||
| 14 | S.foo(); | ||
| 15 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 16 | try stdout_writer.interface.print("{}\n", .{S.bar}); | ||
| 17 | } | ||
| 18 | const std = @import("std"); | ||
| 19 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 20 | #expect_stdout="123\n" | ||
| 21 | |||
| 22 | #update=add conflict | ||
| 23 | #file=main.zig | ||
| 24 | export fn foo() void {} | ||
| 25 | const bar: u32 = 123; | ||
| 26 | const other: u32 = 456; | ||
| 27 | comptime { | ||
| 28 | @export(&bar, .{ .name = "bar" }); | ||
| 29 | @export(&other, .{ .name = "foo" }); | ||
| 30 | } | ||
| 31 | pub fn main() !void { | ||
| 32 | const S = struct { | ||
| 33 | extern fn foo() void; | ||
| 34 | extern const bar: u32; | ||
| 35 | extern const other: u32; | ||
| 36 | }; | ||
| 37 | S.foo(); | ||
| 38 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 39 | try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); | ||
| 40 | } | ||
| 41 | const std = @import("std"); | ||
| 42 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 43 | #expect_error=main.zig:6:5: error: exported symbol collision: foo | ||
| 44 | #expect_error=main.zig:1:1: note: other symbol here | ||
| 45 | |||
| 46 | #update=resolve conflict | ||
| 47 | #file=main.zig | ||
| 48 | export fn foo() void {} | ||
| 49 | const bar: u32 = 123; | ||
| 50 | const other: u32 = 456; | ||
| 51 | comptime { | ||
| 52 | @export(&bar, .{ .name = "bar" }); | ||
| 53 | @export(&other, .{ .name = "other" }); | ||
| 54 | } | ||
| 55 | pub fn main() !void { | ||
| 56 | const S = struct { | ||
| 57 | extern fn foo() void; | ||
| 58 | extern const bar: u32; | ||
| 59 | extern const other: u32; | ||
| 60 | }; | ||
| 61 | S.foo(); | ||
| 62 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 63 | try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); | ||
| 64 | } | ||
| 65 | const std = @import("std"); | ||
| 66 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 67 | #expect_stdout="123 456\n" | ||
| 68 | |||
| 69 | #update=put exports in decl | ||
| 70 | #file=main.zig | ||
| 71 | export fn foo() void {} | ||
| 72 | const bar: u32 = 123; | ||
| 73 | const other: u32 = 456; | ||
| 74 | const does_exports = { | ||
| 75 | @export(&bar, .{ .name = "bar" }); | ||
| 76 | @export(&other, .{ .name = "other" }); | ||
| 77 | }; | ||
| 78 | comptime { | ||
| 79 | _ = does_exports; | ||
| 80 | } | ||
| 81 | pub fn main() !void { | ||
| 82 | const S = struct { | ||
| 83 | extern fn foo() void; | ||
| 84 | extern const bar: u32; | ||
| 85 | extern const other: u32; | ||
| 86 | }; | ||
| 87 | S.foo(); | ||
| 88 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 89 | try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); | ||
| 90 | } | ||
| 91 | const std = @import("std"); | ||
| 92 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 93 | #expect_stdout="123 456\n" | ||
| 94 | |||
| 95 | #update=remove reference to exporting decl | ||
| 96 | #file=main.zig | ||
| 97 | export fn foo() void {} | ||
| 98 | const bar: u32 = 123; | ||
| 99 | const other: u32 = 456; | ||
| 100 | const does_exports = { | ||
| 101 | @export(&bar, .{ .name = "bar" }); | ||
| 102 | @export(&other, .{ .name = "other" }); | ||
| 103 | }; | ||
| 104 | comptime { | ||
| 105 | //_ = does_exports; | ||
| 106 | } | ||
| 107 | pub fn main() !void { | ||
| 108 | const S = struct { | ||
| 109 | extern fn foo() void; | ||
| 110 | }; | ||
| 111 | S.foo(); | ||
| 112 | } | ||
| 113 | const std = @import("std"); | ||
| 114 | #expect_stdout="" | ||
| 115 | |||
| 116 | #update=mark consts as export | ||
| 117 | #file=main.zig | ||
| 118 | export fn foo() void {} | ||
| 119 | export const bar: u32 = 123; | ||
| 120 | export const other: u32 = 456; | ||
| 121 | const does_exports = { | ||
| 122 | @export(&bar, .{ .name = "bar" }); | ||
| 123 | @export(&other, .{ .name = "other" }); | ||
| 124 | }; | ||
| 125 | comptime { | ||
| 126 | //_ = does_exports; | ||
| 127 | } | ||
| 128 | pub fn main() !void { | ||
| 129 | const S = struct { | ||
| 130 | extern fn foo() void; | ||
| 131 | extern const bar: u32; | ||
| 132 | extern const other: u32; | ||
| 133 | }; | ||
| 134 | S.foo(); | ||
| 135 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 136 | try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); | ||
| 137 | } | ||
| 138 | const std = @import("std"); | ||
| 139 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 140 | #expect_stdout="123 456\n" | ||
| 141 | |||
| 142 | #update=reintroduce reference to exporting decl, introducing conflict | ||
| 143 | #file=main.zig | ||
| 144 | export fn foo() void {} | ||
| 145 | export const bar: u32 = 123; | ||
| 146 | export const other: u32 = 456; | ||
| 147 | const does_exports = { | ||
| 148 | @export(&bar, .{ .name = "bar" }); | ||
| 149 | @export(&other, .{ .name = "other" }); | ||
| 150 | }; | ||
| 151 | comptime { | ||
| 152 | _ = does_exports; | ||
| 153 | } | ||
| 154 | pub fn main() !void { | ||
| 155 | const S = struct { | ||
| 156 | extern fn foo() void; | ||
| 157 | extern const bar: u32; | ||
| 158 | extern const other: u32; | ||
| 159 | }; | ||
| 160 | S.foo(); | ||
| 161 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 162 | try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); | ||
| 163 | } | ||
| 164 | const std = @import("std"); | ||
| 165 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 166 | #expect_error=main.zig:5:5: error: exported symbol collision: bar | ||
| 167 | #expect_error=main.zig:2:1: note: other symbol here | ||
| 168 | #expect_error=main.zig:6:5: error: exported symbol collision: other | ||
| 169 | #expect_error=main.zig:3:1: note: other symbol here | ||
test/incremental/change_exports/0/main.zig created+17| ... | @@ -0,0 +1,17 @@ | ||
| 1 | export fn foo() void {} | ||
| 2 | const bar: u32 = 123; | ||
| 3 | const other: u32 = 456; | ||
| 4 | comptime { | ||
| 5 | @export(&bar, .{ .name = "bar" }); | ||
| 6 | } | ||
| 7 | pub fn main() !void { | ||
| 8 | const S = struct { | ||
| 9 | extern fn foo() void; | ||
| 10 | extern const bar: u32; | ||
| 11 | }; | ||
| 12 | S.foo(); | ||
| 13 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 14 | try stdout_writer.interface.print("{}\n", .{S.bar}); | ||
| 15 | } | ||
| 16 | const std = @import("std"); | ||
| 17 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_exports/1/main.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | export fn foo() void {} | ||
| 2 | const bar: u32 = 123; | ||
| 3 | const other: u32 = 456; | ||
| 4 | comptime { | ||
| 5 | @export(&bar, .{ .name = "bar" }); | ||
| 6 | @export(&other, .{ .name = "foo" }); | ||
| 7 | } | ||
| 8 | pub fn main() !void { | ||
| 9 | const S = struct { | ||
| 10 | extern fn foo() void; | ||
| 11 | extern const bar: u32; | ||
| 12 | extern const other: u32; | ||
| 13 | }; | ||
| 14 | S.foo(); | ||
| 15 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 16 | try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); | ||
| 17 | } | ||
| 18 | const std = @import("std"); | ||
| 19 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_exports/2/main.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | export fn foo() void {} | ||
| 2 | const bar: u32 = 123; | ||
| 3 | const other: u32 = 456; | ||
| 4 | comptime { | ||
| 5 | @export(&bar, .{ .name = "bar" }); | ||
| 6 | @export(&other, .{ .name = "other" }); | ||
| 7 | } | ||
| 8 | pub fn main() !void { | ||
| 9 | const S = struct { | ||
| 10 | extern fn foo() void; | ||
| 11 | extern const bar: u32; | ||
| 12 | extern const other: u32; | ||
| 13 | }; | ||
| 14 | S.foo(); | ||
| 15 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 16 | try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); | ||
| 17 | } | ||
| 18 | const std = @import("std"); | ||
| 19 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_exports/3/main.zig created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | export fn foo() void {} | ||
| 2 | const bar: u32 = 123; | ||
| 3 | const other: u32 = 456; | ||
| 4 | const does_exports = { | ||
| 5 | @export(&bar, .{ .name = "bar" }); | ||
| 6 | @export(&other, .{ .name = "other" }); | ||
| 7 | }; | ||
| 8 | comptime { | ||
| 9 | _ = does_exports; | ||
| 10 | } | ||
| 11 | pub fn main() !void { | ||
| 12 | const S = struct { | ||
| 13 | extern fn foo() void; | ||
| 14 | extern const bar: u32; | ||
| 15 | extern const other: u32; | ||
| 16 | }; | ||
| 17 | S.foo(); | ||
| 18 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 19 | try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); | ||
| 20 | } | ||
| 21 | const std = @import("std"); | ||
| 22 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_exports/4/main.zig created+17| ... | @@ -0,0 +1,17 @@ | ||
| 1 | export fn foo() void {} | ||
| 2 | const bar: u32 = 123; | ||
| 3 | const other: u32 = 456; | ||
| 4 | const does_exports = { | ||
| 5 | @export(&bar, .{ .name = "bar" }); | ||
| 6 | @export(&other, .{ .name = "other" }); | ||
| 7 | }; | ||
| 8 | comptime { | ||
| 9 | //_ = does_exports; | ||
| 10 | } | ||
| 11 | pub fn main() !void { | ||
| 12 | const S = struct { | ||
| 13 | extern fn foo() void; | ||
| 14 | }; | ||
| 15 | S.foo(); | ||
| 16 | } | ||
| 17 | const std = @import("std"); | ||
test/incremental/change_exports/5/main.zig created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | export fn foo() void {} | ||
| 2 | export const bar: u32 = 123; | ||
| 3 | export const other: u32 = 456; | ||
| 4 | const does_exports = { | ||
| 5 | @export(&bar, .{ .name = "bar" }); | ||
| 6 | @export(&other, .{ .name = "other" }); | ||
| 7 | }; | ||
| 8 | comptime { | ||
| 9 | //_ = does_exports; | ||
| 10 | } | ||
| 11 | pub fn main() !void { | ||
| 12 | const S = struct { | ||
| 13 | extern fn foo() void; | ||
| 14 | extern const bar: u32; | ||
| 15 | extern const other: u32; | ||
| 16 | }; | ||
| 17 | S.foo(); | ||
| 18 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 19 | try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); | ||
| 20 | } | ||
| 21 | const std = @import("std"); | ||
| 22 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_exports/6/main.zig created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | export fn foo() void {} | ||
| 2 | export const bar: u32 = 123; | ||
| 3 | export const other: u32 = 456; | ||
| 4 | const does_exports = { | ||
| 5 | @export(&bar, .{ .name = "bar" }); | ||
| 6 | @export(&other, .{ .name = "other" }); | ||
| 7 | }; | ||
| 8 | comptime { | ||
| 9 | _ = does_exports; | ||
| 10 | } | ||
| 11 | pub fn main() !void { | ||
| 12 | const S = struct { | ||
| 13 | extern fn foo() void; | ||
| 14 | extern const bar: u32; | ||
| 15 | extern const other: u32; | ||
| 16 | }; | ||
| 17 | S.foo(); | ||
| 18 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 19 | try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); | ||
| 20 | } | ||
| 21 | const std = @import("std"); | ||
| 22 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_exports/manifest created+54| ... | @@ -0,0 +1,54 @@ | ||
| 1 | #exe { | ||
| 2 | -Mroot=main.zig | ||
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig 0/main.zig | ||
| 6 | #update | ||
| 7 | #check stdout { | ||
| 8 | 123 | ||
| 9 | #} | ||
| 10 | |||
| 11 | #write main.zig 1/main.zig | ||
| 12 | #update { | ||
| 13 | add conflict#} | ||
| 14 | #check errors { | ||
| 15 | main.zig:6:5: error: exported symbol collision: foo | ||
| 16 | main.zig:1:1: note: other symbol here | ||
| 17 | #} | ||
| 18 | |||
| 19 | #write main.zig 2/main.zig | ||
| 20 | #update { | ||
| 21 | resolve conflict#} | ||
| 22 | #check stdout { | ||
| 23 | 123 456 | ||
| 24 | #} | ||
| 25 | |||
| 26 | #write main.zig 3/main.zig | ||
| 27 | #update { | ||
| 28 | put exports in decl#} | ||
| 29 | #check stdout { | ||
| 30 | 123 456 | ||
| 31 | #} | ||
| 32 | |||
| 33 | #write main.zig 4/main.zig | ||
| 34 | #update { | ||
| 35 | remove reference to exporting decl#} | ||
| 36 | #check stdout { | ||
| 37 | #} | ||
| 38 | |||
| 39 | #write main.zig 5/main.zig | ||
| 40 | #update { | ||
| 41 | mark consts as export#} | ||
| 42 | #check stdout { | ||
| 43 | 123 456 | ||
| 44 | #} | ||
| 45 | |||
| 46 | #write main.zig 6/main.zig | ||
| 47 | #update { | ||
| 48 | reintroduce reference to exporting decl, introducing conflict#} | ||
| 49 | #check errors { | ||
| 50 | main.zig:5:5: error: exported symbol collision: bar | ||
| 51 | main.zig:2:1: note: other symbol here | ||
| 52 | main.zig:6:5: error: exported symbol collision: other | ||
| 53 | main.zig:3:1: note: other symbol here | ||
| 54 | #} | ||
test/incremental/change_fn_type+24-9| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() !void { | 6 | pub fn main() !void { |
| 4 | try foo(123); | 7 | try foo(123); |
| 5 | } | 8 | } |
| ... | @@ -9,10 +12,13 @@ fn foo(x: u8) !void { | ... | @@ -9,10 +12,13 @@ fn foo(x: u8) !void { |
| 9 | } | 12 | } |
| 10 | const std = @import("std"); | 13 | const std = @import("std"); |
| 11 | const io = std.Io.Threaded.global_single_threaded.io(); | 14 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 12 | #expect_stdout="123\n" | 15 | #} |
| 16 | #update | ||
| 17 | #check stdout { | ||
| 18 | 123 | ||
| 19 | #} | ||
| 13 | 20 | ||
| 14 | #update=change function type | 21 | #write main.zig { |
| 15 | #file=main.zig | ||
| 16 | pub fn main() !void { | 22 | pub fn main() !void { |
| 17 | try foo(123); | 23 | try foo(123); |
| 18 | } | 24 | } |
| ... | @@ -22,10 +28,14 @@ fn foo(x: i64) !void { | ... | @@ -22,10 +28,14 @@ fn foo(x: i64) !void { |
| 22 | } | 28 | } |
| 23 | const std = @import("std"); | 29 | const std = @import("std"); |
| 24 | const io = std.Io.Threaded.global_single_threaded.io(); | 30 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 25 | #expect_stdout="123\n" | 31 | #} |
| 32 | #update { | ||
| 33 | change function type#} | ||
| 34 | #check stdout { | ||
| 35 | 123 | ||
| 36 | #} | ||
| 26 | 37 | ||
| 27 | #update=change function argument | 38 | #write main.zig { |
| 28 | #file=main.zig | ||
| 29 | pub fn main() !void { | 39 | pub fn main() !void { |
| 30 | try foo(-42); | 40 | try foo(-42); |
| 31 | } | 41 | } |
| ... | @@ -35,4 +45,9 @@ fn foo(x: i64) !void { | ... | @@ -35,4 +45,9 @@ fn foo(x: i64) !void { |
| 35 | } | 45 | } |
| 36 | const std = @import("std"); | 46 | const std = @import("std"); |
| 37 | const io = std.Io.Threaded.global_single_threaded.io(); | 47 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 38 | #expect_stdout="-42\n" | 48 | #} |
| 49 | #update { | ||
| 50 | change function argument#} | ||
| 51 | #check stdout { | ||
| 52 | -42 | ||
| 53 | #} |
test/incremental/change_generic_line_number+20-6| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | const io = std.Io.Threaded.global_single_threaded.io(); | 7 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 5 | fn Printer(message: []const u8) type { | 8 | fn Printer(message: []const u8) type { |
| ... | @@ -13,9 +16,14 @@ pub fn main() !void { | ... | @@ -13,9 +16,14 @@ pub fn main() !void { |
| 13 | try Printer("foo\n").print(); | 16 | try Printer("foo\n").print(); |
| 14 | try Printer("bar\n").print(); | 17 | try Printer("bar\n").print(); |
| 15 | } | 18 | } |
| 16 | #expect_stdout="foo\nbar\n" | 19 | #} |
| 17 | #update=change line number | 20 | #update |
| 18 | #file=main.zig | 21 | #check stdout { |
| 22 | foo | ||
| 23 | bar | ||
| 24 | #} | ||
| 25 | |||
| 26 | #write main.zig { | ||
| 19 | const std = @import("std"); | 27 | const std = @import("std"); |
| 20 | const io = std.Io.Threaded.global_single_threaded.io(); | 28 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 21 | 29 | ||
| ... | @@ -30,4 +38,10 @@ pub fn main() !void { | ... | @@ -30,4 +38,10 @@ pub fn main() !void { |
| 30 | try Printer("foo\n").print(); | 38 | try Printer("foo\n").print(); |
| 31 | try Printer("bar\n").print(); | 39 | try Printer("bar\n").print(); |
| 32 | } | 40 | } |
| 33 | #expect_stdout="foo\nbar\n" | 41 | #} |
| 42 | #update { | ||
| 43 | change line number#} | ||
| 44 | #check stdout { | ||
| 45 | foo | ||
| 46 | bar | ||
| 47 | #} |
test/incremental/change_line_number+18-6| ... | @@ -1,17 +1,29 @@ | ... | @@ -1,17 +1,29 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | pub fn main() !void { | 7 | pub fn main() !void { |
| 5 | try std.Io.File.stdout().writeStreamingAll(io, "foo\n"); | 8 | try std.Io.File.stdout().writeStreamingAll(io, "foo\n"); |
| 6 | } | 9 | } |
| 7 | const io = std.Io.Threaded.global_single_threaded.io(); | 10 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 8 | #expect_stdout="foo\n" | 11 | #} |
| 9 | #update=change line number | 12 | #update |
| 10 | #file=main.zig | 13 | #check stdout { |
| 14 | foo | ||
| 15 | #} | ||
| 16 | |||
| 17 | #write main.zig { | ||
| 11 | const std = @import("std"); | 18 | const std = @import("std"); |
| 12 | 19 | ||
| 13 | pub fn main() !void { | 20 | pub fn main() !void { |
| 14 | try std.Io.File.stdout().writeStreamingAll(io, "foo\n"); | 21 | try std.Io.File.stdout().writeStreamingAll(io, "foo\n"); |
| 15 | } | 22 | } |
| 16 | const io = std.Io.Threaded.global_single_threaded.io(); | 23 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 17 | #expect_stdout="foo\n" | 24 | #} |
| 25 | #update { | ||
| 26 | change line number#} | ||
| 27 | #check stdout { | ||
| 28 | foo | ||
| 29 | #} |
test/incremental/change_module+49-23| ... | @@ -1,61 +1,87 @@ | ... | @@ -1,61 +1,87 @@ |
| 1 | #module=foo=foo.zig | 1 | #exe { |
| 2 | --dep foo -Mroot=main.zig | ||
| 3 | -Mfoo=foo.zig | ||
| 4 | #} | ||
| 2 | 5 | ||
| 3 | #update=initial version | 6 | #write main.zig { |
| 4 | #file=main.zig | ||
| 5 | pub fn main() void { | 7 | pub fn main() void { |
| 6 | _ = @import("foo"); | 8 | _ = @import("foo"); |
| 7 | //_ = @import("other.zig"); | 9 | //_ = @import("other.zig"); |
| 8 | } | 10 | } |
| 9 | #file=foo.zig | 11 | #} |
| 12 | #write foo.zig { | ||
| 10 | comptime { | 13 | comptime { |
| 11 | _ = @import("other.zig"); | 14 | _ = @import("other.zig"); |
| 12 | } | 15 | } |
| 13 | #file=other.zig | 16 | #} |
| 17 | #write other.zig { | ||
| 14 | fn f() void { | 18 | fn f() void { |
| 15 | @compileLog(@src().module); | 19 | @compileLog(@src().module); |
| 16 | } | 20 | } |
| 17 | comptime { | 21 | comptime { |
| 18 | f(); | 22 | f(); |
| 19 | } | 23 | } |
| 20 | #expect_error=other.zig:2:5: error: found compile log statement | 24 | #} |
| 21 | #expect_compile_log=@as([:0]const u8, "foo"[0..3]) | 25 | #update |
| 26 | #check errors { | ||
| 27 | other.zig:2:5: error: found compile log statement | ||
| 22 | 28 | ||
| 23 | #update=change module of other.zig | 29 | Compile Log Output: |
| 24 | #file=main.zig | 30 | @as([:0]const u8, "foo"[0..3]) |
| 31 | #} | ||
| 32 | |||
| 33 | #write main.zig { | ||
| 25 | pub fn main() void { | 34 | pub fn main() void { |
| 26 | _ = @import("foo"); | 35 | _ = @import("foo"); |
| 27 | _ = @import("other.zig"); | 36 | _ = @import("other.zig"); |
| 28 | } | 37 | } |
| 29 | #file=foo.zig | 38 | #} |
| 39 | #write foo.zig { | ||
| 30 | comptime { | 40 | comptime { |
| 31 | //_ = @import("other.zig"); | 41 | //_ = @import("other.zig"); |
| 32 | } | 42 | } |
| 33 | #expect_error=other.zig:2:5: error: found compile log statement | 43 | #} |
| 34 | #expect_compile_log=@as([:0]const u8, "root"[0..4]) | 44 | #update { |
| 45 | change module of other.zig#} | ||
| 46 | #check errors { | ||
| 47 | other.zig:2:5: error: found compile log statement | ||
| 48 | |||
| 49 | Compile Log Output: | ||
| 50 | @as([:0]const u8, "root"[0..4]) | ||
| 51 | #} | ||
| 35 | 52 | ||
| 36 | #update=put other.zig in both modules | 53 | #write main.zig { |
| 37 | #file=main.zig | ||
| 38 | pub fn main() void { | 54 | pub fn main() void { |
| 39 | _ = @import("foo"); | 55 | _ = @import("foo"); |
| 40 | _ = @import("other.zig"); | 56 | _ = @import("other.zig"); |
| 41 | } | 57 | } |
| 42 | #file=foo.zig | 58 | #} |
| 59 | #write foo.zig { | ||
| 43 | comptime { | 60 | comptime { |
| 44 | _ = @import("other.zig"); | 61 | _ = @import("other.zig"); |
| 45 | } | 62 | } |
| 46 | #expect_error=other.zig:1:1: error: file exists in modules 'root' and 'foo' | 63 | #} |
| 47 | #expect_error=other.zig:1:1: note: files must belong to only one module | 64 | #update { |
| 48 | #expect_error=main.zig:3:17: note: file is imported here by the root of module 'root' | 65 | put other.zig in both modules#} |
| 49 | #expect_error=foo.zig:2:17: note: file is imported here by the root of module 'foo' | 66 | #check errors { |
| 67 | other.zig:1:1: error: file exists in modules 'root' and 'foo' | ||
| 68 | other.zig:1:1: note: files must belong to only one module | ||
| 69 | main.zig:3:17: note: file is imported here by the root of module 'root' | ||
| 70 | foo.zig:2:17: note: file is imported here by the root of module 'foo' | ||
| 71 | #} | ||
| 50 | 72 | ||
| 51 | #update=put other.zig in no modules | 73 | #write main.zig { |
| 52 | #file=main.zig | ||
| 53 | pub fn main() void { | 74 | pub fn main() void { |
| 54 | _ = @import("foo"); | 75 | _ = @import("foo"); |
| 55 | //_ = @import("other.zig"); | 76 | //_ = @import("other.zig"); |
| 56 | } | 77 | } |
| 57 | #file=foo.zig | 78 | #} |
| 79 | #write foo.zig { | ||
| 58 | comptime { | 80 | comptime { |
| 59 | //_ = @import("other.zig"); | 81 | //_ = @import("other.zig"); |
| 60 | } | 82 | } |
| 61 | #expect_stdout="" | 83 | #} |
| 84 | #update { | ||
| 85 | put other.zig in no modules#} | ||
| 86 | #check stdout { | ||
| 87 | #} |
test/incremental/change_panic_handler+24-9| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() !u8 { | 6 | pub fn main() !u8 { |
| 4 | var a: u8 = undefined; | 7 | var a: u8 = undefined; |
| 5 | a = 255; | 8 | a = 255; |
| ... | @@ -14,10 +17,13 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { | ... | @@ -14,10 +17,13 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { |
| 14 | } | 17 | } |
| 15 | const std = @import("std"); | 18 | const std = @import("std"); |
| 16 | const io = std.Io.Threaded.global_single_threaded.io(); | 19 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 17 | #expect_stdout="panic message: integer overflow\n" | 20 | #} |
| 21 | #update | ||
| 22 | #check stdout { | ||
| 23 | panic message: integer overflow | ||
| 24 | #} | ||
| 18 | 25 | ||
| 19 | #update=change the panic handler body | 26 | #write main.zig { |
| 20 | #file=main.zig | ||
| 21 | pub fn main() !u8 { | 27 | pub fn main() !u8 { |
| 22 | var a: u8 = undefined; | 28 | var a: u8 = undefined; |
| 23 | a = 255; | 29 | a = 255; |
| ... | @@ -32,10 +38,14 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { | ... | @@ -32,10 +38,14 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { |
| 32 | } | 38 | } |
| 33 | const std = @import("std"); | 39 | const std = @import("std"); |
| 34 | const io = std.Io.Threaded.global_single_threaded.io(); | 40 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 35 | #expect_stdout="new panic message: integer overflow\n" | 41 | #} |
| 42 | #update { | ||
| 43 | change the panic handler body#} | ||
| 44 | #check stdout { | ||
| 45 | new panic message: integer overflow | ||
| 46 | #} | ||
| 36 | 47 | ||
| 37 | #update=change the panic handler function value | 48 | #write main.zig { |
| 38 | #file=main.zig | ||
| 39 | pub fn main() !u8 { | 49 | pub fn main() !u8 { |
| 40 | var a: u8 = undefined; | 50 | var a: u8 = undefined; |
| 41 | a = 255; | 51 | a = 255; |
| ... | @@ -50,4 +60,9 @@ fn myPanicNew(msg: []const u8, _: ?usize) noreturn { | ... | @@ -50,4 +60,9 @@ fn myPanicNew(msg: []const u8, _: ?usize) noreturn { |
| 50 | } | 60 | } |
| 51 | const std = @import("std"); | 61 | const std = @import("std"); |
| 52 | const io = std.Io.Threaded.global_single_threaded.io(); | 62 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 53 | #expect_stdout="third panic message: integer overflow\n" | 63 | #} |
| 64 | #update { | ||
| 65 | change the panic handler function value#} | ||
| 66 | #check stdout { | ||
| 67 | third panic message: integer overflow | ||
| 68 | #} |
test/incremental/change_panic_handler_explicit deleted-149| ... | @@ -1,149 +0,0 @@ | ||
| 1 | #update=initial version | ||
| 2 | #file=main.zig | ||
| 3 | pub fn main() !u8 { | ||
| 4 | var a: u8 = undefined; | ||
| 5 | a = 255; | ||
| 6 | _ = a + 1; | ||
| 7 | return 1; | ||
| 8 | } | ||
| 9 | const no_panic = std.debug.no_panic; | ||
| 10 | pub const panic = struct { | ||
| 11 | pub const call = myPanic; | ||
| 12 | pub fn integerOverflow() noreturn { | ||
| 13 | @panic("integer overflow"); | ||
| 14 | } | ||
| 15 | pub const sentinelMismatch = no_panic.sentinelMismatch; | ||
| 16 | pub const unwrapError = no_panic.unwrapError; | ||
| 17 | pub const outOfBounds = no_panic.outOfBounds; | ||
| 18 | pub const startGreaterThanEnd = no_panic.startGreaterThanEnd; | ||
| 19 | pub const inactiveUnionField = no_panic.inactiveUnionField; | ||
| 20 | pub const sliceCastLenRemainder = no_panic.sliceCastLenRemainder; | ||
| 21 | pub const reachedUnreachable = no_panic.reachedUnreachable; | ||
| 22 | pub const unwrapNull = no_panic.unwrapNull; | ||
| 23 | pub const castToNull = no_panic.castToNull; | ||
| 24 | pub const incorrectAlignment = no_panic.incorrectAlignment; | ||
| 25 | pub const invalidErrorCode = no_panic.invalidErrorCode; | ||
| 26 | pub const unexpectedErrorCode = no_panic.unexpectedErrorCode; | ||
| 27 | pub const integerOutOfBounds = no_panic.integerOutOfBounds; | ||
| 28 | pub const shlOverflow = no_panic.shlOverflow; | ||
| 29 | pub const shrOverflow = no_panic.shrOverflow; | ||
| 30 | pub const divideByZero = no_panic.divideByZero; | ||
| 31 | pub const exactDivisionRemainder = no_panic.exactDivisionRemainder; | ||
| 32 | pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds; | ||
| 33 | pub const corruptSwitch = no_panic.corruptSwitch; | ||
| 34 | pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; | ||
| 35 | pub const invalidEnumValue = no_panic.invalidEnumValue; | ||
| 36 | pub const forLenMismatch = no_panic.forLenMismatch; | ||
| 37 | pub const copyLenMismatch = no_panic.copyLenMismatch; | ||
| 38 | pub const memcpyAlias = no_panic.memcpyAlias; | ||
| 39 | pub const noreturnReturned = no_panic.noreturnReturned; | ||
| 40 | pub const loadUninstantiableType = no_panic.loadUninstantiableType; | ||
| 41 | }; | ||
| 42 | fn myPanic(msg: []const u8, _: ?usize) noreturn { | ||
| 43 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 44 | stdout_writer.interface.print("panic message: {s}\n", .{msg}) catch {}; | ||
| 45 | std.process.exit(0); | ||
| 46 | } | ||
| 47 | const std = @import("std"); | ||
| 48 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 49 | #expect_stdout="panic message: integer overflow\n" | ||
| 50 | |||
| 51 | #update=change the panic handler body | ||
| 52 | #file=main.zig | ||
| 53 | pub fn main() !u8 { | ||
| 54 | var a: u8 = undefined; | ||
| 55 | a = 255; | ||
| 56 | _ = a + 1; | ||
| 57 | return 1; | ||
| 58 | } | ||
| 59 | const no_panic = std.debug.no_panic; | ||
| 60 | pub const panic = struct { | ||
| 61 | pub const call = myPanic; | ||
| 62 | pub fn integerOverflow() noreturn { | ||
| 63 | @panic("integer overflow"); | ||
| 64 | } | ||
| 65 | pub const sentinelMismatch = no_panic.sentinelMismatch; | ||
| 66 | pub const unwrapError = no_panic.unwrapError; | ||
| 67 | pub const outOfBounds = no_panic.outOfBounds; | ||
| 68 | pub const startGreaterThanEnd = no_panic.startGreaterThanEnd; | ||
| 69 | pub const inactiveUnionField = no_panic.inactiveUnionField; | ||
| 70 | pub const sliceCastLenRemainder = no_panic.sliceCastLenRemainder; | ||
| 71 | pub const reachedUnreachable = no_panic.reachedUnreachable; | ||
| 72 | pub const unwrapNull = no_panic.unwrapNull; | ||
| 73 | pub const castToNull = no_panic.castToNull; | ||
| 74 | pub const incorrectAlignment = no_panic.incorrectAlignment; | ||
| 75 | pub const invalidErrorCode = no_panic.invalidErrorCode; | ||
| 76 | pub const unexpectedErrorCode = no_panic.unexpectedErrorCode; | ||
| 77 | pub const integerOutOfBounds = no_panic.integerOutOfBounds; | ||
| 78 | pub const shlOverflow = no_panic.shlOverflow; | ||
| 79 | pub const shrOverflow = no_panic.shrOverflow; | ||
| 80 | pub const divideByZero = no_panic.divideByZero; | ||
| 81 | pub const exactDivisionRemainder = no_panic.exactDivisionRemainder; | ||
| 82 | pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds; | ||
| 83 | pub const corruptSwitch = no_panic.corruptSwitch; | ||
| 84 | pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; | ||
| 85 | pub const invalidEnumValue = no_panic.invalidEnumValue; | ||
| 86 | pub const forLenMismatch = no_panic.forLenMismatch; | ||
| 87 | pub const copyLenMismatch = no_panic.copyLenMismatch; | ||
| 88 | pub const memcpyAlias = no_panic.memcpyAlias; | ||
| 89 | pub const noreturnReturned = no_panic.noreturnReturned; | ||
| 90 | pub const loadUninstantiableType = no_panic.loadUninstantiableType; | ||
| 91 | }; | ||
| 92 | fn myPanic(msg: []const u8, _: ?usize) noreturn { | ||
| 93 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 94 | stdout_writer.interface.print("new panic message: {s}\n", .{msg}) catch {}; | ||
| 95 | std.process.exit(0); | ||
| 96 | } | ||
| 97 | const std = @import("std"); | ||
| 98 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 99 | #expect_stdout="new panic message: integer overflow\n" | ||
| 100 | |||
| 101 | #update=change the panic handler function value | ||
| 102 | #file=main.zig | ||
| 103 | pub fn main() !u8 { | ||
| 104 | var a: u8 = undefined; | ||
| 105 | a = 255; | ||
| 106 | _ = a + 1; | ||
| 107 | return 1; | ||
| 108 | } | ||
| 109 | const no_panic = std.debug.no_panic; | ||
| 110 | pub const panic = struct { | ||
| 111 | pub const call = myPanicNew; | ||
| 112 | pub fn integerOverflow() noreturn { | ||
| 113 | @panic("integer overflow"); | ||
| 114 | } | ||
| 115 | pub const sentinelMismatch = std.debug.no_panic.sentinelMismatch; | ||
| 116 | pub const unwrapError = std.debug.no_panic.unwrapError; | ||
| 117 | pub const outOfBounds = std.debug.no_panic.outOfBounds; | ||
| 118 | pub const startGreaterThanEnd = std.debug.no_panic.startGreaterThanEnd; | ||
| 119 | pub const inactiveUnionField = std.debug.no_panic.inactiveUnionField; | ||
| 120 | pub const sliceCastLenRemainder = no_panic.sliceCastLenRemainder; | ||
| 121 | pub const reachedUnreachable = no_panic.reachedUnreachable; | ||
| 122 | pub const unwrapNull = no_panic.unwrapNull; | ||
| 123 | pub const castToNull = no_panic.castToNull; | ||
| 124 | pub const incorrectAlignment = no_panic.incorrectAlignment; | ||
| 125 | pub const invalidErrorCode = no_panic.invalidErrorCode; | ||
| 126 | pub const unexpectedErrorCode = no_panic.unexpectedErrorCode; | ||
| 127 | pub const integerOutOfBounds = no_panic.integerOutOfBounds; | ||
| 128 | pub const shlOverflow = no_panic.shlOverflow; | ||
| 129 | pub const shrOverflow = no_panic.shrOverflow; | ||
| 130 | pub const divideByZero = no_panic.divideByZero; | ||
| 131 | pub const exactDivisionRemainder = no_panic.exactDivisionRemainder; | ||
| 132 | pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds; | ||
| 133 | pub const corruptSwitch = no_panic.corruptSwitch; | ||
| 134 | pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; | ||
| 135 | pub const invalidEnumValue = no_panic.invalidEnumValue; | ||
| 136 | pub const forLenMismatch = no_panic.forLenMismatch; | ||
| 137 | pub const copyLenMismatch = no_panic.copyLenMismatch; | ||
| 138 | pub const memcpyAlias = no_panic.memcpyAlias; | ||
| 139 | pub const noreturnReturned = no_panic.noreturnReturned; | ||
| 140 | pub const loadUninstantiableType = no_panic.loadUninstantiableType; | ||
| 141 | }; | ||
| 142 | fn myPanicNew(msg: []const u8, _: ?usize) noreturn { | ||
| 143 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 144 | stdout_writer.interface.print("third panic message: {s}\n", .{msg}) catch {}; | ||
| 145 | std.process.exit(0); | ||
| 146 | } | ||
| 147 | const std = @import("std"); | ||
| 148 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
| 149 | #expect_stdout="third panic message: integer overflow\n" | ||
test/incremental/change_panic_handler_explicit/0/main.zig created+46| ... | @@ -0,0 +1,46 @@ | ||
| 1 | pub fn main() !u8 { | ||
| 2 | var a: u8 = undefined; | ||
| 3 | a = 255; | ||
| 4 | _ = a + 1; | ||
| 5 | return 1; | ||
| 6 | } | ||
| 7 | const no_panic = std.debug.no_panic; | ||
| 8 | pub const panic = struct { | ||
| 9 | pub const call = myPanic; | ||
| 10 | pub fn integerOverflow() noreturn { | ||
| 11 | @panic("integer overflow"); | ||
| 12 | } | ||
| 13 | pub const sentinelMismatch = no_panic.sentinelMismatch; | ||
| 14 | pub const unwrapError = no_panic.unwrapError; | ||
| 15 | pub const outOfBounds = no_panic.outOfBounds; | ||
| 16 | pub const startGreaterThanEnd = no_panic.startGreaterThanEnd; | ||
| 17 | pub const inactiveUnionField = no_panic.inactiveUnionField; | ||
| 18 | pub const sliceCastLenRemainder = no_panic.sliceCastLenRemainder; | ||
| 19 | pub const reachedUnreachable = no_panic.reachedUnreachable; | ||
| 20 | pub const unwrapNull = no_panic.unwrapNull; | ||
| 21 | pub const castToNull = no_panic.castToNull; | ||
| 22 | pub const incorrectAlignment = no_panic.incorrectAlignment; | ||
| 23 | pub const invalidErrorCode = no_panic.invalidErrorCode; | ||
| 24 | pub const unexpectedErrorCode = no_panic.unexpectedErrorCode; | ||
| 25 | pub const integerOutOfBounds = no_panic.integerOutOfBounds; | ||
| 26 | pub const shlOverflow = no_panic.shlOverflow; | ||
| 27 | pub const shrOverflow = no_panic.shrOverflow; | ||
| 28 | pub const divideByZero = no_panic.divideByZero; | ||
| 29 | pub const exactDivisionRemainder = no_panic.exactDivisionRemainder; | ||
| 30 | pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds; | ||
| 31 | pub const corruptSwitch = no_panic.corruptSwitch; | ||
| 32 | pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; | ||
| 33 | pub const invalidEnumValue = no_panic.invalidEnumValue; | ||
| 34 | pub const forLenMismatch = no_panic.forLenMismatch; | ||
| 35 | pub const copyLenMismatch = no_panic.copyLenMismatch; | ||
| 36 | pub const memcpyAlias = no_panic.memcpyAlias; | ||
| 37 | pub const noreturnReturned = no_panic.noreturnReturned; | ||
| 38 | pub const loadUninstantiableType = no_panic.loadUninstantiableType; | ||
| 39 | }; | ||
| 40 | fn myPanic(msg: []const u8, _: ?usize) noreturn { | ||
| 41 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 42 | stdout_writer.interface.print("panic message: {s}\n", .{msg}) catch {}; | ||
| 43 | std.process.exit(0); | ||
| 44 | } | ||
| 45 | const std = @import("std"); | ||
| 46 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_panic_handler_explicit/1/main.zig created+46| ... | @@ -0,0 +1,46 @@ | ||
| 1 | pub fn main() !u8 { | ||
| 2 | var a: u8 = undefined; | ||
| 3 | a = 255; | ||
| 4 | _ = a + 1; | ||
| 5 | return 1; | ||
| 6 | } | ||
| 7 | const no_panic = std.debug.no_panic; | ||
| 8 | pub const panic = struct { | ||
| 9 | pub const call = myPanic; | ||
| 10 | pub fn integerOverflow() noreturn { | ||
| 11 | @panic("integer overflow"); | ||
| 12 | } | ||
| 13 | pub const sentinelMismatch = no_panic.sentinelMismatch; | ||
| 14 | pub const unwrapError = no_panic.unwrapError; | ||
| 15 | pub const outOfBounds = no_panic.outOfBounds; | ||
| 16 | pub const startGreaterThanEnd = no_panic.startGreaterThanEnd; | ||
| 17 | pub const inactiveUnionField = no_panic.inactiveUnionField; | ||
| 18 | pub const sliceCastLenRemainder = no_panic.sliceCastLenRemainder; | ||
| 19 | pub const reachedUnreachable = no_panic.reachedUnreachable; | ||
| 20 | pub const unwrapNull = no_panic.unwrapNull; | ||
| 21 | pub const castToNull = no_panic.castToNull; | ||
| 22 | pub const incorrectAlignment = no_panic.incorrectAlignment; | ||
| 23 | pub const invalidErrorCode = no_panic.invalidErrorCode; | ||
| 24 | pub const unexpectedErrorCode = no_panic.unexpectedErrorCode; | ||
| 25 | pub const integerOutOfBounds = no_panic.integerOutOfBounds; | ||
| 26 | pub const shlOverflow = no_panic.shlOverflow; | ||
| 27 | pub const shrOverflow = no_panic.shrOverflow; | ||
| 28 | pub const divideByZero = no_panic.divideByZero; | ||
| 29 | pub const exactDivisionRemainder = no_panic.exactDivisionRemainder; | ||
| 30 | pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds; | ||
| 31 | pub const corruptSwitch = no_panic.corruptSwitch; | ||
| 32 | pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; | ||
| 33 | pub const invalidEnumValue = no_panic.invalidEnumValue; | ||
| 34 | pub const forLenMismatch = no_panic.forLenMismatch; | ||
| 35 | pub const copyLenMismatch = no_panic.copyLenMismatch; | ||
| 36 | pub const memcpyAlias = no_panic.memcpyAlias; | ||
| 37 | pub const noreturnReturned = no_panic.noreturnReturned; | ||
| 38 | pub const loadUninstantiableType = no_panic.loadUninstantiableType; | ||
| 39 | }; | ||
| 40 | fn myPanic(msg: []const u8, _: ?usize) noreturn { | ||
| 41 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 42 | stdout_writer.interface.print("new panic message: {s}\n", .{msg}) catch {}; | ||
| 43 | std.process.exit(0); | ||
| 44 | } | ||
| 45 | const std = @import("std"); | ||
| 46 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_panic_handler_explicit/2/main.zig created+46| ... | @@ -0,0 +1,46 @@ | ||
| 1 | pub fn main() !u8 { | ||
| 2 | var a: u8 = undefined; | ||
| 3 | a = 255; | ||
| 4 | _ = a + 1; | ||
| 5 | return 1; | ||
| 6 | } | ||
| 7 | const no_panic = std.debug.no_panic; | ||
| 8 | pub const panic = struct { | ||
| 9 | pub const call = myPanicNew; | ||
| 10 | pub fn integerOverflow() noreturn { | ||
| 11 | @panic("integer overflow"); | ||
| 12 | } | ||
| 13 | pub const sentinelMismatch = no_panic.sentinelMismatch; | ||
| 14 | pub const unwrapError = no_panic.unwrapError; | ||
| 15 | pub const outOfBounds = no_panic.outOfBounds; | ||
| 16 | pub const startGreaterThanEnd = no_panic.startGreaterThanEnd; | ||
| 17 | pub const inactiveUnionField = no_panic.inactiveUnionField; | ||
| 18 | pub const sliceCastLenRemainder = no_panic.sliceCastLenRemainder; | ||
| 19 | pub const reachedUnreachable = no_panic.reachedUnreachable; | ||
| 20 | pub const unwrapNull = no_panic.unwrapNull; | ||
| 21 | pub const castToNull = no_panic.castToNull; | ||
| 22 | pub const incorrectAlignment = no_panic.incorrectAlignment; | ||
| 23 | pub const invalidErrorCode = no_panic.invalidErrorCode; | ||
| 24 | pub const unexpectedErrorCode = no_panic.unexpectedErrorCode; | ||
| 25 | pub const integerOutOfBounds = no_panic.integerOutOfBounds; | ||
| 26 | pub const shlOverflow = no_panic.shlOverflow; | ||
| 27 | pub const shrOverflow = no_panic.shrOverflow; | ||
| 28 | pub const divideByZero = no_panic.divideByZero; | ||
| 29 | pub const exactDivisionRemainder = no_panic.exactDivisionRemainder; | ||
| 30 | pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds; | ||
| 31 | pub const corruptSwitch = no_panic.corruptSwitch; | ||
| 32 | pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; | ||
| 33 | pub const invalidEnumValue = no_panic.invalidEnumValue; | ||
| 34 | pub const forLenMismatch = no_panic.forLenMismatch; | ||
| 35 | pub const copyLenMismatch = no_panic.copyLenMismatch; | ||
| 36 | pub const memcpyAlias = no_panic.memcpyAlias; | ||
| 37 | pub const noreturnReturned = no_panic.noreturnReturned; | ||
| 38 | pub const loadUninstantiableType = no_panic.loadUninstantiableType; | ||
| 39 | }; | ||
| 40 | fn myPanicNew(msg: []const u8, _: ?usize) noreturn { | ||
| 41 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 42 | stdout_writer.interface.print("third panic message: {s}\n", .{msg}) catch {}; | ||
| 43 | std.process.exit(0); | ||
| 44 | } | ||
| 45 | const std = @import("std"); | ||
| 46 | const io = std.Io.Threaded.global_single_threaded.io(); | ||
test/incremental/change_panic_handler_explicit/manifest created+23| ... | @@ -0,0 +1,23 @@ | ||
| 1 | #exe { | ||
| 2 | -Mroot=main.zig | ||
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig 0/main.zig | ||
| 6 | #update | ||
| 7 | #check stdout { | ||
| 8 | panic message: integer overflow | ||
| 9 | #} | ||
| 10 | |||
| 11 | #write main.zig 1/main.zig | ||
| 12 | #update { | ||
| 13 | change the panic handler body#} | ||
| 14 | #check stdout { | ||
| 15 | new panic message: integer overflow | ||
| 16 | #} | ||
| 17 | |||
| 18 | #write main.zig 2/main.zig | ||
| 19 | #update { | ||
| 20 | change the panic handler function value#} | ||
| 21 | #check stdout { | ||
| 22 | third panic message: integer overflow | ||
| 23 | #} | ||
test/incremental/change_reify_struct_field_type+16-6| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | fn getEnum(s: @Struct(.auto, null, &.{"field"}, &.{struct { tag: Enum }}, &.{.{}})) Enum { | 6 | fn getEnum(s: @Struct(.auto, null, &.{"field"}, &.{struct { tag: Enum }}, &.{.{}})) Enum { |
| 4 | return s.field.tag; | 7 | return s.field.tag; |
| 5 | } | 8 | } |
| ... | @@ -8,9 +11,12 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -8,9 +11,12 @@ pub fn main(init: std.process.Init) !void { |
| 8 | } | 11 | } |
| 9 | const Enum = enum { foo, bar }; | 12 | const Enum = enum { foo, bar }; |
| 10 | const std = @import("std"); | 13 | const std = @import("std"); |
| 11 | #expect_stdout="foo" | 14 | #} |
| 12 | #update=change field type | 15 | #update |
| 13 | #file=main.zig | 16 | #check stdout { |
| 17 | foo#} | ||
| 18 | |||
| 19 | #write main.zig { | ||
| 14 | fn getEnum(s: @Struct(.auto, null, &.{"field"}, &.{Enum}, &.{.{}})) Enum { | 20 | fn getEnum(s: @Struct(.auto, null, &.{"field"}, &.{Enum}, &.{.{}})) Enum { |
| 15 | return s.field; | 21 | return s.field; |
| 16 | } | 22 | } |
| ... | @@ -19,4 +25,8 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -19,4 +25,8 @@ pub fn main(init: std.process.Init) !void { |
| 19 | } | 25 | } |
| 20 | const Enum = enum { foo, bar }; | 26 | const Enum = enum { foo, bar }; |
| 21 | const std = @import("std"); | 27 | const std = @import("std"); |
| 22 | #expect_stdout="bar" | 28 | #} |
| 29 | #update { | ||
| 30 | change field type#} | ||
| 31 | #check stdout { | ||
| 32 | bar#} |
test/incremental/change_shift_op+18-6| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() !void { | 6 | pub fn main() !void { |
| 4 | try foo(0x1300); | 7 | try foo(0x1300); |
| 5 | } | 8 | } |
| ... | @@ -9,9 +12,13 @@ fn foo(x: u16) !void { | ... | @@ -9,9 +12,13 @@ fn foo(x: u16) !void { |
| 9 | } | 12 | } |
| 10 | const std = @import("std"); | 13 | const std = @import("std"); |
| 11 | const io = std.Io.Threaded.global_single_threaded.io(); | 14 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 12 | #expect_stdout="0x3000\n" | 15 | #} |
| 13 | #update=change to right shift | 16 | #update |
| 14 | #file=main.zig | 17 | #check stdout { |
| 18 | 0x3000 | ||
| 19 | #} | ||
| 20 | |||
| 21 | #write main.zig { | ||
| 15 | pub fn main() !void { | 22 | pub fn main() !void { |
| 16 | try foo(0x1300); | 23 | try foo(0x1300); |
| 17 | } | 24 | } |
| ... | @@ -21,4 +28,9 @@ fn foo(x: u16) !void { | ... | @@ -21,4 +28,9 @@ fn foo(x: u16) !void { |
| 21 | } | 28 | } |
| 22 | const std = @import("std"); | 29 | const std = @import("std"); |
| 23 | const io = std.Io.Threaded.global_single_threaded.io(); | 30 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 24 | #expect_stdout="0x130\n" | 31 | #} |
| 32 | #update { | ||
| 33 | change to right shift#} | ||
| 34 | #check stdout { | ||
| 35 | 0x130 | ||
| 36 | #} |
test/incremental/change_struct_same_fields+24-9| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const S = extern struct { x: u8, y: u8 }; | 6 | const S = extern struct { x: u8, y: u8 }; |
| 4 | pub fn main() !void { | 7 | pub fn main() !void { |
| 5 | const val: S = .{ .x = 100, .y = 200 }; | 8 | const val: S = .{ .x = 100, .y = 200 }; |
| ... | @@ -14,10 +17,13 @@ fn foo(val: *const S) !void { | ... | @@ -14,10 +17,13 @@ fn foo(val: *const S) !void { |
| 14 | } | 17 | } |
| 15 | const std = @import("std"); | 18 | const std = @import("std"); |
| 16 | const io = std.Io.Threaded.global_single_threaded.io(); | 19 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 17 | #expect_stdout="100 200\n" | 20 | #} |
| 21 | #update | ||
| 22 | #check stdout { | ||
| 23 | 100 200 | ||
| 24 | #} | ||
| 18 | 25 | ||
| 19 | #update=change struct layout | 26 | #write main.zig { |
| 20 | #file=main.zig | ||
| 21 | const S = extern struct { x: u32, y: u32 }; | 27 | const S = extern struct { x: u32, y: u32 }; |
| 22 | pub fn main() !void { | 28 | pub fn main() !void { |
| 23 | const val: S = .{ .x = 100, .y = 200 }; | 29 | const val: S = .{ .x = 100, .y = 200 }; |
| ... | @@ -32,10 +38,14 @@ fn foo(val: *const S) !void { | ... | @@ -32,10 +38,14 @@ fn foo(val: *const S) !void { |
| 32 | } | 38 | } |
| 33 | const std = @import("std"); | 39 | const std = @import("std"); |
| 34 | const io = std.Io.Threaded.global_single_threaded.io(); | 40 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 35 | #expect_stdout="100 200\n" | 41 | #} |
| 42 | #update { | ||
| 43 | change struct layout#} | ||
| 44 | #check stdout { | ||
| 45 | 100 200 | ||
| 46 | #} | ||
| 36 | 47 | ||
| 37 | #update=change values | 48 | #write main.zig { |
| 38 | #file=main.zig | ||
| 39 | const S = extern struct { x: u32, y: u32 }; | 49 | const S = extern struct { x: u32, y: u32 }; |
| 40 | pub fn main() !void { | 50 | pub fn main() !void { |
| 41 | const val: S = .{ .x = 1234, .y = 5678 }; | 51 | const val: S = .{ .x = 1234, .y = 5678 }; |
| ... | @@ -50,4 +60,9 @@ fn foo(val: *const S) !void { | ... | @@ -50,4 +60,9 @@ fn foo(val: *const S) !void { |
| 50 | } | 60 | } |
| 51 | const std = @import("std"); | 61 | const std = @import("std"); |
| 52 | const io = std.Io.Threaded.global_single_threaded.io(); | 62 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 53 | #expect_stdout="1234 5678\n" | 63 | #} |
| 64 | #update { | ||
| 65 | change values#} | ||
| 66 | #check stdout { | ||
| 67 | 1234 5678 | ||
| 68 | #} |
test/incremental/change_union_tag_type+25-10| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const A = enum(u8) { a }; | 6 | const A = enum(u8) { a }; |
| 4 | const B = enum(u8) { b }; | 7 | const B = enum(u8) { b }; |
| 5 | const Foo = union(A) { a: u8 }; | 8 | const Foo = union(A) { a: u8 }; |
| ... | @@ -9,10 +12,13 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -9,10 +12,13 @@ pub fn main(init: std.process.Init) !void { |
| 9 | try stdout_writer.interface.print("{s}\n", .{field_name}); | 12 | try stdout_writer.interface.print("{s}\n", .{field_name}); |
| 10 | } | 13 | } |
| 11 | const std = @import("std"); | 14 | const std = @import("std"); |
| 12 | #expect_stdout="a\n" | 15 | #} |
| 16 | #update | ||
| 17 | #check stdout { | ||
| 18 | a | ||
| 19 | #} | ||
| 13 | 20 | ||
| 14 | #update=change tag type | 21 | #write main.zig { |
| 15 | #file=main.zig | ||
| 16 | const A = enum(u8) { a }; | 22 | const A = enum(u8) { a }; |
| 17 | const B = enum(u8) { b }; | 23 | const B = enum(u8) { b }; |
| 18 | const Foo = union(B) { a: u8 }; | 24 | const Foo = union(B) { a: u8 }; |
| ... | @@ -22,11 +28,15 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -22,11 +28,15 @@ pub fn main(init: std.process.Init) !void { |
| 22 | try stdout_writer.interface.print("{s}\n", .{field_name}); | 28 | try stdout_writer.interface.print("{s}\n", .{field_name}); |
| 23 | } | 29 | } |
| 24 | const std = @import("std"); | 30 | const std = @import("std"); |
| 25 | #expect_error=main.zig:3:24: error: no field named 'a' in enum 'main.B' | 31 | #} |
| 26 | #expect_error=main.zig:2:11: note: enum declared here | 32 | #update { |
| 33 | change tag type#} | ||
| 34 | #check errors { | ||
| 35 | main.zig:3:24: error: no field named 'a' in enum 'main.B' | ||
| 36 | main.zig:2:11: note: enum declared here | ||
| 37 | #} | ||
| 27 | 38 | ||
| 28 | #update=change field name to match new tag type | 39 | #write main.zig { |
| 29 | #file=main.zig | ||
| 30 | const A = enum(u8) { a }; | 40 | const A = enum(u8) { a }; |
| 31 | const B = enum(u8) { b }; | 41 | const B = enum(u8) { b }; |
| 32 | const Foo = union(B) { b: u8 }; | 42 | const Foo = union(B) { b: u8 }; |
| ... | @@ -36,4 +46,9 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -36,4 +46,9 @@ pub fn main(init: std.process.Init) !void { |
| 36 | try stdout_writer.interface.print("{s}\n", .{field_name}); | 46 | try stdout_writer.interface.print("{s}\n", .{field_name}); |
| 37 | } | 47 | } |
| 38 | const std = @import("std"); | 48 | const std = @import("std"); |
| 39 | #expect_stdout="b\n" | 49 | #} |
| 50 | #update { | ||
| 51 | change field name to match new tag type#} | ||
| 52 | #check stdout { | ||
| 53 | b | ||
| 54 | #} |
test/incremental/change_zon_file+48-21| ... | @@ -1,47 +1,74 @@ | ... | @@ -1,47 +1,74 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | const message: []const u8 = @import("message.zon"); | 7 | const message: []const u8 = @import("message.zon"); |
| 5 | pub fn main() !void { | 8 | pub fn main() !void { |
| 6 | try std.Io.File.stdout().writeStreamingAll(io, message); | 9 | try std.Io.File.stdout().writeStreamingAll(io, message); |
| 7 | } | 10 | } |
| 8 | const io = std.Io.Threaded.global_single_threaded.io(); | 11 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 9 | #file=message.zon | 12 | #} |
| 13 | #write message.zon { | ||
| 10 | "Hello, World!\n" | 14 | "Hello, World!\n" |
| 11 | #expect_stdout="Hello, World!\n" | 15 | #} |
| 16 | #update | ||
| 17 | #check stdout { | ||
| 18 | Hello, World! | ||
| 19 | #} | ||
| 12 | 20 | ||
| 13 | #update=change ZON file contents | 21 | #write message.zon { |
| 14 | #file=message.zon | ||
| 15 | "Hello again, World!\n" | 22 | "Hello again, World!\n" |
| 16 | #expect_stdout="Hello again, World!\n" | 23 | #} |
| 24 | #update { | ||
| 25 | change ZON file contents#} | ||
| 26 | #check stdout { | ||
| 27 | Hello again, World! | ||
| 28 | #} | ||
| 17 | 29 | ||
| 18 | #update=delete file | 30 | #delete message.zon |
| 19 | #rm_file=message.zon | 31 | #update { |
| 20 | #expect_error=message.zon:1:1: error: unable to load "message.zon": FileNotFound | 32 | delete file#} |
| 21 | #expect_error=main.zig:2:37: note: file imported here | 33 | #check errors { |
| 34 | message.zon:1:1: error: unable to load "message.zon": FileNotFound | ||
| 35 | main.zig:2:37: note: file imported here | ||
| 36 | #} | ||
| 22 | 37 | ||
| 23 | #update=remove reference to ZON file | 38 | #write main.zig { |
| 24 | #file=main.zig | ||
| 25 | const std = @import("std"); | 39 | const std = @import("std"); |
| 26 | const message: []const u8 = @import("message.zon"); | 40 | const message: []const u8 = @import("message.zon"); |
| 27 | pub fn main() !void { | 41 | pub fn main() !void { |
| 28 | try std.Io.File.stdout().writeStreamingAll(io, "a hardcoded string\n"); | 42 | try std.Io.File.stdout().writeStreamingAll(io, "a hardcoded string\n"); |
| 29 | } | 43 | } |
| 30 | const io = std.Io.Threaded.global_single_threaded.io(); | 44 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 31 | #expect_error=message.zon:1:1: error: unable to load "message.zon": FileNotFound | 45 | #} |
| 32 | #expect_error=main.zig:2:37: note: file imported here | 46 | #update { |
| 47 | remove reference to ZON file#} | ||
| 48 | #check errors { | ||
| 49 | message.zon:1:1: error: unable to load "message.zon": FileNotFound | ||
| 50 | main.zig:2:37: note: file imported here | ||
| 51 | #} | ||
| 33 | 52 | ||
| 34 | #update=recreate ZON file | 53 | #write message.zon { |
| 35 | #file=message.zon | ||
| 36 | "We're back, World!\n" | 54 | "We're back, World!\n" |
| 37 | #expect_stdout="a hardcoded string\n" | 55 | #} |
| 56 | #update { | ||
| 57 | recreate ZON file#} | ||
| 58 | #check stdout { | ||
| 59 | a hardcoded string | ||
| 60 | #} | ||
| 38 | 61 | ||
| 39 | #update=re-introduce reference to ZON file | 62 | #write main.zig { |
| 40 | #file=main.zig | ||
| 41 | const std = @import("std"); | 63 | const std = @import("std"); |
| 42 | const message: []const u8 = @import("message.zon"); | 64 | const message: []const u8 = @import("message.zon"); |
| 43 | pub fn main() !void { | 65 | pub fn main() !void { |
| 44 | try std.Io.File.stdout().writeStreamingAll(io, message); | 66 | try std.Io.File.stdout().writeStreamingAll(io, message); |
| 45 | } | 67 | } |
| 46 | const io = std.Io.Threaded.global_single_threaded.io(); | 68 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 47 | #expect_stdout="We're back, World!\n" | 69 | #} |
| 70 | #update { | ||
| 71 | re-introduce reference to ZON file#} | ||
| 72 | #check stdout { | ||
| 73 | We're back, World! | ||
| 74 | #} |
test/incremental/change_zon_file_no_result_type+19-7| ... | @@ -1,21 +1,33 @@ | ... | @@ -1,21 +1,33 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | const io = std.Io.Threaded.global_single_threaded.io(); | 7 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 5 | pub fn main() !void { | 8 | pub fn main() !void { |
| 6 | try std.Io.File.stdout().writeStreamingAll(io, @import("foo.zon").message); | 9 | try std.Io.File.stdout().writeStreamingAll(io, @import("foo.zon").message); |
| 7 | } | 10 | } |
| 8 | #file=foo.zon | 11 | #} |
| 12 | #write foo.zon { | ||
| 9 | .{ | 13 | .{ |
| 10 | .message = "Hello, World!\n", | 14 | .message = "Hello, World!\n", |
| 11 | .a_number = 0, | 15 | .a_number = 0, |
| 12 | } | 16 | } |
| 13 | #expect_stdout="Hello, World!\n" | 17 | #} |
| 18 | #update | ||
| 19 | #check stdout { | ||
| 20 | Hello, World! | ||
| 21 | #} | ||
| 14 | 22 | ||
| 15 | #update=change ZON file | 23 | #write foo.zon { |
| 16 | #file=foo.zon | ||
| 17 | .{ | 24 | .{ |
| 18 | .message = "Hello again, World!\n", | 25 | .message = "Hello again, World!\n", |
| 19 | .b_number = 0, | 26 | .b_number = 0, |
| 20 | } | 27 | } |
| 21 | #expect_stdout="Hello again, World!\n" | 28 | #} |
| 29 | #update { | ||
| 30 | change ZON file#} | ||
| 31 | #check stdout { | ||
| 32 | Hello again, World! | ||
| 33 | #} |
test/incremental/compile_error_then_log+24-8| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version with compile error | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() void {} | 6 | pub fn main() void {} |
| 4 | comptime { | 7 | comptime { |
| 5 | @compileError("this is an error"); | 8 | @compileError("this is an error"); |
| ... | @@ -7,11 +10,17 @@ comptime { | ... | @@ -7,11 +10,17 @@ comptime { |
| 7 | comptime { | 10 | comptime { |
| 8 | @compileLog("this is a log"); | 11 | @compileLog("this is a log"); |
| 9 | } | 12 | } |
| 10 | #expect_error=main.zig:3:5: error: this is an error | 13 | #} |
| 11 | #expect_compile_log=@as(*const [13:0]u8, "this is a log") | 14 | #update { |
| 15 | compile error#} | ||
| 16 | #check errors { | ||
| 17 | main.zig:3:5: error: this is an error | ||
| 18 | |||
| 19 | Compile Log Output: | ||
| 20 | @as(*const [13:0]u8, "this is a log") | ||
| 21 | #} | ||
| 12 | 22 | ||
| 13 | #update=remove the compile error | 23 | #write main.zig { |
| 14 | #file=main.zig | ||
| 15 | pub fn main() void {} | 24 | pub fn main() void {} |
| 16 | comptime { | 25 | comptime { |
| 17 | //@compileError("this is an error"); | 26 | //@compileError("this is an error"); |
| ... | @@ -19,5 +28,12 @@ comptime { | ... | @@ -19,5 +28,12 @@ comptime { |
| 19 | comptime { | 28 | comptime { |
| 20 | @compileLog("this is a log"); | 29 | @compileLog("this is a log"); |
| 21 | } | 30 | } |
| 22 | #expect_error=main.zig:6:5: error: found compile log statement | 31 | #} |
| 23 | #expect_compile_log=@as(*const [13:0]u8, "this is a log") | 32 | #update { |
| 33 | remove the compile error#} | ||
| 34 | #check errors { | ||
| 35 | main.zig:6:5: error: found compile log statement | ||
| 36 | |||
| 37 | Compile Log Output: | ||
| 38 | @as(*const [13:0]u8, "this is a log") | ||
| 39 | #} |
test/incremental/compile_log+28-10| ... | @@ -1,28 +1,46 @@ | ... | @@ -1,28 +1,46 @@ |
| 1 | #update=initial version with no compile log | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | pub fn main() !void { | 7 | pub fn main() !void { |
| 5 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); | 8 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); |
| 6 | } | 9 | } |
| 7 | const io = std.Io.Threaded.global_single_threaded.io(); | 10 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 8 | #expect_stdout="Hello, World!\n" | 11 | #} |
| 12 | #update { | ||
| 13 | no compile log#} | ||
| 14 | #check stdout { | ||
| 15 | Hello, World! | ||
| 16 | #} | ||
| 9 | 17 | ||
| 10 | #update=add compile log | 18 | #write main.zig { |
| 11 | #file=main.zig | ||
| 12 | const std = @import("std"); | 19 | const std = @import("std"); |
| 13 | pub fn main() !void { | 20 | pub fn main() !void { |
| 14 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); | 21 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); |
| 15 | @compileLog("this is a log"); | 22 | @compileLog("this is a log"); |
| 16 | } | 23 | } |
| 17 | const io = std.Io.Threaded.global_single_threaded.io(); | 24 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 18 | #expect_error=main.zig:4:5: error: found compile log statement | 25 | #} |
| 19 | #expect_compile_log=@as(*const [13:0]u8, "this is a log") | 26 | #update { |
| 27 | add compile log#} | ||
| 28 | #check errors { | ||
| 29 | main.zig:4:5: error: found compile log statement | ||
| 30 | |||
| 31 | Compile Log Output: | ||
| 32 | @as(*const [13:0]u8, "this is a log") | ||
| 33 | #} | ||
| 20 | 34 | ||
| 21 | #update=remove compile log | 35 | #write main.zig { |
| 22 | #file=main.zig | ||
| 23 | const std = @import("std"); | 36 | const std = @import("std"); |
| 24 | pub fn main() !void { | 37 | pub fn main() !void { |
| 25 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); | 38 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); |
| 26 | } | 39 | } |
| 27 | const io = std.Io.Threaded.global_single_threaded.io(); | 40 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 28 | #expect_stdout="Hello, World!\n" | 41 | #} |
| 42 | #update { | ||
| 43 | remove compile log#} | ||
| 44 | #check stdout { | ||
| 45 | Hello, World! | ||
| 46 | #} |
test/incremental/delete_comptime_decls+20-9| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() void {} | 6 | pub fn main() void {} |
| 4 | comptime { | 7 | comptime { |
| 5 | var array = [_:0]u8{ 1, 2, 3, 4 }; | 8 | var array = [_:0]u8{ 1, 2, 3, 4 }; |
| ... | @@ -22,13 +25,16 @@ comptime { | ... | @@ -22,13 +25,16 @@ comptime { |
| 22 | const slice = array[3..2]; | 25 | const slice = array[3..2]; |
| 23 | _ = slice; | 26 | _ = slice; |
| 24 | } | 27 | } |
| 25 | #expect_error=main.zig:5:32: error: end index 6 out of bounds for slice of length 4 +1 (sentinel) | 28 | #} |
| 26 | #expect_error=main.zig:10:28: error: end index 6 out of bounds for array of length 4 +1 (sentinel) | 29 | #update |
| 27 | #expect_error=main.zig:15:28: error: end index 5 out of bounds for array of length 4 | 30 | #check errors { |
| 28 | #expect_error=main.zig:20:25: error: start index 3 is larger than end index 2 | 31 | main.zig:5:32: error: end index 6 out of bounds for slice of length 4 +1 (sentinel) |
| 32 | main.zig:10:28: error: end index 6 out of bounds for array of length 4 +1 (sentinel) | ||
| 33 | main.zig:15:28: error: end index 5 out of bounds for array of length 4 | ||
| 34 | main.zig:20:25: error: start index 3 is larger than end index 2 | ||
| 35 | #} | ||
| 29 | 36 | ||
| 30 | #update=delete and modify comptime decls | 37 | #write main.zig { |
| 31 | #file=main.zig | ||
| 32 | pub fn main() void {} | 38 | pub fn main() void {} |
| 33 | comptime { | 39 | comptime { |
| 34 | const x: [*c]u8 = null; | 40 | const x: [*c]u8 = null; |
| ... | @@ -37,4 +43,9 @@ comptime { | ... | @@ -37,4 +43,9 @@ comptime { |
| 37 | const y = x[0..runtime_len]; | 43 | const y = x[0..runtime_len]; |
| 38 | _ = y; | 44 | _ = y; |
| 39 | } | 45 | } |
| 40 | #expect_error=main.zig:6:16: error: slice of null pointer | 46 | #} |
| 47 | #update { | ||
| 48 | delete and modify comptime decls#} | ||
| 49 | #check errors { | ||
| 50 | main.zig:6:16: error: slice of null pointer | ||
| 51 | #} |
test/incremental/dependency_on_type_of_inferred_global+17-6| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const foo = @as(u8, 123); | 6 | const foo = @as(u8, 123); |
| 4 | comptime { | 7 | comptime { |
| 5 | // depends on value of `foo` | 8 | // depends on value of `foo` |
| ... | @@ -10,9 +13,12 @@ comptime { | ... | @@ -10,9 +13,12 @@ comptime { |
| 10 | if (@TypeOf(&foo) != *const u8) unreachable; | 13 | if (@TypeOf(&foo) != *const u8) unreachable; |
| 11 | } | 14 | } |
| 12 | pub fn main() void {} | 15 | pub fn main() void {} |
| 13 | #expect_stdout="" | 16 | #} |
| 14 | #update=change the type | 17 | #update |
| 15 | #file=main.zig | 18 | #check stdout { |
| 19 | #} | ||
| 20 | |||
| 21 | #write main.zig { | ||
| 16 | const foo = @as(u16, 123); | 22 | const foo = @as(u16, 123); |
| 17 | comptime { | 23 | comptime { |
| 18 | // depends on value of `foo` | 24 | // depends on value of `foo` |
| ... | @@ -23,4 +29,9 @@ comptime { | ... | @@ -23,4 +29,9 @@ comptime { |
| 23 | if (@TypeOf(&foo) != *const u8) unreachable; | 29 | if (@TypeOf(&foo) != *const u8) unreachable; |
| 24 | } | 30 | } |
| 25 | pub fn main() void {} | 31 | pub fn main() void {} |
| 26 | #expect_error=main.zig:8:37: error: reached unreachable code | 32 | #} |
| 33 | #update { | ||
| 34 | change the type#} | ||
| 35 | #check errors { | ||
| 36 | main.zig:8:37: error: reached unreachable code | ||
| 37 | #} |
test/incremental/do_nothing+19-7| ... | @@ -1,6 +1,11 @@ | ... | @@ -1,6 +1,11 @@ |
| 1 | // TODO: it'd be great if we could actually check that no analysis happened! | 1 | #todo { |
| 2 | #update=initial version | 2 | it'd be great if we could actually check that no analysis happened! |
| 3 | #file=main.zig | 3 | #} |
| 4 | #exe { | ||
| 5 | -Mroot=main.zig | ||
| 6 | #} | ||
| 7 | |||
| 8 | #write main.zig { | ||
| 4 | pub fn main() void { | 9 | pub fn main() void { |
| 5 | const ptr: *const O = @ptrFromInt(0x1000); | 10 | const ptr: *const O = @ptrFromInt(0x1000); |
| 6 | _ = ptr; | 11 | _ = ptr; |
| ... | @@ -18,9 +23,12 @@ const O = opaque { | ... | @@ -18,9 +23,12 @@ const O = opaque { |
| 18 | _ = wrapper; | 23 | _ = wrapper; |
| 19 | } | 24 | } |
| 20 | }; | 25 | }; |
| 21 | #expect_stdout="" | 26 | #} |
| 22 | #update=do literally nothing | 27 | #update |
| 23 | #file=main.zig | 28 | #check stdout { |
| 29 | #} | ||
| 30 | |||
| 31 | #write main.zig { | ||
| 24 | pub fn main() void { | 32 | pub fn main() void { |
| 25 | const ptr: *const O = @ptrFromInt(0x1000); | 33 | const ptr: *const O = @ptrFromInt(0x1000); |
| 26 | _ = ptr; | 34 | _ = ptr; |
| ... | @@ -38,4 +46,8 @@ const O = opaque { | ... | @@ -38,4 +46,8 @@ const O = opaque { |
| 38 | _ = wrapper; | 46 | _ = wrapper; |
| 39 | } | 47 | } |
| 40 | }; | 48 | }; |
| 41 | #expect_stdout="" | 49 | #} |
| 50 | #update { | ||
| 51 | do literally nothing#} | ||
| 52 | #check stdout { | ||
| 53 | #} |
test/incremental/fix_astgen_failure+37-13| ... | @@ -1,35 +1,59 @@ | ... | @@ -1,35 +1,59 @@ |
| 1 | #update=initial version with error | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() !void { | 6 | pub fn main() !void { |
| 4 | try @import("foo.zig").hello(); | 7 | try @import("foo.zig").hello(); |
| 5 | } | 8 | } |
| 6 | #file=foo.zig | 9 | #} |
| 10 | #write foo.zig { | ||
| 7 | pub fn hello() !void { | 11 | pub fn hello() !void { |
| 8 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); | 12 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); |
| 9 | } | 13 | } |
| 10 | #expect_error=foo.zig:2:9: error: use of undeclared identifier 'std' | 14 | #} |
| 11 | #update=fix the error | 15 | #update { |
| 12 | #file=foo.zig | 16 | error#} |
| 17 | #check errors { | ||
| 18 | foo.zig:2:9: error: use of undeclared identifier 'std' | ||
| 19 | #} | ||
| 20 | |||
| 21 | #write foo.zig { | ||
| 13 | const std = @import("std"); | 22 | const std = @import("std"); |
| 14 | pub fn hello() !void { | 23 | pub fn hello() !void { |
| 15 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); | 24 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); |
| 16 | } | 25 | } |
| 17 | const io = std.Io.Threaded.global_single_threaded.io(); | 26 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 18 | #expect_stdout="Hello, World!\n" | 27 | #} |
| 19 | #update=add new error | 28 | #update { |
| 20 | #file=foo.zig | 29 | fix the error#} |
| 30 | #check stdout { | ||
| 31 | Hello, World! | ||
| 32 | #} | ||
| 33 | |||
| 34 | #write foo.zig { | ||
| 21 | const std = @import("std"); | 35 | const std = @import("std"); |
| 22 | pub fn hello() !void { | 36 | pub fn hello() !void { |
| 23 | try std.Io.File.stdout().writeStreamingAll(io, hello_str); | 37 | try std.Io.File.stdout().writeStreamingAll(io, hello_str); |
| 24 | } | 38 | } |
| 25 | const io = std.Io.Threaded.global_single_threaded.io(); | 39 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 26 | #expect_error=foo.zig:3:52: error: use of undeclared identifier 'hello_str' | 40 | #} |
| 27 | #update=fix the new error | 41 | #update { |
| 28 | #file=foo.zig | 42 | add new error#} |
| 43 | #check errors { | ||
| 44 | foo.zig:3:52: error: use of undeclared identifier 'hello_str' | ||
| 45 | #} | ||
| 46 | |||
| 47 | #write foo.zig { | ||
| 29 | const std = @import("std"); | 48 | const std = @import("std"); |
| 30 | const hello_str = "Hello, World! Again!\n"; | 49 | const hello_str = "Hello, World! Again!\n"; |
| 31 | pub fn hello() !void { | 50 | pub fn hello() !void { |
| 32 | try std.Io.File.stdout().writeStreamingAll(io, hello_str); | 51 | try std.Io.File.stdout().writeStreamingAll(io, hello_str); |
| 33 | } | 52 | } |
| 34 | const io = std.Io.Threaded.global_single_threaded.io(); | 53 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 35 | #expect_stdout="Hello, World! Again!\n" | 54 | #} |
| 55 | #update { | ||
| 56 | fix the new error#} | ||
| 57 | #check stdout { | ||
| 58 | Hello, World! Again! | ||
| 59 | #} |
test/incremental/function_becomes_inline+25-9| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=non-inline version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() !void { | 6 | pub fn main() !void { |
| 4 | try foo(); | 7 | try foo(); |
| 5 | } | 8 | } |
| ... | @@ -8,10 +11,14 @@ fn foo() !void { | ... | @@ -8,10 +11,14 @@ fn foo() !void { |
| 8 | } | 11 | } |
| 9 | const std = @import("std"); | 12 | const std = @import("std"); |
| 10 | const io = std.Io.Threaded.global_single_threaded.io(); | 13 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 11 | #expect_stdout="Hello, World!\n" | 14 | #} |
| 15 | #update { | ||
| 16 | non-inline version#} | ||
| 17 | #check stdout { | ||
| 18 | Hello, World! | ||
| 19 | #} | ||
| 12 | 20 | ||
| 13 | #update=make function inline | 21 | #write main.zig { |
| 14 | #file=main.zig | ||
| 15 | pub fn main() !void { | 22 | pub fn main() !void { |
| 16 | try foo(); | 23 | try foo(); |
| 17 | } | 24 | } |
| ... | @@ -20,10 +27,14 @@ inline fn foo() !void { | ... | @@ -20,10 +27,14 @@ inline fn foo() !void { |
| 20 | } | 27 | } |
| 21 | const std = @import("std"); | 28 | const std = @import("std"); |
| 22 | const io = std.Io.Threaded.global_single_threaded.io(); | 29 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 23 | #expect_stdout="Hello, World!\n" | 30 | #} |
| 31 | #update { | ||
| 32 | make function inline#} | ||
| 33 | #check stdout { | ||
| 34 | Hello, World! | ||
| 35 | #} | ||
| 24 | 36 | ||
| 25 | #update=change string | 37 | #write main.zig { |
| 26 | #file=main.zig | ||
| 27 | pub fn main() !void { | 38 | pub fn main() !void { |
| 28 | try foo(); | 39 | try foo(); |
| 29 | } | 40 | } |
| ... | @@ -32,4 +43,9 @@ inline fn foo() !void { | ... | @@ -32,4 +43,9 @@ inline fn foo() !void { |
| 32 | } | 43 | } |
| 33 | const std = @import("std"); | 44 | const std = @import("std"); |
| 34 | const io = std.Io.Threaded.global_single_threaded.io(); | 45 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 35 | #expect_stdout="Hello, `inline` World!\n" | 46 | #} |
| 47 | #update { | ||
| 48 | change string#} | ||
| 49 | #check stdout { | ||
| 50 | Hello, `inline` World! | ||
| 51 | #} |
test/incremental/hello+18-6| ... | @@ -1,16 +1,28 @@ | ... | @@ -1,16 +1,28 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | const io = std.Io.Threaded.global_single_threaded.io(); | 7 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 5 | pub fn main() !void { | 8 | pub fn main() !void { |
| 6 | try std.Io.File.stdout().writeStreamingAll(io, "good morning\n"); | 9 | try std.Io.File.stdout().writeStreamingAll(io, "good morning\n"); |
| 7 | } | 10 | } |
| 8 | #expect_stdout="good morning\n" | 11 | #} |
| 9 | #update=change the string | 12 | #update |
| 10 | #file=main.zig | 13 | #check stdout { |
| 14 | good morning | ||
| 15 | #} | ||
| 16 | |||
| 17 | #write main.zig { | ||
| 11 | const std = @import("std"); | 18 | const std = @import("std"); |
| 12 | const io = std.Io.Threaded.global_single_threaded.io(); | 19 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 13 | pub fn main() !void { | 20 | pub fn main() !void { |
| 14 | try std.Io.File.stdout().writeStreamingAll(io, "おはようございます\n"); | 21 | try std.Io.File.stdout().writeStreamingAll(io, "おはようございます\n"); |
| 15 | } | 22 | } |
| 16 | #expect_stdout="おはようございます\n" | 23 | #} |
| 24 | #update { | ||
| 25 | change the string#} | ||
| 26 | #check stdout { | ||
| 27 | おはようございます | ||
| 28 | #} |
test/incremental/increase_array_field_length+17-6| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const Big = struct { arr: [10]u8 }; | 6 | const Big = struct { arr: [10]u8 }; |
| 4 | pub fn main(init: std.process.Init) !void { | 7 | pub fn main(init: std.process.Init) !void { |
| 5 | var big: Big = undefined; | 8 | var big: Big = undefined; |
| ... | @@ -12,10 +15,13 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -12,10 +15,13 @@ pub fn main(init: std.process.Init) !void { |
| 12 | }; | 15 | }; |
| 13 | } | 16 | } |
| 14 | const std = @import("std"); | 17 | const std = @import("std"); |
| 15 | #expect_stdout="arr[9] = 234\n" | 18 | #} |
| 19 | #update | ||
| 20 | #check stdout { | ||
| 21 | arr[9] = 234 | ||
| 22 | #} | ||
| 16 | 23 | ||
| 17 | #update=increase array length | 24 | #write main.zig { |
| 18 | #file=main.zig | ||
| 19 | const Big = struct { arr: [1000]u8 }; | 25 | const Big = struct { arr: [1000]u8 }; |
| 20 | pub fn main(init: std.process.Init) !void { | 26 | pub fn main(init: std.process.Init) !void { |
| 21 | var big: Big = undefined; | 27 | var big: Big = undefined; |
| ... | @@ -28,4 +34,9 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -28,4 +34,9 @@ pub fn main(init: std.process.Init) !void { |
| 28 | }; | 34 | }; |
| 29 | } | 35 | } |
| 30 | const std = @import("std"); | 36 | const std = @import("std"); |
| 31 | #expect_stdout="arr[999] = 234\n" | 37 | #} |
| 38 | #update { | ||
| 39 | increase array length#} | ||
| 40 | #check stdout { | ||
| 41 | arr[999] = 234 | ||
| 42 | #} |
test/incremental/make_decl_pub+20-8| ... | @@ -1,23 +1,35 @@ | ... | @@ -1,23 +1,35 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const foo = @import("foo.zig"); | 6 | const foo = @import("foo.zig"); |
| 4 | pub fn main() !void { | 7 | pub fn main() !void { |
| 5 | try foo.hello(); | 8 | try foo.hello(); |
| 6 | } | 9 | } |
| 7 | #file=foo.zig | 10 | #} |
| 11 | #write foo.zig { | ||
| 8 | const std = @import("std"); | 12 | const std = @import("std"); |
| 9 | fn hello() !void { | 13 | fn hello() !void { |
| 10 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); | 14 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); |
| 11 | } | 15 | } |
| 12 | const io = std.Io.Threaded.global_single_threaded.io(); | 16 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 13 | #expect_error=main.zig:3:12: error: 'hello' is not marked 'pub' | 17 | #} |
| 14 | #expect_error=foo.zig:2:1: note: declared here | 18 | #update |
| 19 | #check errors { | ||
| 20 | main.zig:3:12: error: 'hello' is not marked 'pub' | ||
| 21 | foo.zig:2:1: note: declared here | ||
| 22 | #} | ||
| 15 | 23 | ||
| 16 | #update=make hello pub | 24 | #write foo.zig { |
| 17 | #file=foo.zig | ||
| 18 | const std = @import("std"); | 25 | const std = @import("std"); |
| 19 | pub fn hello() !void { | 26 | pub fn hello() !void { |
| 20 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); | 27 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); |
| 21 | } | 28 | } |
| 22 | const io = std.Io.Threaded.global_single_threaded.io(); | 29 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 23 | #expect_stdout="Hello, World!\n" | 30 | #} |
| 31 | #update { | ||
| 32 | make hello pub#} | ||
| 33 | #check stdout { | ||
| 34 | Hello, World! | ||
| 35 | #} |
test/incremental/modify_inline_fn+18-6| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | pub fn main() !void { | 7 | pub fn main() !void { |
| 5 | const str = getStr(); | 8 | const str = getStr(); |
| ... | @@ -9,9 +12,13 @@ inline fn getStr() []const u8 { | ... | @@ -9,9 +12,13 @@ inline fn getStr() []const u8 { |
| 9 | return "foo\n"; | 12 | return "foo\n"; |
| 10 | } | 13 | } |
| 11 | const io = std.Io.Threaded.global_single_threaded.io(); | 14 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 12 | #expect_stdout="foo\n" | 15 | #} |
| 13 | #update=change the string | 16 | #update |
| 14 | #file=main.zig | 17 | #check stdout { |
| 18 | foo | ||
| 19 | #} | ||
| 20 | |||
| 21 | #write main.zig { | ||
| 15 | const std = @import("std"); | 22 | const std = @import("std"); |
| 16 | pub fn main() !void { | 23 | pub fn main() !void { |
| 17 | const str = getStr(); | 24 | const str = getStr(); |
| ... | @@ -21,4 +28,9 @@ inline fn getStr() []const u8 { | ... | @@ -21,4 +28,9 @@ inline fn getStr() []const u8 { |
| 21 | return "bar\n"; | 28 | return "bar\n"; |
| 22 | } | 29 | } |
| 23 | const io = std.Io.Threaded.global_single_threaded.io(); | 30 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 24 | #expect_stdout="bar\n" | 31 | #} |
| 32 | #update { | ||
| 33 | change the string#} | ||
| 34 | #check stdout { | ||
| 35 | bar | ||
| 36 | #} |
test/incremental/move_src+17-6| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | pub fn main() !void { | 7 | pub fn main() !void { |
| 5 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | 8 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); |
| ... | @@ -12,10 +15,13 @@ fn bar() u32 { | ... | @@ -12,10 +15,13 @@ fn bar() u32 { |
| 12 | return 123; | 15 | return 123; |
| 13 | } | 16 | } |
| 14 | const io = std.Io.Threaded.global_single_threaded.io(); | 17 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 15 | #expect_stdout="7 123\n" | 18 | #} |
| 19 | #update | ||
| 20 | #check stdout { | ||
| 21 | 7 123 | ||
| 22 | #} | ||
| 16 | 23 | ||
| 17 | #update=add newline | 24 | #write main.zig { |
| 18 | #file=main.zig | ||
| 19 | const std = @import("std"); | 25 | const std = @import("std"); |
| 20 | pub fn main() !void { | 26 | pub fn main() !void { |
| 21 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | 27 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); |
| ... | @@ -29,4 +35,9 @@ fn bar() u32 { | ... | @@ -29,4 +35,9 @@ fn bar() u32 { |
| 29 | return 123; | 35 | return 123; |
| 30 | } | 36 | } |
| 31 | const io = std.Io.Threaded.global_single_threaded.io(); | 37 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 32 | #expect_stdout="8 123\n" | 38 | #} |
| 39 | #update { | ||
| 40 | add newline#} | ||
| 41 | #check stdout { | ||
| 42 | 8 123 | ||
| 43 | #} |
test/incremental/pointer_to_decl_with_astgen_error+22-9| ... | @@ -1,23 +1,36 @@ | ... | @@ -1,23 +1,36 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const foo = {}; | 6 | const foo = {}; |
| 4 | pub fn main() void { | 7 | pub fn main() void { |
| 5 | _ = &foo; | 8 | _ = &foo; |
| 6 | } | 9 | } |
| 7 | #expect_stdout="" | 10 | #} |
| 11 | #update | ||
| 12 | #check stdout { | ||
| 13 | #} | ||
| 8 | 14 | ||
| 9 | #update=introduce use of undeclared identifier | 15 | #write main.zig { |
| 10 | #file=main.zig | ||
| 11 | const foo = something; | 16 | const foo = something; |
| 12 | pub fn main() void { | 17 | pub fn main() void { |
| 13 | _ = &foo; | 18 | _ = &foo; |
| 14 | } | 19 | } |
| 15 | #expect_error=main.zig:1:13: error: use of undeclared identifier 'something' | 20 | #} |
| 21 | #update { | ||
| 22 | introduce use of undeclared identifier#} | ||
| 23 | #check errors { | ||
| 24 | main.zig:1:13: error: use of undeclared identifier 'something' | ||
| 25 | #} | ||
| 16 | 26 | ||
| 17 | #update=revert use of undeclared identifier | 27 | #write main.zig { |
| 18 | #file=main.zig | ||
| 19 | const foo = {}; | 28 | const foo = {}; |
| 20 | pub fn main() void { | 29 | pub fn main() void { |
| 21 | _ = &foo; | 30 | _ = &foo; |
| 22 | } | 31 | } |
| 23 | #expect_stdout="" | 32 | #} |
| 33 | #update { | ||
| 34 | revert use of undeclared identifier#} | ||
| 35 | #check stdout { | ||
| 36 | #} |
test/incremental/recursive_function_becomes_non_recursive+17-6| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub fn main() !void { | 6 | pub fn main() !void { |
| 4 | try foo(false); | 7 | try foo(false); |
| 5 | } | 8 | } |
| ... | @@ -10,10 +13,13 @@ fn foo(recurse: bool) !void { | ... | @@ -10,10 +13,13 @@ fn foo(recurse: bool) !void { |
| 10 | } | 13 | } |
| 11 | const std = @import("std"); | 14 | const std = @import("std"); |
| 12 | const io = std.Io.Threaded.global_single_threaded.io(); | 15 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 13 | #expect_stdout="non-recursive path\n" | 16 | #} |
| 17 | #update | ||
| 18 | #check stdout { | ||
| 19 | non-recursive path | ||
| 20 | #} | ||
| 14 | 21 | ||
| 15 | #update=eliminate recursion and change argument | 22 | #write main.zig { |
| 16 | #file=main.zig | ||
| 17 | pub fn main() !void { | 23 | pub fn main() !void { |
| 18 | try foo(true); | 24 | try foo(true); |
| 19 | } | 25 | } |
| ... | @@ -24,4 +30,9 @@ fn foo(recurse: bool) !void { | ... | @@ -24,4 +30,9 @@ fn foo(recurse: bool) !void { |
| 24 | } | 30 | } |
| 25 | const std = @import("std"); | 31 | const std = @import("std"); |
| 26 | const io = std.Io.Threaded.global_single_threaded.io(); | 32 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 27 | #expect_stdout="x==1\n" | 33 | #} |
| 34 | #update { | ||
| 35 | eliminate recursion and change argument#} | ||
| 36 | #check stdout { | ||
| 37 | x==1 | ||
| 38 | #} |
test/incremental/remove_enum_field+21-9| ... | @@ -1,27 +1,39 @@ | ... | @@ -1,27 +1,39 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const MyEnum = enum(u8) { | 6 | const MyEnum = enum(u8) { |
| 4 | foo = 1, | 7 | foo = 1, |
| 5 | bar = 2, | 8 | bar = 2, |
| 6 | }; | 9 | }; |
| 7 | pub fn main() !void { | 10 | pub fn main() !void { |
| 8 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | 11 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); |
| 9 | try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)}); | 12 | try stdout_writer.interface.print("{}\n", .{@backingInt(MyEnum.foo)}); |
| 10 | } | 13 | } |
| 11 | const std = @import("std"); | 14 | const std = @import("std"); |
| 12 | const io = std.Io.Threaded.global_single_threaded.io(); | 15 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 13 | #expect_stdout="1\n" | 16 | #} |
| 14 | #update=remove enum field | 17 | #update |
| 15 | #file=main.zig | 18 | #check stdout { |
| 19 | 1 | ||
| 20 | #} | ||
| 21 | |||
| 22 | #write main.zig { | ||
| 16 | const MyEnum = enum(u8) { | 23 | const MyEnum = enum(u8) { |
| 17 | //foo = 1, | 24 | //foo = 1, |
| 18 | bar = 2, | 25 | bar = 2, |
| 19 | }; | 26 | }; |
| 20 | pub fn main() !void { | 27 | pub fn main() !void { |
| 21 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | 28 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); |
| 22 | try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)}); | 29 | try stdout_writer.interface.print("{}\n", .{@backingInt(MyEnum.foo)}); |
| 23 | } | 30 | } |
| 24 | const std = @import("std"); | 31 | const std = @import("std"); |
| 25 | const io = std.Io.Threaded.global_single_threaded.io(); | 32 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 26 | #expect_error=main.zig:7:69: error: enum 'main.MyEnum' has no member named 'foo' | 33 | #} |
| 27 | #expect_error=main.zig:1:16: note: enum declared here | 34 | #update { |
| 35 | remove enum field#} | ||
| 36 | #check errors { | ||
| 37 | main.zig:7:68: error: enum 'main.MyEnum' has no member named 'foo' | ||
| 38 | main.zig:1:16: note: enum declared here | ||
| 39 | #} |
test/incremental/remove_function_with_changed_callee+16-6| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | const io = std.Io.Threaded.global_single_threaded.io(); | 7 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 5 | 8 | ||
| ... | @@ -23,9 +26,12 @@ fn bar(a: A) !void { | ... | @@ -23,9 +26,12 @@ fn bar(a: A) !void { |
| 23 | pub fn main() !void { | 26 | pub fn main() !void { |
| 24 | try foo(); | 27 | try foo(); |
| 25 | } | 28 | } |
| 26 | #expect_stdout="0" | 29 | #} |
| 27 | #update=remove foo and change bar wasm function type | 30 | #update |
| 28 | #file=main.zig | 31 | #check stdout { |
| 32 | 0#} | ||
| 33 | |||
| 34 | #write main.zig { | ||
| 29 | //! This update must preserve `bar` `InternPool.Index` value, while changing its wasm function type. | 35 | //! This update must preserve `bar` `InternPool.Index` value, while changing its wasm function type. |
| 30 | //! If `foo` code is preserved in the binary without any changes, wasm type checking will fail. | 36 | //! If `foo` code is preserved in the binary without any changes, wasm type checking will fail. |
| 31 | const std = @import("std"); | 37 | const std = @import("std"); |
| ... | @@ -47,4 +53,8 @@ fn bar(a: A) !void { | ... | @@ -47,4 +53,8 @@ fn bar(a: A) !void { |
| 47 | pub fn main() !void { | 53 | pub fn main() !void { |
| 48 | try bar(.one); | 54 | try bar(.one); |
| 49 | } | 55 | } |
| 50 | #expect_stdout="1" | 56 | #} |
| 57 | #update { | ||
| 58 | remove foo and change bar wasm function type#} | ||
| 59 | #check stdout { | ||
| 60 | 1#} |
test/incremental/remove_invalid_union_backing_enum+18-7| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const E = enum { a, b, c }; | 6 | const E = enum { a, b, c }; |
| 4 | const U = union(E) { | 7 | const U = union(E) { |
| 5 | a: i32, | 8 | a: i32, |
| ... | @@ -11,10 +14,14 @@ pub fn main() void { | ... | @@ -11,10 +14,14 @@ pub fn main() void { |
| 11 | const u: U = .{ .a = 123 }; | 14 | const u: U = .{ .a = 123 }; |
| 12 | _ = u; | 15 | _ = u; |
| 13 | } | 16 | } |
| 14 | #expect_error=main.zig:6:5: error: no field named 'd' in enum 'main.E' | 17 | #} |
| 15 | #expect_error=main.zig:1:11: note: enum declared here | 18 | #update |
| 16 | #update=remove invalid backing enum | 19 | #check errors { |
| 17 | #file=main.zig | 20 | main.zig:6:5: error: no field named 'd' in enum 'main.E' |
| 21 | main.zig:1:11: note: enum declared here | ||
| 22 | #} | ||
| 23 | |||
| 24 | #write main.zig { | ||
| 18 | const U = union { | 25 | const U = union { |
| 19 | a: i32, | 26 | a: i32, |
| 20 | b: f64, | 27 | b: f64, |
| ... | @@ -25,4 +32,8 @@ pub fn main() void { | ... | @@ -25,4 +32,8 @@ pub fn main() void { |
| 25 | const u: U = .{ .a = 123 }; | 32 | const u: U = .{ .a = 123 }; |
| 26 | _ = u; | 33 | _ = u; |
| 27 | } | 34 | } |
| 28 | #expect_stdout="" | 35 | #} |
| 36 | #update { | ||
| 37 | remove invalid backing enum#} | ||
| 38 | #check stdout { | ||
| 39 | #} |
test/incremental/struct_reification_becomes_anon_init+17-6| ... | @@ -1,19 +1,30 @@ | ... | @@ -1,19 +1,30 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const Foo = @Struct(.auto, null, &.{"reified_field"}, &.{void}, &.{.{}}); | 6 | const Foo = @Struct(.auto, null, &.{"reified_field"}, &.{void}, &.{.{}}); |
| 4 | pub fn main(init: std.process.Init) !void { | 7 | pub fn main(init: std.process.Init) !void { |
| 5 | const field_name = @typeInfo(Foo).@"struct".field_names[0]; | 8 | const field_name = @typeInfo(Foo).@"struct".field_names[0]; |
| 6 | try std.Io.File.stdout().writeStreamingAll(init.io, field_name ++ "\n"); | 9 | try std.Io.File.stdout().writeStreamingAll(init.io, field_name ++ "\n"); |
| 7 | } | 10 | } |
| 8 | const std = @import("std"); | 11 | const std = @import("std"); |
| 9 | #expect_stdout="reified_field\n" | 12 | #} |
| 13 | #update | ||
| 14 | #check stdout { | ||
| 15 | reified_field | ||
| 16 | #} | ||
| 10 | 17 | ||
| 11 | #update=change to `@TypeOf` anonymous init | 18 | #write main.zig { |
| 12 | #file=main.zig | ||
| 13 | const Foo = @TypeOf(.{ .anon_field = 123 }); | 19 | const Foo = @TypeOf(.{ .anon_field = 123 }); |
| 14 | pub fn main(init: std.process.Init) !void { | 20 | pub fn main(init: std.process.Init) !void { |
| 15 | const field_name = @typeInfo(Foo).@"struct".field_names[0]; | 21 | const field_name = @typeInfo(Foo).@"struct".field_names[0]; |
| 16 | try std.Io.File.stdout().writeStreamingAll(init.io, field_name ++ "\n"); | 22 | try std.Io.File.stdout().writeStreamingAll(init.io, field_name ++ "\n"); |
| 17 | } | 23 | } |
| 18 | const std = @import("std"); | 24 | const std = @import("std"); |
| 19 | #expect_stdout="anon_field\n" | 25 | #} |
| 26 | #update { | ||
| 27 | change to `@TypeOf` anonymous init#} | ||
| 28 | #check stdout { | ||
| 29 | anon_field | ||
| 30 | #} |
test/incremental/tag_name+23-9| ... | @@ -1,27 +1,41 @@ | ... | @@ -1,27 +1,41 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | var some_enum: enum { first, second } = .first; | 7 | var some_enum: enum { first, second } = .first; |
| 5 | const io = std.Io.Threaded.global_single_threaded.io(); | 8 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 6 | pub fn main() !void { | 9 | pub fn main() !void { |
| 7 | try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); | 10 | try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); |
| 8 | } | 11 | } |
| 9 | #expect_stdout="first" | 12 | #} |
| 10 | #update=no change | 13 | #update |
| 11 | #file=main.zig | 14 | #check stdout { |
| 15 | first#} | ||
| 16 | |||
| 17 | #write main.zig { | ||
| 12 | const std = @import("std"); | 18 | const std = @import("std"); |
| 13 | var some_enum: enum { first, second } = .first; | 19 | var some_enum: enum { first, second } = .first; |
| 14 | const io = std.Io.Threaded.global_single_threaded.io(); | 20 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 15 | pub fn main() !void { | 21 | pub fn main() !void { |
| 16 | try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); | 22 | try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); |
| 17 | } | 23 | } |
| 18 | #expect_stdout="first" | 24 | #} |
| 19 | #update=swap fields | 25 | #update { |
| 20 | #file=main.zig | 26 | no change#} |
| 27 | #check stdout { | ||
| 28 | first#} | ||
| 29 | |||
| 30 | #write main.zig { | ||
| 21 | const std = @import("std"); | 31 | const std = @import("std"); |
| 22 | var some_enum: enum { second, first } = .first; | 32 | var some_enum: enum { second, first } = .first; |
| 23 | const io = std.Io.Threaded.global_single_threaded.io(); | 33 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 24 | pub fn main() !void { | 34 | pub fn main() !void { |
| 25 | try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); | 35 | try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); |
| 26 | } | 36 | } |
| 27 | #expect_stdout="first" | 37 | #} |
| 38 | #update { | ||
| 39 | swap fields#} | ||
| 40 | #check stdout { | ||
| 41 | first#} |
test/incremental/temporary_analysis_error_in_generic_signature+25-10| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | //! The original repro here depends on re-analysis order, which depends on | 6 | //! The original repro here depends on re-analysis order, which depends on |
| 4 | //! declaration order, so this exact declaration order must be used. | 7 | //! declaration order, so this exact declaration order must be used. |
| 5 | const Foo = struct { x: u8 }; | 8 | const Foo = struct { x: u8 }; |
| ... | @@ -11,10 +14,13 @@ fn bar(comptime x: u8) @This().Foo { | ... | @@ -11,10 +14,13 @@ fn bar(comptime x: u8) @This().Foo { |
| 11 | return .{ .x = x }; | 14 | return .{ .x = x }; |
| 12 | } | 15 | } |
| 13 | const std = @import("std"); | 16 | const std = @import("std"); |
| 14 | #expect_stdout="Z\n" | 17 | #} |
| 18 | #update | ||
| 19 | #check stdout { | ||
| 20 | Z | ||
| 21 | #} | ||
| 15 | 22 | ||
| 16 | #update=change generic signature to use non-existent member | 23 | #write main.zig { |
| 17 | #file=main.zig | ||
| 18 | //! The original repro here depends on re-analysis order, which depends on | 24 | //! The original repro here depends on re-analysis order, which depends on |
| 19 | //! declaration order, so this exact declaration order must be used. | 25 | //! declaration order, so this exact declaration order must be used. |
| 20 | const Foo = struct { x: u8 }; | 26 | const Foo = struct { x: u8 }; |
| ... | @@ -26,11 +32,15 @@ fn bar(comptime x: u8) @This().FooAlias { | ... | @@ -26,11 +32,15 @@ fn bar(comptime x: u8) @This().FooAlias { |
| 26 | return .{ .x = x }; | 32 | return .{ .x = x }; |
| 27 | } | 33 | } |
| 28 | const std = @import("std"); | 34 | const std = @import("std"); |
| 29 | #expect_error=main.zig:8:31: error: root source file struct 'main' has no member named 'FooAlias' | 35 | #} |
| 30 | #expect_error=main.zig:1:1: note: struct declared here | 36 | #update { |
| 37 | change generic signature to use non-existent member#} | ||
| 38 | #check errors { | ||
| 39 | main.zig:8:31: error: root source file struct 'main' has no member named 'FooAlias' | ||
| 40 | main.zig:1:1: note: struct declared here | ||
| 41 | #} | ||
| 31 | 42 | ||
| 32 | #update=add that member, fixing the error | 43 | #write main.zig { |
| 33 | #file=main.zig | ||
| 34 | //! The original repro here depends on re-analysis order, which depends on | 44 | //! The original repro here depends on re-analysis order, which depends on |
| 35 | //! declaration order, so this exact declaration order must be used. | 45 | //! declaration order, so this exact declaration order must be used. |
| 36 | const Foo = struct { x: u8 }; | 46 | const Foo = struct { x: u8 }; |
| ... | @@ -43,4 +53,9 @@ fn bar(comptime x: u8) @This().FooAlias { | ... | @@ -43,4 +53,9 @@ fn bar(comptime x: u8) @This().FooAlias { |
| 43 | return .{ .x = x }; | 53 | return .{ .x = x }; |
| 44 | } | 54 | } |
| 45 | const std = @import("std"); | 55 | const std = @import("std"); |
| 46 | #expect_stdout="Z\n" | 56 | #} |
| 57 | #update { | ||
| 58 | add that member, fixing the error#} | ||
| 59 | #check stdout { | ||
| 60 | Z | ||
| 61 | #} |
test/incremental/temporary_parse_error+22-9| ... | @@ -1,15 +1,28 @@ | ... | @@ -1,15 +1,28 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | pub fn main() !void {} | 7 | pub fn main() !void {} |
| 5 | #expect_stdout="" | 8 | #} |
| 9 | #update | ||
| 10 | #check stdout { | ||
| 11 | #} | ||
| 6 | 12 | ||
| 7 | #update=introduce parse error | 13 | #write main.zig { |
| 8 | #file=main.zig | ||
| 9 | pub fn main() !void { | 14 | pub fn main() !void { |
| 10 | #expect_error=main.zig:2:1: error: expected statement, found 'EOF' | 15 | #} |
| 16 | #update { | ||
| 17 | introduce parse error#} | ||
| 18 | #check errors { | ||
| 19 | main.zig:2:1: error: expected statement, found 'EOF' | ||
| 20 | #} | ||
| 11 | 21 | ||
| 12 | #update=fix parse error | 22 | #write main.zig { |
| 13 | #file=main.zig | ||
| 14 | pub fn main() !void {} | 23 | pub fn main() !void {} |
| 15 | #expect_stdout="" | 24 | #} |
| 25 | #update { | ||
| 26 | fix parse error#} | ||
| 27 | #check stdout { | ||
| 28 | #} |
test/incremental/type_becomes_comptime_only+21-9| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const SomeType = u32; | 6 | const SomeType = u32; |
| 4 | const S = struct { | 7 | const S = struct { |
| 5 | x: SomeType, | 8 | x: SomeType, |
| ... | @@ -9,10 +12,12 @@ pub fn main() void { | ... | @@ -9,10 +12,12 @@ pub fn main() void { |
| 9 | const s: S = .{ .x = 456 }; | 12 | const s: S = .{ .x = 456 }; |
| 10 | s.foo(); | 13 | s.foo(); |
| 11 | } | 14 | } |
| 12 | #expect_stdout="" | 15 | #} |
| 16 | #update | ||
| 17 | #check stdout { | ||
| 18 | #} | ||
| 13 | 19 | ||
| 14 | #update=make S comptime-only | 20 | #write main.zig { |
| 15 | #file=main.zig | ||
| 16 | const SomeType = comptime_int; | 21 | const SomeType = comptime_int; |
| 17 | const S = struct { | 22 | const S = struct { |
| 18 | x: SomeType, | 23 | x: SomeType, |
| ... | @@ -22,10 +27,13 @@ pub fn main() void { | ... | @@ -22,10 +27,13 @@ pub fn main() void { |
| 22 | const s: S = .{ .x = 456 }; | 27 | const s: S = .{ .x = 456 }; |
| 23 | s.foo(); | 28 | s.foo(); |
| 24 | } | 29 | } |
| 25 | #expect_stdout="" | 30 | #} |
| 31 | #update { | ||
| 32 | make S comptime-only#} | ||
| 33 | #check stdout { | ||
| 34 | #} | ||
| 26 | 35 | ||
| 27 | #update=make S runtime again | 36 | #write main.zig { |
| 28 | #file=main.zig | ||
| 29 | const SomeType = u16; | 37 | const SomeType = u16; |
| 30 | const S = struct { | 38 | const S = struct { |
| 31 | x: SomeType, | 39 | x: SomeType, |
| ... | @@ -35,4 +43,8 @@ pub fn main() void { | ... | @@ -35,4 +43,8 @@ pub fn main() void { |
| 35 | const s: S = .{ .x = 456 }; | 43 | const s: S = .{ .x = 456 }; |
| 36 | s.foo(); | 44 | s.foo(); |
| 37 | } | 45 | } |
| 38 | #expect_stdout="" | 46 | #} |
| 47 | #update { | ||
| 48 | make S runtime again#} | ||
| 49 | #check stdout { | ||
| 50 | #} |
test/incremental/type_dependency_loop+41-21| ... | @@ -1,50 +1,70 @@ | ... | @@ -1,50 +1,70 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | pub const A = struct { b: B }; | 6 | pub const A = struct { b: B }; |
| 4 | pub const B = struct { a: A }; | 7 | pub const B = struct { a: A }; |
| 5 | pub fn main() void { | 8 | pub fn main() void { |
| 6 | _ = @as(B, undefined); | 9 | _ = @as(B, undefined); |
| 7 | } | 10 | } |
| 8 | #expect_error=:error: dependency loop with length 2 | 11 | #} |
| 9 | #expect_error=main.zig:2:27: note: type 'main.B' depends on type 'main.A' for field declared here | 12 | #update |
| 10 | #expect_error=main.zig:1:27: note: type 'main.A' depends on type 'main.B' for field declared here | 13 | #check errors { |
| 11 | #expect_error=:note: eliminate any one of these dependencies to break the loop | 14 | error: dependency loop with length 2 |
| 15 | main.zig:2:27: note: type 'main.B' depends on type 'main.A' for field declared here | ||
| 16 | main.zig:1:27: note: type 'main.A' depends on type 'main.B' for field declared here | ||
| 17 | note: eliminate any one of these dependencies to break the loop | ||
| 18 | #} | ||
| 12 | 19 | ||
| 13 | #update=remove reference to dependency loop | 20 | #write main.zig { |
| 14 | #file=main.zig | ||
| 15 | pub const A = struct { b: B }; | 21 | pub const A = struct { b: B }; |
| 16 | pub const B = struct { a: A }; | 22 | pub const B = struct { a: A }; |
| 17 | pub fn main() void { | 23 | pub fn main() void { |
| 18 | _ = B; | 24 | _ = B; |
| 19 | } | 25 | } |
| 20 | #expect_stdout="" | 26 | #} |
| 27 | #update { | ||
| 28 | remove reference to dependency loop#} | ||
| 29 | #check stdout { | ||
| 30 | #} | ||
| 21 | 31 | ||
| 22 | #update=change dependency loop without fixing it | 32 | #write main.zig { |
| 23 | #file=main.zig | ||
| 24 | pub const A = struct { b: B }; | 33 | pub const A = struct { b: B }; |
| 25 | pub const B = struct { a: *align(@alignOf(A)) A }; | 34 | pub const B = struct { a: *align(@alignOf(A)) A }; |
| 26 | pub fn main() void { | 35 | pub fn main() void { |
| 27 | _ = B; | 36 | _ = B; |
| 28 | } | 37 | } |
| 29 | #expect_stdout="" | 38 | #} |
| 39 | #update { | ||
| 40 | change dependency loop without fixing it#} | ||
| 41 | #check stdout { | ||
| 42 | #} | ||
| 30 | 43 | ||
| 31 | #update=reference dependency loop again | 44 | #write main.zig { |
| 32 | #file=main.zig | ||
| 33 | pub const A = struct { b: B }; | 45 | pub const A = struct { b: B }; |
| 34 | pub const B = struct { a: *align(@alignOf(A)) A }; | 46 | pub const B = struct { a: *align(@alignOf(A)) A }; |
| 35 | pub fn main() void { | 47 | pub fn main() void { |
| 36 | _ = @as(B, undefined); | 48 | _ = @as(B, undefined); |
| 37 | } | 49 | } |
| 38 | #expect_error=:error: dependency loop with length 2 | 50 | #} |
| 39 | #expect_error=main.zig:2:43: note: type 'main.B' depends on type 'main.A' for alignment query here | 51 | #update { |
| 40 | #expect_error=main.zig:1:27: note: type 'main.A' depends on type 'main.B' for field declared here | 52 | reference dependency loop again#} |
| 41 | #expect_error=:note: eliminate any one of these dependencies to break the loop | 53 | #check errors { |
| 54 | error: dependency loop with length 2 | ||
| 55 | main.zig:2:43: note: type 'main.B' depends on type 'main.A' for alignment query here | ||
| 56 | main.zig:1:27: note: type 'main.A' depends on type 'main.B' for field declared here | ||
| 57 | note: eliminate any one of these dependencies to break the loop | ||
| 58 | #} | ||
| 42 | 59 | ||
| 43 | #update=fix dependency loop | 60 | #write main.zig { |
| 44 | #file=main.zig | ||
| 45 | pub const A = struct { b: B }; | 61 | pub const A = struct { b: B }; |
| 46 | pub const B = struct { a: *A }; | 62 | pub const B = struct { a: *A }; |
| 47 | pub fn main() void { | 63 | pub fn main() void { |
| 48 | _ = @as(B, undefined); | 64 | _ = @as(B, undefined); |
| 49 | } | 65 | } |
| 50 | #expect_stdout="" | 66 | #} |
| 67 | #update { | ||
| 68 | fix dependency loop#} | ||
| 69 | #check stdout { | ||
| 70 | #} |
test/incremental/unreferenced_error+31-12| ... | @@ -1,25 +1,35 @@ | ... | @@ -1,25 +1,35 @@ |
| 1 | #update=initial version | 1 | #exe { |
| 2 | #file=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | ||
| 4 | |||
| 5 | #write main.zig { | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| 4 | pub fn main() !void { | 7 | pub fn main() !void { |
| 5 | try std.Io.File.stdout().writeStreamingAll(io, a); | 8 | try std.Io.File.stdout().writeStreamingAll(io, a); |
| 6 | } | 9 | } |
| 7 | const a = "Hello, World!\n"; | 10 | const a = "Hello, World!\n"; |
| 8 | const io = std.Io.Threaded.global_single_threaded.io(); | 11 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 9 | #expect_stdout="Hello, World!\n" | 12 | #} |
| 13 | #update | ||
| 14 | #check stdout { | ||
| 15 | Hello, World! | ||
| 16 | #} | ||
| 10 | 17 | ||
| 11 | #update=introduce compile error | 18 | #write main.zig { |
| 12 | #file=main.zig | ||
| 13 | const std = @import("std"); | 19 | const std = @import("std"); |
| 14 | pub fn main() !void { | 20 | pub fn main() !void { |
| 15 | try std.Io.File.stdout().writeStreamingAll(io, a); | 21 | try std.Io.File.stdout().writeStreamingAll(io, a); |
| 16 | } | 22 | } |
| 17 | const a = @compileError("bad a"); | 23 | const a = @compileError("bad a"); |
| 18 | const io = std.Io.Threaded.global_single_threaded.io(); | 24 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 19 | #expect_error=main.zig:5:11: error: bad a | 25 | #} |
| 26 | #update { | ||
| 27 | introduce compile error#} | ||
| 28 | #check errors { | ||
| 29 | main.zig:5:11: error: bad a | ||
| 30 | #} | ||
| 20 | 31 | ||
| 21 | #update=remove error reference | 32 | #write main.zig { |
| 22 | #file=main.zig | ||
| 23 | const std = @import("std"); | 33 | const std = @import("std"); |
| 24 | pub fn main() !void { | 34 | pub fn main() !void { |
| 25 | try std.Io.File.stdout().writeStreamingAll(io, b); | 35 | try std.Io.File.stdout().writeStreamingAll(io, b); |
| ... | @@ -27,10 +37,14 @@ pub fn main() !void { | ... | @@ -27,10 +37,14 @@ pub fn main() !void { |
| 27 | const a = @compileError("bad a"); | 37 | const a = @compileError("bad a"); |
| 28 | const b = "Hi there!\n"; | 38 | const b = "Hi there!\n"; |
| 29 | const io = std.Io.Threaded.global_single_threaded.io(); | 39 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 30 | #expect_stdout="Hi there!\n" | 40 | #} |
| 41 | #update { | ||
| 42 | remove error reference#} | ||
| 43 | #check stdout { | ||
| 44 | Hi there! | ||
| 45 | #} | ||
| 31 | 46 | ||
| 32 | #update=introduce and remove reference to error | 47 | #write main.zig { |
| 33 | #file=main.zig | ||
| 34 | const std = @import("std"); | 48 | const std = @import("std"); |
| 35 | pub fn main() !void { | 49 | pub fn main() !void { |
| 36 | try std.Io.File.stdout().writeStreamingAll(io, a); | 50 | try std.Io.File.stdout().writeStreamingAll(io, a); |
| ... | @@ -38,4 +52,9 @@ pub fn main() !void { | ... | @@ -38,4 +52,9 @@ pub fn main() !void { |
| 38 | const a = "Back to a\n"; | 52 | const a = "Back to a\n"; |
| 39 | const b = @compileError("bad b"); | 53 | const b = @compileError("bad b"); |
| 40 | const io = std.Io.Threaded.global_single_threaded.io(); | 54 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 41 | #expect_stdout="Back to a\n" | 55 | #} |
| 56 | #update { | ||
| 57 | introduce and remove reference to error#} | ||
| 58 | #check stdout { | ||
| 59 | Back to a | ||
| 60 | #} |
test/src/Cases.zig+1| ... | @@ -626,6 +626,7 @@ pub fn lowerToBuildSteps( | ... | @@ -626,6 +626,7 @@ pub fn lowerToBuildSteps( |
| 626 | if (getExternalExecutor(io, &case.target.result, .{ | 626 | if (getExternalExecutor(io, &case.target.result, .{ |
| 627 | .host_cpu_arch = host.result.cpu.arch, | 627 | .host_cpu_arch = host.result.cpu.arch, |
| 628 | .host_os_tag = host.result.os.tag, | 628 | .host_os_tag = host.result.os.tag, |
| 629 | .link_mode = .dynamic, // TODO: this emulates old behavior until this file is deleted | ||
| 629 | .link_libc = true, | 630 | .link_libc = true, |
| 630 | }) != .native) { | 631 | }) != .native) { |
| 631 | // We wouldn't be able to run the compiled C code. | 632 | // We wouldn't be able to run the compiled C code. |
test/src/Runner.zig created+1858| ... | @@ -0,0 +1,1858 @@ | ||
| 1 | const assert = std.debug.assert; | ||
| 2 | const std = @import("std"); | ||
| 3 | const Runner = @This(); | ||
| 4 | const zig_version_string = @import("builtin").zig_version_string; | ||
| 5 | |||
| 6 | gpa: std.mem.Allocator, | ||
| 7 | arena: *std.heap.ArenaAllocator, | ||
| 8 | io: std.Io, | ||
| 9 | prog_node: std.Progress.Node, | ||
| 10 | args: Args, | ||
| 11 | host: std.Target, | ||
| 12 | server: std.zig.Server, | ||
| 13 | eb_wip: std.zig.ErrorBundle.Wip, | ||
| 14 | |||
| 15 | const Args = struct { | ||
| 16 | @"test": ?struct { | ||
| 17 | input_dir: std.zig.Server.Message.InputDir, | ||
| 18 | dir: std.Io.Dir, | ||
| 19 | }, | ||
| 20 | manifest_file: ?std.Io.File, | ||
| 21 | zig_exe: ?std.Io.File, | ||
| 22 | lib_dir: ?std.Io.Dir, | ||
| 23 | src_dir: ?std.Io.Dir, | ||
| 24 | targets: std.ArrayList(u32), | ||
| 25 | target_bytes: std.ArrayList(u8), | ||
| 26 | libc_runtimes_dir: ?std.Io.Dir, | ||
| 27 | enable_darling: bool, | ||
| 28 | enable_qemu: bool, | ||
| 29 | enable_rosetta: bool, | ||
| 30 | enable_wasmtime: bool, | ||
| 31 | enable_wine: bool, | ||
| 32 | quiet: bool, | ||
| 33 | |||
| 34 | fn deinit(args: *Args, gpa: std.mem.Allocator) void { | ||
| 35 | args.targets.deinit(gpa); | ||
| 36 | args.target_bytes.deinit(gpa); | ||
| 37 | args.* = undefined; | ||
| 38 | } | ||
| 39 | |||
| 40 | fn addTarget(args: *Args, gpa: std.mem.Allocator, target: []const u8) std.mem.Allocator.Error!void { | ||
| 41 | try args.target_bytes.ensureUnusedCapacity(gpa, target.len + 1); | ||
| 42 | try args.targets.append(gpa, @intCast(args.target_bytes.items.len)); | ||
| 43 | args.target_bytes.appendSliceAssumeCapacity(target); | ||
| 44 | args.target_bytes.appendAssumeCapacity(0); | ||
| 45 | } | ||
| 46 | }; | ||
| 47 | |||
| 48 | fn deinit(runner: *Runner) void { | ||
| 49 | runner.prog_node.end(); | ||
| 50 | runner.args.deinit(runner.gpa); | ||
| 51 | runner.eb_wip.deinit(); | ||
| 52 | runner.* = undefined; | ||
| 53 | } | ||
| 54 | |||
| 55 | const FailError = error{AlreadyReported} || std.mem.Allocator.Error || std.Io.Cancelable; | ||
| 56 | fn fail(runner: *Runner, comptime fmt: []const u8, args: anytype) FailError { | ||
| 57 | return runner.failString(try runner.eb_wip.printString(fmt, args)); | ||
| 58 | } | ||
| 59 | fn failString(runner: *Runner, msg: std.zig.ErrorBundle.String) FailError { | ||
| 60 | try runner.eb_wip.addRootErrorMessage(.{ | ||
| 61 | .msg = msg, | ||
| 62 | }); | ||
| 63 | return error.AlreadyReported; | ||
| 64 | } | ||
| 65 | |||
| 66 | pub const ProtocolError = error{ | ||
| 67 | ServerReadFailed, | ||
| 68 | ServerWriteFailed, | ||
| 69 | ServerEndOfStream, | ||
| 70 | } || FailError; | ||
| 71 | pub fn runServer(runner: *Runner) ProtocolError { | ||
| 72 | runner.server.serveStringMessage(.zig_version, zig_version_string) catch |err| switch (err) { | ||
| 73 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 74 | }; | ||
| 75 | while (true) { | ||
| 76 | _ = runner.arena.reset(.retain_capacity); | ||
| 77 | const arena = runner.arena.allocator(); | ||
| 78 | const hdr = runner.server.receiveMessage() catch |err| switch (err) { | ||
| 79 | error.ReadFailed => return error.ServerReadFailed, | ||
| 80 | error.EndOfStream => return error.ServerEndOfStream, | ||
| 81 | }; | ||
| 82 | switch (hdr.tag) { | ||
| 83 | else => |tag| return runner.fail("unsupported message: {t}", .{tag}), | ||
| 84 | .exit => std.process.exit(0), | ||
| 85 | .args => { | ||
| 86 | const args_body = try arena.alloc(u8, hdr.bytes_len); | ||
| 87 | runner.server.in.readSliceAll(args_body) catch unreachable; | ||
| 88 | const State = enum { | ||
| 89 | positional, | ||
| 90 | @"--zig", | ||
| 91 | @"--lib", | ||
| 92 | @"--src", | ||
| 93 | @"--target", | ||
| 94 | @"--libc-runtimes", | ||
| 95 | }; | ||
| 96 | var state: State = .positional; | ||
| 97 | const state_expected: std.enums.EnumArray(State, enum { | ||
| 98 | path, | ||
| 99 | dir, | ||
| 100 | file, | ||
| 101 | string, | ||
| 102 | }) = .init(.{ | ||
| 103 | .positional = .path, | ||
| 104 | .@"--zig" = .file, | ||
| 105 | .@"--lib" = .dir, | ||
| 106 | .@"--src" = .dir, | ||
| 107 | .@"--target" = .string, | ||
| 108 | .@"--libc-runtimes" = .dir, | ||
| 109 | }); | ||
| 110 | var input_dir: std.zig.Server.Message.InputDir = .cwd; | ||
| 111 | var args_body_offset: usize = 0; | ||
| 112 | while (args_body.len - args_body_offset > 0) { | ||
| 113 | const arg: std.zig.Client.Message.Arg = | ||
| 114 | @fromBackingInt(args_body[args_body_offset]); | ||
| 115 | args_body_offset += 1; | ||
| 116 | arg: switch (arg) { | ||
| 117 | .string => { | ||
| 118 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; | ||
| 119 | const string = args_body[args_body_offset..end]; | ||
| 120 | args_body_offset = end + 1; | ||
| 121 | switch (state) { | ||
| 122 | .positional => if (std.mem.eql(u8, string, "--target")) { | ||
| 123 | state = .@"--target"; | ||
| 124 | continue; | ||
| 125 | } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| { | ||
| 126 | try runner.args.addTarget(runner.gpa, target); | ||
| 127 | } else if (std.mem.eql(u8, string, "--libc-runtimes")) { | ||
| 128 | state = .@"--libc-runtimes"; | ||
| 129 | continue; | ||
| 130 | } else if (std.mem.eql(u8, string, "-fdarling")) { | ||
| 131 | runner.args.enable_darling = true; | ||
| 132 | } else if (std.mem.eql(u8, string, "-fqemu")) { | ||
| 133 | runner.args.enable_qemu = true; | ||
| 134 | } else if (std.mem.eql(u8, string, "-frosetta")) { | ||
| 135 | runner.args.enable_rosetta = true; | ||
| 136 | } else if (std.mem.eql(u8, string, "-fwasmtime")) { | ||
| 137 | runner.args.enable_wasmtime = true; | ||
| 138 | } else if (std.mem.eql(u8, string, "-fwine")) { | ||
| 139 | runner.args.enable_wine = true; | ||
| 140 | } else if (std.mem.eql(u8, string, "--quiet")) { | ||
| 141 | runner.args.quiet = true; | ||
| 142 | }, | ||
| 143 | .@"--target" => try runner.args.addTarget(runner.gpa, string), | ||
| 144 | else => return runner.fail( | ||
| 145 | "\"{t}\" expected {t}, got {t}", | ||
| 146 | .{ state, state_expected.get(state), arg }, | ||
| 147 | ), | ||
| 148 | } | ||
| 149 | }, | ||
| 150 | .prefix => { | ||
| 151 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; | ||
| 152 | const string = args_body[args_body_offset..end]; | ||
| 153 | args_body_offset = end + 1; | ||
| 154 | assert(state == .positional); | ||
| 155 | state = if (std.mem.eql(u8, string, "--zig=")) | ||
| 156 | .@"--zig" | ||
| 157 | else if (std.mem.eql(u8, string, "--lib=")) | ||
| 158 | .@"--lib" | ||
| 159 | else if (std.mem.eql(u8, string, "--src=")) | ||
| 160 | .@"--src" | ||
| 161 | else if (std.mem.eql(u8, string, "--libc-runtimes=")) | ||
| 162 | .@"--libc-runtimes" | ||
| 163 | else | ||
| 164 | return runner.fail("unsupported arg prefix: {q}", .{string}); | ||
| 165 | continue; | ||
| 166 | }, | ||
| 167 | .suffix => { | ||
| 168 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; | ||
| 169 | const string = args_body[args_body_offset..end]; | ||
| 170 | args_body_offset = end + 1; | ||
| 171 | return runner.fail("unsupported arg suffix: {q}", .{string}); | ||
| 172 | }, | ||
| 173 | .input_dir => { | ||
| 174 | input_dir = @fromBackingInt(@backingInt(input_dir) + 1); | ||
| 175 | continue :arg .output_dir; | ||
| 176 | }, | ||
| 177 | .output_dir => { | ||
| 178 | const dir_handle: *align(1) const std.Io.Dir.Handle = @ptrCast( | ||
| 179 | args_body[args_body_offset..][0..@sizeOf(std.Io.Dir.Handle)], | ||
| 180 | ); | ||
| 181 | args_body_offset += @sizeOf(std.Io.Dir.Handle); | ||
| 182 | const dir = runner.io.vtable.inheritParentDir( | ||
| 183 | runner.io.userdata, | ||
| 184 | dir_handle.*, | ||
| 185 | ) catch |err| switch (err) { | ||
| 186 | error.Canceled => |e| return e, | ||
| 187 | else => |e| return runner.fail( | ||
| 188 | "unable to inherit parent dir: {t}", | ||
| 189 | .{e}, | ||
| 190 | ), | ||
| 191 | }; | ||
| 192 | switch (state) { | ||
| 193 | .positional => { | ||
| 194 | if (runner.args.@"test" != null or | ||
| 195 | runner.args.manifest_file != null) return runner.fail( | ||
| 196 | "{t} specified multiple times", | ||
| 197 | .{state}, | ||
| 198 | ); | ||
| 199 | runner.args.@"test" = .{ .input_dir = input_dir, .dir = dir }; | ||
| 200 | const manifest_path = "manifest"; | ||
| 201 | runner.args.manifest_file = dir.openFile( | ||
| 202 | runner.io, | ||
| 203 | manifest_path, | ||
| 204 | .{}, | ||
| 205 | ) catch |err| switch (err) { | ||
| 206 | error.Canceled => |e| return e, | ||
| 207 | else => |e| return runner.fail( | ||
| 208 | "unable to open manifest file: {t}", | ||
| 209 | .{e}, | ||
| 210 | ), | ||
| 211 | }; | ||
| 212 | try runner.discoverInput(input_dir, manifest_path); | ||
| 213 | }, | ||
| 214 | .@"--lib" => { | ||
| 215 | if (runner.args.lib_dir != null) return runner.fail( | ||
| 216 | "\"{t}\" specified multiple times", | ||
| 217 | .{state}, | ||
| 218 | ); | ||
| 219 | runner.args.lib_dir = dir; | ||
| 220 | }, | ||
| 221 | .@"--src" => { | ||
| 222 | if (runner.args.src_dir != null) return runner.fail( | ||
| 223 | "\"{t}\" specified multiple times", | ||
| 224 | .{state}, | ||
| 225 | ); | ||
| 226 | runner.args.src_dir = dir; | ||
| 227 | }, | ||
| 228 | .@"--libc-runtimes" => { | ||
| 229 | if (runner.args.libc_runtimes_dir != null) return runner.fail( | ||
| 230 | "\"{t}\" specified multiple times", | ||
| 231 | .{state}, | ||
| 232 | ); | ||
| 233 | runner.args.libc_runtimes_dir = dir; | ||
| 234 | }, | ||
| 235 | else => return runner.fail( | ||
| 236 | "\"{t}\" expected {t}, got {t}", | ||
| 237 | .{ state, state_expected.get(state), arg }, | ||
| 238 | ), | ||
| 239 | } | ||
| 240 | }, | ||
| 241 | .input_file, .input_file_content, .output_file => { | ||
| 242 | const file_handle: *align(1) const std.Io.File.Handle = @ptrCast( | ||
| 243 | args_body[args_body_offset..][0..@sizeOf(std.Io.File.Handle)], | ||
| 244 | ); | ||
| 245 | args_body_offset += @sizeOf(std.Io.File.Handle); | ||
| 246 | const file = runner.io.vtable.inheritParentFile( | ||
| 247 | runner.io.userdata, | ||
| 248 | file_handle.*, | ||
| 249 | .{ .nonblocking = false }, | ||
| 250 | ) catch |err| switch (err) { | ||
| 251 | error.Canceled => |e| return e, | ||
| 252 | else => |e| return runner.fail( | ||
| 253 | "unable to inherit parent file: {t}", | ||
| 254 | .{e}, | ||
| 255 | ), | ||
| 256 | }; | ||
| 257 | switch (state) { | ||
| 258 | .positional => { | ||
| 259 | if (runner.args.@"test" != null or | ||
| 260 | runner.args.manifest_file != null) return runner.fail( | ||
| 261 | "{t} specified multiple times", | ||
| 262 | .{state}, | ||
| 263 | ); | ||
| 264 | runner.args.manifest_file = file; | ||
| 265 | }, | ||
| 266 | .@"--zig" => { | ||
| 267 | if (runner.args.zig_exe != null) return runner.fail( | ||
| 268 | "\"{t}\" specified multiple times", | ||
| 269 | .{state}, | ||
| 270 | ); | ||
| 271 | runner.args.zig_exe = file; | ||
| 272 | }, | ||
| 273 | else => return runner.fail( | ||
| 274 | "\"{t}\" expected {t}, got {t}", | ||
| 275 | .{ state, state_expected.get(state), arg }, | ||
| 276 | ), | ||
| 277 | } | ||
| 278 | }, | ||
| 279 | } | ||
| 280 | state = .positional; | ||
| 281 | } | ||
| 282 | }, | ||
| 283 | .query_test_metadata => { | ||
| 284 | const expected_panic_msgs = try arena.alloc(u32, runner.args.targets.items.len); | ||
| 285 | @memset(expected_panic_msgs, 0); | ||
| 286 | runner.server.serveTestMetadata(.{ | ||
| 287 | .names = runner.args.targets.items, | ||
| 288 | .expected_panic_msgs = expected_panic_msgs, | ||
| 289 | .string_bytes = runner.args.target_bytes.items, | ||
| 290 | }) catch |err| switch (err) { | ||
| 291 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 292 | }; | ||
| 293 | }, | ||
| 294 | .run_test => { | ||
| 295 | const test_index = runner.server.receiveBody_u32() catch unreachable; | ||
| 296 | runner.server.serveBodylessMessage(.test_started) catch |err| switch (err) { | ||
| 297 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 298 | }; | ||
| 299 | runner.server.serveTestResults(.{ | ||
| 300 | .index = test_index, | ||
| 301 | .flags = .{ | ||
| 302 | .status = if (runner.testOne(test_index)) | ||
| 303 | .pass | ||
| 304 | else |err| status: switch (err) { | ||
| 305 | error.Canceled, | ||
| 306 | error.OutOfMemory, | ||
| 307 | error.ServerWriteFailed, | ||
| 308 | => |e| return e, | ||
| 309 | error.AlreadyReported => { | ||
| 310 | var eb = try runner.eb_wip.toOwnedBundle(""); | ||
| 311 | defer eb.deinit(runner.gpa); | ||
| 312 | assert(eb.errorMessageCount() > 0); // already reported what? | ||
| 313 | runner.server.serveErrorBundle( | ||
| 314 | .error_bundle, | ||
| 315 | eb, | ||
| 316 | ) catch |server_err| switch (server_err) { | ||
| 317 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 318 | }; | ||
| 319 | const eb_wip: std.zig.ErrorBundle.Wip = try .init(runner.gpa); | ||
| 320 | runner.eb_wip.deinit(); | ||
| 321 | runner.eb_wip = eb_wip; | ||
| 322 | break :status .fail; | ||
| 323 | }, | ||
| 324 | error.SkipTest => .skip, | ||
| 325 | }, | ||
| 326 | .fuzz = false, | ||
| 327 | .log_err_count = 0, | ||
| 328 | .leak_count = 0, | ||
| 329 | }, | ||
| 330 | }) catch |err| switch (err) { | ||
| 331 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 332 | }; | ||
| 333 | assert(runner.eb_wip.root_list.items.len == 0); // failed to report | ||
| 334 | }, | ||
| 335 | } | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | fn discoverInput( | ||
| 340 | runner: *Runner, | ||
| 341 | dir: std.zig.Server.Message.InputDir, | ||
| 342 | sub_path: []const u8, | ||
| 343 | ) error{ServerWriteFailed}!void { | ||
| 344 | return runner.discoverInputInner(dir, sub_path) catch |err| switch (err) { | ||
| 345 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 346 | }; | ||
| 347 | } | ||
| 348 | fn discoverInputInner( | ||
| 349 | runner: *Runner, | ||
| 350 | dir: std.zig.Server.Message.InputDir, | ||
| 351 | sub_path: []const u8, | ||
| 352 | ) std.Io.Writer.Error!void { | ||
| 353 | try runner.server.serveMessageHeader(.{ | ||
| 354 | .tag = .discovered_inputs, | ||
| 355 | .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) + | ||
| 356 | sub_path.len + 1), | ||
| 357 | }); | ||
| 358 | try runner.server.out.writeInt(u32, @backingInt(dir), .little); | ||
| 359 | try runner.server.out.writeAll(sub_path); | ||
| 360 | try runner.server.out.writeByte(0); | ||
| 361 | try runner.server.out.flush(); | ||
| 362 | } | ||
| 363 | |||
| 364 | pub const SourceReader = struct { | ||
| 365 | const Io = std.Io; | ||
| 366 | |||
| 367 | backing: *std.Io.Reader, | ||
| 368 | line_aw: std.Io.Writer.Allocating, | ||
| 369 | interface: std.Io.Reader, | ||
| 370 | line: u32, | ||
| 371 | eb_wip: *std.zig.ErrorBundle.Wip, | ||
| 372 | src_path: std.zig.ErrorBundle.String, | ||
| 373 | |||
| 374 | pub fn init( | ||
| 375 | reader: *Io.Reader, | ||
| 376 | gpa: std.mem.Allocator, | ||
| 377 | eb_wip: *std.zig.ErrorBundle.Wip, | ||
| 378 | src_path: std.zig.ErrorBundle.String, | ||
| 379 | ) SourceReader { | ||
| 380 | return .{ | ||
| 381 | .backing = reader, | ||
| 382 | .line_aw = .init(gpa), | ||
| 383 | .interface = .{ | ||
| 384 | .vtable = &.{ | ||
| 385 | .stream = stream, | ||
| 386 | .discard = discard, | ||
| 387 | .readVec = readVec, | ||
| 388 | .rebase = rebase, | ||
| 389 | }, | ||
| 390 | .buffer = &.{}, | ||
| 391 | .seek = 0, | ||
| 392 | .end = 0, | ||
| 393 | }, | ||
| 394 | .line = 0, | ||
| 395 | .eb_wip = eb_wip, | ||
| 396 | .src_path = src_path, | ||
| 397 | }; | ||
| 398 | } | ||
| 399 | fn deinit(s: *SourceReader) void { | ||
| 400 | s.line_aw.deinit(); | ||
| 401 | } | ||
| 402 | fn stream(r: *Io.Reader, _: *Io.Writer, _: Io.Limit) Io.Reader.StreamError!usize { | ||
| 403 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 404 | try s.bufferLine(); | ||
| 405 | return 0; | ||
| 406 | } | ||
| 407 | fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { | ||
| 408 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 409 | const remaining = r.buffered(); | ||
| 410 | if (remaining.len > 0) { | ||
| 411 | const n = limit.minInt(remaining.len); | ||
| 412 | r.toss(n); | ||
| 413 | return n; | ||
| 414 | } | ||
| 415 | try s.bufferLine(); | ||
| 416 | return 0; | ||
| 417 | } | ||
| 418 | fn readVec(r: *Io.Reader, _: [][]u8) Io.Reader.Error!usize { | ||
| 419 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 420 | try s.bufferLine(); | ||
| 421 | return 0; | ||
| 422 | } | ||
| 423 | fn rebase(r: *Io.Reader, capacity: usize) Io.Reader.RebaseError!void { | ||
| 424 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 425 | defer s.updateBuffer(); | ||
| 426 | s.line_aw.ensureUnusedCapacity(capacity) catch |err| switch (err) { | ||
| 427 | error.OutOfMemory => return error.ReadFailed, | ||
| 428 | }; | ||
| 429 | } | ||
| 430 | fn updateBuffer(s: *SourceReader) void { | ||
| 431 | s.interface.buffer = s.line_aw.writer.buffer; | ||
| 432 | s.interface.end = s.line_aw.writer.end; | ||
| 433 | } | ||
| 434 | fn bufferLine(s: *SourceReader) Io.Reader.Error!void { | ||
| 435 | defer s.updateBuffer(); | ||
| 436 | { | ||
| 437 | const written = s.line_aw.written(); | ||
| 438 | const start = if (std.mem.findScalarLast(u8, written[0..s.interface.seek], '\n')) |newline| | ||
| 439 | newline + 1 | ||
| 440 | else | ||
| 441 | 0; | ||
| 442 | s.line += @intCast(std.mem.countScalar(u8, written[0..start], '\n')); | ||
| 443 | const preserve = written[start..]; | ||
| 444 | @memmove(written[0..preserve.len], preserve); | ||
| 445 | s.line_aw.shrinkRetainingCapacity(preserve.len); | ||
| 446 | s.interface.seek -= start; | ||
| 447 | } | ||
| 448 | const n = s.backing.streamDelimiterEnding(&s.line_aw.writer, '\n') catch |err| switch (err) { | ||
| 449 | error.ReadFailed => |e| return e, | ||
| 450 | error.WriteFailed => return error.ReadFailed, | ||
| 451 | }; | ||
| 452 | if (std.mem.endsWith(u8, s.line_aw.written(), "\r")) s.line_aw.writer.end -= 1; | ||
| 453 | const buffered = s.backing.buffered(); | ||
| 454 | if (n == 0 and buffered.len == 0) return error.EndOfStream; | ||
| 455 | if (std.mem.startsWith(u8, buffered, "\n")) { | ||
| 456 | s.line_aw.writer.writeByte('\n') catch |err| switch (err) { | ||
| 457 | error.WriteFailed => return error.ReadFailed, | ||
| 458 | }; | ||
| 459 | s.backing.toss(1); | ||
| 460 | } | ||
| 461 | } | ||
| 462 | fn fail( | ||
| 463 | s: *SourceReader, | ||
| 464 | src_loc: []const u8, | ||
| 465 | comptime fmt: []const u8, | ||
| 466 | args: anytype, | ||
| 467 | ) FailError { | ||
| 468 | return s.failString(src_loc, try s.eb_wip.printString(fmt, args)); | ||
| 469 | } | ||
| 470 | fn failString( | ||
| 471 | s: *SourceReader, | ||
| 472 | src_loc: []const u8, | ||
| 473 | msg: std.zig.ErrorBundle.String, | ||
| 474 | ) FailError { | ||
| 475 | const line: u32, const column: u32, const source_line = src_loc: { | ||
| 476 | const pos = src_loc.ptr - s.interface.buffer.ptr; | ||
| 477 | const end = std.mem.findScalarPos(u8, s.interface.buffer, pos, '\n') orelse | ||
| 478 | s.interface.buffer.len; | ||
| 479 | const start = if (std.mem.findScalarLast(u8, s.interface.buffer[0..end], '\n')) |newline| | ||
| 480 | newline + 1 | ||
| 481 | else | ||
| 482 | 0; | ||
| 483 | break :src_loc .{ | ||
| 484 | @intCast(s.line + std.mem.countScalar(u8, s.interface.buffer[0..start], '\n')), | ||
| 485 | @intCast(pos - start), | ||
| 486 | s.interface.buffer[start..end], | ||
| 487 | }; | ||
| 488 | }; | ||
| 489 | try s.eb_wip.addRootErrorMessage(.{ | ||
| 490 | .msg = msg, | ||
| 491 | .src_loc = try s.eb_wip.addSourceLocation(.{ | ||
| 492 | .src_path = s.src_path, | ||
| 493 | .line = line, | ||
| 494 | .column = column, | ||
| 495 | .span_start = column, | ||
| 496 | .span_main = column, | ||
| 497 | .span_end = @intCast(column + src_loc.len), | ||
| 498 | .source_line = try s.eb_wip.addString(source_line), | ||
| 499 | }), | ||
| 500 | }); | ||
| 501 | return error.AlreadyReported; | ||
| 502 | } | ||
| 503 | }; | ||
| 504 | pub const DelimitedReader = struct { | ||
| 505 | const Io = std.Io; | ||
| 506 | |||
| 507 | unlimited: *Io.Reader, | ||
| 508 | remaining: Io.Limit, | ||
| 509 | delimiter: []const u8, | ||
| 510 | interface: Io.Reader, | ||
| 511 | |||
| 512 | pub fn init(reader: *Io.Reader, delimiter: []const u8, buffer: []u8) DelimitedReader { | ||
| 513 | return .{ | ||
| 514 | .unlimited = reader, | ||
| 515 | .remaining = .nothing, | ||
| 516 | .delimiter = delimiter, | ||
| 517 | .interface = .{ | ||
| 518 | .vtable = &.{ | ||
| 519 | .stream = stream, | ||
| 520 | .discard = discard, | ||
| 521 | }, | ||
| 522 | .buffer = buffer, | ||
| 523 | .seek = 0, | ||
| 524 | .end = 0, | ||
| 525 | }, | ||
| 526 | }; | ||
| 527 | } | ||
| 528 | fn stream(r: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { | ||
| 529 | const d: *DelimitedReader = @fieldParentPtr("interface", r); | ||
| 530 | const block = try d.unlimited.peekGreedy(d.delimiter.len); | ||
| 531 | d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse | ||
| 532 | block.len - d.delimiter.len + 1); | ||
| 533 | if (d.remaining == .nothing) return error.EndOfStream; | ||
| 534 | const n = try d.unlimited.stream(w, limit.min(d.remaining)); | ||
| 535 | d.remaining = d.remaining.subtract(n).?; | ||
| 536 | return n; | ||
| 537 | } | ||
| 538 | fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { | ||
| 539 | const d: *DelimitedReader = @fieldParentPtr("interface", r); | ||
| 540 | const block = try d.unlimited.peekGreedy(d.delimiter.len); | ||
| 541 | d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse | ||
| 542 | block.len - d.delimiter.len + 1); | ||
| 543 | if (d.remaining == .nothing) return error.EndOfStream; | ||
| 544 | const n = try d.unlimited.discard(limit.min(d.remaining)); | ||
| 545 | d.remaining = d.remaining.subtract(n).?; | ||
| 546 | return n; | ||
| 547 | } | ||
| 548 | }; | ||
| 549 | |||
| 550 | const Update = struct { | ||
| 551 | target_query: []const u8, | ||
| 552 | manifest_sr: SourceReader, | ||
| 553 | compiler: ?Compiler, | ||
| 554 | src_dir: std.Io.Dir, | ||
| 555 | prog_node: std.Progress.Node, | ||
| 556 | mtime: std.Io.Timestamp, | ||
| 557 | num: usize, | ||
| 558 | allow_skip: bool, | ||
| 559 | }; | ||
| 560 | |||
| 561 | const TestError = error{ | ||
| 562 | SkipTest, | ||
| 563 | } || FailError; | ||
| 564 | fn testOne(runner: *Runner, test_index: u32) (error{ServerWriteFailed} || TestError)!void { | ||
| 565 | const target_query = | ||
| 566 | std.mem.sliceTo(runner.args.target_bytes.items[runner.args.targets.items[test_index]..], 0); | ||
| 567 | const manifest_file = runner.args.manifest_file orelse | ||
| 568 | return runner.fail("missing \"path/to/test\" arg", .{}); | ||
| 569 | var manifest_buffer: [512]u8 = undefined; | ||
| 570 | var manifest_fr = manifest_file.reader(runner.io, &manifest_buffer); | ||
| 571 | var update: Update = .{ | ||
| 572 | .target_query = target_query, | ||
| 573 | .manifest_sr = .init(&manifest_fr.interface, runner.gpa, &runner.eb_wip, src_path_string: { | ||
| 574 | var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; | ||
| 575 | const src_path = src_path: { | ||
| 576 | break :src_path path_buffer[0 .. manifest_file.realPath( | ||
| 577 | runner.io, | ||
| 578 | &path_buffer, | ||
| 579 | ) catch |err| switch (err) { | ||
| 580 | error.Canceled => |e| return e, | ||
| 581 | else => break :src_path "manifest", | ||
| 582 | }]; | ||
| 583 | }; | ||
| 584 | break :src_path_string try runner.eb_wip.addString(src_path); | ||
| 585 | }), | ||
| 586 | .compiler = null, | ||
| 587 | .src_dir = (runner.args.src_dir orelse | ||
| 588 | return runner.fail("missing \"--src=path/to/src\" arg", .{})).createDirPathOpen( | ||
| 589 | runner.io, | ||
| 590 | target_query, | ||
| 591 | .{}, | ||
| 592 | ) catch |err| switch (err) { | ||
| 593 | error.Canceled => |e| return e, | ||
| 594 | else => |e| return runner.fail("unable to create {q}: {t}", .{ target_query, e }), | ||
| 595 | }, | ||
| 596 | .prog_node = .none, | ||
| 597 | .mtime = .now(runner.io, .real), | ||
| 598 | .num = 0, | ||
| 599 | .allow_skip = true, | ||
| 600 | }; | ||
| 601 | defer { | ||
| 602 | update.manifest_sr.deinit(); | ||
| 603 | update.src_dir.close(runner.io); | ||
| 604 | update.prog_node.end(); | ||
| 605 | } | ||
| 606 | |||
| 607 | // hack around `std.Io.Reader` interface bugs by ensuring the | ||
| 608 | // buffer is larger than the longest supported manifest line | ||
| 609 | try update.manifest_sr.line_aw.ensureTotalCapacityPrecise(512); | ||
| 610 | update.manifest_sr.updateBuffer(); | ||
| 611 | |||
| 612 | var skip_delimiter = false; | ||
| 613 | while (update.manifest_sr.interface.takeSentinel('\n')) |line| { | ||
| 614 | var line_it = std.mem.tokenizeScalar(u8, line, ' '); | ||
| 615 | const cmd_src_loc = line_it.next() orelse continue; | ||
| 616 | if (skip_delimiter) { | ||
| 617 | assert(std.mem.eql(u8, cmd_src_loc, "#}")); | ||
| 618 | skip_delimiter = false; | ||
| 619 | const unexpected = line_it.rest(); | ||
| 620 | if (unexpected.len > 0) | ||
| 621 | return update.manifest_sr.fail(unexpected, "unexpected argument {q}", .{unexpected}); | ||
| 622 | continue; | ||
| 623 | } | ||
| 624 | const cmd = std.meta.stringToEnum(Command, std.mem.cutPrefix(u8, cmd_src_loc, "#") orelse | ||
| 625 | return update.manifest_sr.fail(cmd_src_loc, "expected command", .{})) orelse | ||
| 626 | return update.manifest_sr.fail(cmd_src_loc, "unknown command", .{}); | ||
| 627 | const arg = switch (cmd) { | ||
| 628 | .todo, .exe, .lib, .obj, .update => null, | ||
| 629 | .skip, .write, .delete, .check => line_it.next() orelse | ||
| 630 | return update.manifest_sr.fail(line_it.rest()[0..0], "missing argument", .{}), | ||
| 631 | }; | ||
| 632 | var contents_file: ?std.Io.File = null; | ||
| 633 | defer if (contents_file) |file| file.close(runner.io); | ||
| 634 | var contents_buffer: [512]u8 = undefined; | ||
| 635 | var contents_impl: union(enum) { | ||
| 636 | ending: std.Io.Reader, | ||
| 637 | dr: DelimitedReader, | ||
| 638 | fr: std.Io.File.Reader, | ||
| 639 | } = undefined; | ||
| 640 | const contents_r: *std.Io.Reader = contents_r: switch (cmd) { | ||
| 641 | .skip, .delete => { | ||
| 642 | const unexpected = line_it.rest(); | ||
| 643 | if (unexpected.len > 0) return update.manifest_sr.fail( | ||
| 644 | unexpected, | ||
| 645 | "unexpected argument {q}", | ||
| 646 | .{unexpected}, | ||
| 647 | ); | ||
| 648 | contents_impl = .{ .ending = .ending_instance }; | ||
| 649 | break :contents_r &contents_impl.ending; | ||
| 650 | }, | ||
| 651 | else => { | ||
| 652 | const contents_path = line_it.rest(); | ||
| 653 | if (contents_path.len == 0) { | ||
| 654 | contents_impl = .{ .ending = .ending_instance }; | ||
| 655 | break :contents_r &contents_impl.ending; | ||
| 656 | } | ||
| 657 | |||
| 658 | if (std.mem.eql(u8, contents_path, "{")) { | ||
| 659 | contents_impl = .{ | ||
| 660 | .dr = .init(&update.manifest_sr.interface, "#}", &contents_buffer), | ||
| 661 | }; | ||
| 662 | skip_delimiter = true; | ||
| 663 | break :contents_r &contents_impl.dr.interface; | ||
| 664 | } | ||
| 665 | |||
| 666 | const args_test = runner.args.@"test" orelse return update.manifest_sr.fail( | ||
| 667 | contents_path, | ||
| 668 | "contents file requires directory test", | ||
| 669 | .{}, | ||
| 670 | ); | ||
| 671 | try runner.discoverInput(args_test.input_dir, contents_path); | ||
| 672 | |||
| 673 | contents_file = | ||
| 674 | args_test.dir.openFile(runner.io, contents_path, .{}) catch |err| switch (err) { | ||
| 675 | error.Canceled => |e| return e, | ||
| 676 | else => |e| return update.manifest_sr.fail( | ||
| 677 | contents_path, | ||
| 678 | "unable to open contents file: {t}", | ||
| 679 | .{e}, | ||
| 680 | ), | ||
| 681 | }; | ||
| 682 | contents_impl = .{ .fr = contents_file.?.reader(runner.io, &contents_buffer) }; | ||
| 683 | break :contents_r &contents_impl.fr.interface; | ||
| 684 | }, | ||
| 685 | }; | ||
| 686 | |||
| 687 | runner.handleCommand(&update, cmd_src_loc, cmd, arg, contents_r) catch |err| switch (err) { | ||
| 688 | error.Canceled, | ||
| 689 | error.OutOfMemory, | ||
| 690 | error.AlreadyReported, | ||
| 691 | error.SkipTest, | ||
| 692 | => |e| return e, | ||
| 693 | error.ContentsReadFailed => switch (contents_impl) { | ||
| 694 | .ending => unreachable, | ||
| 695 | .dr => switch (manifest_fr.err orelse return error.OutOfMemory) { | ||
| 696 | error.Canceled => |e| return e, | ||
| 697 | else => |e| return runner.fail("unable to read manifest: {t}", .{e}), | ||
| 698 | }, | ||
| 699 | .fr => |fr| switch (fr.err.?) { | ||
| 700 | error.Canceled => |e| return e, | ||
| 701 | else => |e| return runner.fail("unable to read contents file: {t}", .{e}), | ||
| 702 | }, | ||
| 703 | }, | ||
| 704 | }; | ||
| 705 | } else |err| switch (err) { | ||
| 706 | error.ReadFailed => switch (manifest_fr.err orelse return error.OutOfMemory) { | ||
| 707 | error.Canceled => |e| return e, | ||
| 708 | else => |e| return runner.fail("unable to read manifest: {t}", .{e}), | ||
| 709 | }, | ||
| 710 | error.StreamTooLong => unreachable, | ||
| 711 | error.EndOfStream => if (skip_delimiter) | ||
| 712 | return runner.fail("manifest missing \"#}}\" delimiter", .{}), | ||
| 713 | } | ||
| 714 | if (update.compiler) |*comp| { | ||
| 715 | if (comp.state != .idle) return runner.fail("missing \"#check\"", .{}); | ||
| 716 | try comp.exit(); | ||
| 717 | } | ||
| 718 | } | ||
| 719 | const Command = enum { todo, skip, exe, lib, obj, write, delete, update, check }; | ||
| 720 | fn handleCommand( | ||
| 721 | runner: *Runner, | ||
| 722 | update: *Update, | ||
| 723 | cmd_src_loc: []const u8, | ||
| 724 | cmd: Command, | ||
| 725 | arg: ?[]const u8, | ||
| 726 | contents_r: *std.Io.Reader, | ||
| 727 | ) (error{ContentsReadFailed} || TestError)!void { | ||
| 728 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; | ||
| 729 | const cmd_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "#{t}{s}{s}", .{ | ||
| 730 | cmd, if (arg) |_| " " else "", arg orelse "", | ||
| 731 | }) catch &name_buffer, 0); | ||
| 732 | defer cmd_prog_node.end(); | ||
| 733 | |||
| 734 | cmd: switch (cmd) { | ||
| 735 | .todo => _ = contents_r.discardRemaining() catch |err| switch (err) { | ||
| 736 | error.ReadFailed => {}, | ||
| 737 | }, | ||
| 738 | .skip => if (!update.allow_skip) return update.manifest_sr.fail( | ||
| 739 | cmd_src_loc, | ||
| 740 | "\"#skip\" must appear before other commands", | ||
| 741 | .{}, | ||
| 742 | ) else if (std.mem.eql(u8, update.target_query, arg.?)) return error.SkipTest, | ||
| 743 | .exe, .lib, .obj => runner.spawnCompiler(&update.compiler, update.src_dir, switch (cmd) { | ||
| 744 | else => unreachable, | ||
| 745 | .exe => .Exe, | ||
| 746 | .lib => .Lib, | ||
| 747 | .obj => .Obj, | ||
| 748 | }, contents_r, target: { | ||
| 749 | const backend_split = std.mem.findScalarLast(u8, update.target_query, '-') orelse | ||
| 750 | return runner.fail("target {q} missing query", .{update.target_query}); | ||
| 751 | const mode_split = | ||
| 752 | std.mem.findScalarLast(u8, update.target_query[0..backend_split], '-') orelse | ||
| 753 | return runner.fail("target {q} missing mode", .{update.target_query}); | ||
| 754 | const triple = update.target_query[0..mode_split]; | ||
| 755 | const backend_str = update.target_query[backend_split + 1 ..]; | ||
| 756 | const backend = std.meta.stringToEnum(Compiler.Target.Backend, backend_str) orelse | ||
| 757 | return runner.fail("target {q} unknown backend {q}", .{ | ||
| 758 | update.target_query, backend_str, | ||
| 759 | }); | ||
| 760 | const mode_str = update.target_query[mode_split + 1 .. backend_split]; | ||
| 761 | const mode = std.meta.stringToEnum(Compiler.Target.Mode, mode_str) orelse | ||
| 762 | return runner.fail("target {q} unknown mode {q}", .{ update.target_query, mode_str }); | ||
| 763 | break :target .{ | ||
| 764 | .triple = triple, | ||
| 765 | .resolved = std.zig.system.resolveTargetQuery( | ||
| 766 | runner.io, | ||
| 767 | std.Build.parseTargetQuery(.{ | ||
| 768 | .arch_os_abi = triple, | ||
| 769 | .object_format = switch (backend) { | ||
| 770 | .sema, .selfhosted, .llvm => null, | ||
| 771 | .cbe => "c", | ||
| 772 | }, | ||
| 773 | }) catch |err| switch (err) { | ||
| 774 | error.ParseFailed => return runner.fail("unable to parse triple {q}", .{ | ||
| 775 | triple, | ||
| 776 | }), | ||
| 777 | }, | ||
| 778 | ) catch |err| switch (err) { | ||
| 779 | error.Canceled => |e| return e, | ||
| 780 | else => |e| return runner.fail("unable to resolve target {q}: {t}", .{ triple, e }), | ||
| 781 | }, | ||
| 782 | .mode = mode, | ||
| 783 | .backend = backend, | ||
| 784 | }; | ||
| 785 | }) catch |err| switch (err) { | ||
| 786 | error.Canceled, error.OutOfMemory, error.AlreadyReported => |e| return e, | ||
| 787 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 788 | }, | ||
| 789 | .write => { | ||
| 790 | const file = update.src_dir.createFile(runner.io, arg.?, .{}) catch |err| switch (err) { | ||
| 791 | error.Canceled => |e| return e, | ||
| 792 | else => |e| return update.manifest_sr.fail(arg.?, "unable to create file: {t}", .{e}), | ||
| 793 | }; | ||
| 794 | defer file.close(runner.io); | ||
| 795 | var fw_buffer: [512]u8 = undefined; | ||
| 796 | var fw = file.writer(runner.io, &fw_buffer); | ||
| 797 | _ = contents_r.streamRemaining(&fw.interface) catch |err| switch (err) { | ||
| 798 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 799 | error.WriteFailed => switch (fw.err.?) { | ||
| 800 | error.Canceled => |e| return e, | ||
| 801 | else => |e| return update.manifest_sr.fail( | ||
| 802 | arg.?, | ||
| 803 | "unable to write file: {t}", | ||
| 804 | .{e}, | ||
| 805 | ), | ||
| 806 | }, | ||
| 807 | }; | ||
| 808 | fw.flush() catch |err| switch (err) { | ||
| 809 | error.Canceled => |e| return e, | ||
| 810 | else => |e| return update.manifest_sr.fail( | ||
| 811 | arg.?, | ||
| 812 | "unable to write file: {t}", | ||
| 813 | .{e}, | ||
| 814 | ), | ||
| 815 | }; | ||
| 816 | file.setTimestamps(runner.io, .{ .modify_timestamp = .{ | ||
| 817 | .new = update.mtime, | ||
| 818 | } }) catch |err| switch (err) { | ||
| 819 | error.Canceled => |e| return e, | ||
| 820 | else => |e| return update.manifest_sr.fail( | ||
| 821 | arg.?, | ||
| 822 | "unable to update file timestamps: {t}", | ||
| 823 | .{e}, | ||
| 824 | ), | ||
| 825 | }; | ||
| 826 | }, | ||
| 827 | .delete => update.src_dir.deleteFile(runner.io, arg.?) catch |err| switch (err) { | ||
| 828 | error.Canceled => |e| return e, | ||
| 829 | else => |e| return update.manifest_sr.fail(arg.?, "unable to delete file: {t}", .{e}), | ||
| 830 | }, | ||
| 831 | .update => { | ||
| 832 | const comp = &(update.compiler orelse return update.manifest_sr.fail( | ||
| 833 | cmd_src_loc, | ||
| 834 | "missing \"#exe\", \"#lib\", or \"#obj\"", | ||
| 835 | .{}, | ||
| 836 | )); | ||
| 837 | update.prog_node.end(); | ||
| 838 | update.prog_node = runner.prog_node.start( | ||
| 839 | switch (contents_r.readSliceShort(&name_buffer) catch |err| switch (err) { | ||
| 840 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 841 | }) { | ||
| 842 | 0 => switch (update.num) { | ||
| 843 | 0 => "initial update", | ||
| 844 | else => |num| std.mem.print(&name_buffer, "update {d}", .{num}) catch | ||
| 845 | &name_buffer, | ||
| 846 | }, | ||
| 847 | else => |name_len| name_buffer[0..name_len], | ||
| 848 | }, | ||
| 849 | 0, | ||
| 850 | ); | ||
| 851 | _ = contents_r.discardRemaining() catch |err| switch (err) { | ||
| 852 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 853 | }; | ||
| 854 | if (comp.state != .idle) | ||
| 855 | return update.manifest_sr.fail(cmd_src_loc, "missing \"#check\"", .{}); | ||
| 856 | comp.client.serveBodylessMessage(.update) catch |err| switch (err) { | ||
| 857 | error.WriteFailed => switch (comp.fw.err.?) { | ||
| 858 | error.Canceled => |e| return e, | ||
| 859 | else => |e| return runner.fail("unable to send message to compiler: {t}", .{e}), | ||
| 860 | }, | ||
| 861 | }; | ||
| 862 | comp.state = .update; | ||
| 863 | update.mtime = update.mtime.addDuration(.fromSeconds(2)); | ||
| 864 | update.num += 1; | ||
| 865 | }, | ||
| 866 | .check => { | ||
| 867 | const check = std.meta.stringToEnum(Compiler.Check, arg.?) orelse | ||
| 868 | return update.manifest_sr.fail(arg.?, "unknown check", .{}); | ||
| 869 | const expected = | ||
| 870 | contents_r.allocRemaining(runner.gpa, .unlimited) catch |err| switch (err) { | ||
| 871 | error.OutOfMemory => |e| return e, | ||
| 872 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 873 | error.StreamTooLong => unreachable, // .unlimited | ||
| 874 | }; | ||
| 875 | defer runner.gpa.free(expected); | ||
| 876 | |||
| 877 | const comp = &(update.compiler orelse return update.manifest_sr.fail( | ||
| 878 | cmd_src_loc, | ||
| 879 | "missing \"#exe\", \"#lib\", or \"#obj\"", | ||
| 880 | .{}, | ||
| 881 | )); | ||
| 882 | if (comp.state != .update) | ||
| 883 | return update.manifest_sr.fail(cmd_src_loc, "missing \"#update\"", .{}); | ||
| 884 | const stderr = comp.mr.reader(1); | ||
| 885 | while (try comp.receiveMessage()) |message| switch (message.tag) { | ||
| 886 | else => {}, // Ignore other messages, | ||
| 887 | .config => { | ||
| 888 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 889 | comp.config = body_r.takeStruct(std.zig.Server.Message.Config, .little) catch | ||
| 890 | unreachable; | ||
| 891 | }, | ||
| 892 | .emit_digest => { | ||
| 893 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 894 | _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch | ||
| 895 | unreachable; | ||
| 896 | const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch | ||
| 897 | unreachable; | ||
| 898 | try comp.checkSuccess(check, expected, update.src_dir, digest); | ||
| 899 | }, | ||
| 900 | .error_bundle => { | ||
| 901 | const eb = std.zig.Server.allocErrorBundle( | ||
| 902 | runner.arena.allocator(), | ||
| 903 | message.body, | ||
| 904 | ) catch |err| switch (err) { | ||
| 905 | error.OutOfMemory => |e| return e, | ||
| 906 | error.EndOfStream => return runner.fail("error bundle truncated", .{}), | ||
| 907 | }; | ||
| 908 | switch (check) { | ||
| 909 | .errors => { | ||
| 910 | var error_aw: std.Io.Writer.Allocating = .init(runner.gpa); | ||
| 911 | defer error_aw.deinit(); | ||
| 912 | eb.renderToWriter(.{ | ||
| 913 | .include_reference_trace = false, | ||
| 914 | .include_source_line = false, | ||
| 915 | .include_log_text = true, | ||
| 916 | }, &error_aw.writer) catch |err| switch (err) { | ||
| 917 | error.WriteFailed => return error.OutOfMemory, | ||
| 918 | }; | ||
| 919 | const actual = error_aw.written(); | ||
| 920 | std.mem.replaceScalar(u8, actual, '\\', '/'); | ||
| 921 | std.testing.expectEqualStrings(expected, actual) catch |err| switch (err) { | ||
| 922 | error.TestExpectedEqual => return runner.fail( | ||
| 923 | "errors did not match expected", | ||
| 924 | .{}, | ||
| 925 | ), | ||
| 926 | }; | ||
| 927 | }, | ||
| 928 | .stdout, .exit, .lldb => if (eb.errorMessageCount() > 0) { | ||
| 929 | eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { | ||
| 930 | error.Canceled => {}, | ||
| 931 | else => {}, | ||
| 932 | }; | ||
| 933 | return runner.fail("unexpected compile errors", .{}); | ||
| 934 | }, | ||
| 935 | } | ||
| 936 | comp.state = .idle; | ||
| 937 | break :cmd; | ||
| 938 | }, | ||
| 939 | }; | ||
| 940 | |||
| 941 | const buffered_stderr = stderr.buffered(); | ||
| 942 | if (buffered_stderr.len > 0) { | ||
| 943 | if (comp.allow_compiler_stderr) { | ||
| 944 | std.log.info("stderr:\n{s}", .{buffered_stderr}); | ||
| 945 | } else { | ||
| 946 | return runner.fail("unexpected stderr:\n{s}", .{buffered_stderr}); | ||
| 947 | } | ||
| 948 | } | ||
| 949 | |||
| 950 | try comp.exit(); | ||
| 951 | return runner.fail("compiler failed to send terminating error_bundle", .{}); | ||
| 952 | }, | ||
| 953 | } | ||
| 954 | assert(contents_r.discardRemaining() catch |err| switch (err) { | ||
| 955 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 956 | } == 0); | ||
| 957 | switch (cmd) { | ||
| 958 | .todo, .skip => {}, | ||
| 959 | else => update.allow_skip = false, | ||
| 960 | } | ||
| 961 | } | ||
| 962 | |||
| 963 | fn spawnCompiler( | ||
| 964 | runner: *Runner, | ||
| 965 | compiler: *?Compiler, | ||
| 966 | src_dir: std.Io.Dir, | ||
| 967 | output_mode: std.lang.OutputMode, | ||
| 968 | args_r: *std.Io.Reader, | ||
| 969 | target: Compiler.Target, | ||
| 970 | ) (std.Io.Reader.ShortError || FailError)!void { | ||
| 971 | const gpa = runner.gpa; | ||
| 972 | const arena = runner.arena.allocator(); | ||
| 973 | if (compiler.*) |*comp| { | ||
| 974 | try comp.exit(); | ||
| 975 | compiler.* = null; | ||
| 976 | } | ||
| 977 | |||
| 978 | var argv: std.ArrayList([]const u8) = .empty; | ||
| 979 | defer argv.deinit(gpa); | ||
| 980 | try argv.appendSlice(gpa, &.{ | ||
| 981 | "zig", | ||
| 982 | switch (output_mode) { | ||
| 983 | .Obj => "build-obj", | ||
| 984 | .Exe => "build-exe", | ||
| 985 | .Lib => "build-lib", | ||
| 986 | }, | ||
| 987 | "-target", | ||
| 988 | target.triple, | ||
| 989 | "--cache-dir", | ||
| 990 | ".zig-cache", | ||
| 991 | }); | ||
| 992 | switch (target.mode) { | ||
| 993 | .whole => {}, | ||
| 994 | .incremental => try argv.append(gpa, "-fincremental"), | ||
| 995 | } | ||
| 996 | var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; | ||
| 997 | if (target.backend) |b| switch (b) { | ||
| 998 | .sema => try argv.append(gpa, "-fno-emit-bin"), | ||
| 999 | .selfhosted => try argv.append(gpa, "-fno-llvm"), | ||
| 1000 | .llvm => try argv.append(gpa, "-fllvm"), | ||
| 1001 | .cbe => try argv.append(gpa, "-ofmt=c"), | ||
| 1002 | } else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. (runner.args.lib_dir orelse | ||
| 1003 | return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{})) | ||
| 1004 | .realPath(runner.io, &path_buffer) catch |err| switch (err) { | ||
| 1005 | error.Canceled => |e| return e, | ||
| 1006 | else => |e| return runner.fail("unable to get lib path: {t}", .{e}), | ||
| 1007 | }] }); | ||
| 1008 | var need: usize = 1; | ||
| 1009 | var root_name: ?[]const u8 = null; | ||
| 1010 | while (true) { | ||
| 1011 | const done = if (args_r.fill(need)) false else |err| switch (err) { | ||
| 1012 | error.ReadFailed => |e| return e, | ||
| 1013 | error.EndOfStream => true, | ||
| 1014 | }; | ||
| 1015 | const buffered = args_r.buffered(); | ||
| 1016 | const start = std.mem.findNone(u8, buffered, "\n ") orelse buffered.len; | ||
| 1017 | const end = std.mem.findAnyPos(u8, buffered, start, "\n ") orelse buffered.len; | ||
| 1018 | if (end - start > 0) { | ||
| 1019 | const arg = try arena.dupe(u8, buffered[start..end]); | ||
| 1020 | if (std.mem.cutPrefix(u8, arg, "-M")) |module| { | ||
| 1021 | var module_name_it = std.mem.splitScalar(u8, module, '='); | ||
| 1022 | const module_name = module_name_it.next().?; | ||
| 1023 | root_name = root_name orelse module_name; | ||
| 1024 | } | ||
| 1025 | try argv.append(gpa, arg); | ||
| 1026 | args_r.toss(end); | ||
| 1027 | need = 1; | ||
| 1028 | } else { | ||
| 1029 | args_r.toss(start); | ||
| 1030 | need = buffered.len - start + 1; | ||
| 1031 | } | ||
| 1032 | if (done) break; | ||
| 1033 | } | ||
| 1034 | try argv.append(gpa, "--listen=-"); | ||
| 1035 | |||
| 1036 | const out_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{ | ||
| 1037 | .root_name = root_name orelse "root", | ||
| 1038 | .cpu_arch = target.resolved.cpu.arch, | ||
| 1039 | .os_tag = target.resolved.os.tag, | ||
| 1040 | .ofmt = target.resolved.ofmt, | ||
| 1041 | .abi = target.resolved.abi, | ||
| 1042 | .output_mode = output_mode, | ||
| 1043 | }); | ||
| 1044 | |||
| 1045 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; | ||
| 1046 | const comp_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "compile {s}", .{ | ||
| 1047 | out_name, | ||
| 1048 | }) catch &name_buffer, 0); | ||
| 1049 | errdefer comp_prog_node.end(); | ||
| 1050 | |||
| 1051 | const child = std.process.spawn(runner.io, .{ | ||
| 1052 | .exe = .{ .file = runner.args.zig_exe orelse | ||
| 1053 | return runner.fail("missing \"--zig=path/to/zig\" arg", .{}) }, | ||
| 1054 | .argv = argv.items, | ||
| 1055 | .cwd = .{ .dir = src_dir }, | ||
| 1056 | .progress_node = comp_prog_node, | ||
| 1057 | .stdin = .pipe, | ||
| 1058 | .stdout = .pipe, | ||
| 1059 | .stderr = .pipe, | ||
| 1060 | }) catch |err| switch (err) { | ||
| 1061 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1062 | else => |e| return runner.fail("unable to spawn compiler: {t}", .{e}), | ||
| 1063 | }; | ||
| 1064 | compiler.* = .{ | ||
| 1065 | .runner = runner, | ||
| 1066 | .prog_node = comp_prog_node, | ||
| 1067 | .allow_compiler_stderr = true, | ||
| 1068 | .output_mode = output_mode, | ||
| 1069 | .state = .idle, | ||
| 1070 | .child = child, | ||
| 1071 | .fw = undefined, | ||
| 1072 | .mr_buffer = undefined, | ||
| 1073 | .mr = undefined, | ||
| 1074 | .client = undefined, | ||
| 1075 | .target = target, | ||
| 1076 | .out_name = out_name, | ||
| 1077 | .config = null, | ||
| 1078 | }; | ||
| 1079 | const comp = &compiler.*.?; | ||
| 1080 | comp.fw = child.stdin.?.writerStreaming(runner.io, &.{}); | ||
| 1081 | comp.mr.init(gpa, runner.io, comp.mr_buffer.toStreams(), &.{ | ||
| 1082 | child.stdout.?, child.stderr.?, | ||
| 1083 | }); | ||
| 1084 | comp.client = .{ .in = comp.mr.reader(0), .out = &comp.fw.interface }; | ||
| 1085 | } | ||
| 1086 | const Compiler = struct { | ||
| 1087 | runner: *Runner, | ||
| 1088 | prog_node: std.Progress.Node, | ||
| 1089 | allow_compiler_stderr: bool, | ||
| 1090 | output_mode: std.lang.OutputMode, | ||
| 1091 | state: enum { idle, update }, | ||
| 1092 | child: std.process.Child, | ||
| 1093 | fw: std.Io.File.Writer, | ||
| 1094 | mr_buffer: std.Io.File.MultiReader.Buffer(2), | ||
| 1095 | mr: std.Io.File.MultiReader, | ||
| 1096 | client: std.zig.Client, | ||
| 1097 | target: Target, | ||
| 1098 | out_name: []const u8, | ||
| 1099 | config: ?std.zig.Server.Message.Config, | ||
| 1100 | |||
| 1101 | const Target = struct { | ||
| 1102 | triple: []const u8, | ||
| 1103 | resolved: std.Target, | ||
| 1104 | mode: Mode, | ||
| 1105 | backend: ?Backend, | ||
| 1106 | |||
| 1107 | const Mode = enum { | ||
| 1108 | whole, | ||
| 1109 | incremental, | ||
| 1110 | }; | ||
| 1111 | |||
| 1112 | const Backend = enum { | ||
| 1113 | /// Run semantic analysis only. Runtime output will not be tested, but we still verify | ||
| 1114 | /// that compilation succeeds. Corresponds to `-fno-emit-bin`. | ||
| 1115 | sema, | ||
| 1116 | /// Use the self-hosted code generation backend for this target. | ||
| 1117 | /// Corresponds to `-fno-llvm -fno-lld`. | ||
| 1118 | selfhosted, | ||
| 1119 | /// Use the LLVM backend. | ||
| 1120 | /// Corresponds to `-fllvm -flld`. | ||
| 1121 | llvm, | ||
| 1122 | /// Use the C backend. The output is compiled with `zig cc`. | ||
| 1123 | /// Corresponds to `-ofmt=c`. | ||
| 1124 | cbe, | ||
| 1125 | }; | ||
| 1126 | }; | ||
| 1127 | |||
| 1128 | fn receiveMessage(comp: *Compiler) FailError!?struct { | ||
| 1129 | tag: std.zig.Server.Message.Tag, | ||
| 1130 | body: []const u8, | ||
| 1131 | } { | ||
| 1132 | const header = comp.client.receiveMessageWithMultiReader( | ||
| 1133 | &comp.mr, | ||
| 1134 | .none, | ||
| 1135 | ) catch |err| switch (err) { | ||
| 1136 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1137 | error.Timeout => unreachable, | ||
| 1138 | error.EndOfStream => return null, | ||
| 1139 | else => |e| return comp.runner.fail("unable to receive message: {t}", .{e}), | ||
| 1140 | }; | ||
| 1141 | const body = comp.client.in.take(header.bytes_len) catch unreachable; | ||
| 1142 | const stderr = comp.mr.reader(1); | ||
| 1143 | if (stderr.bufferedLen() > 0) { | ||
| 1144 | if (comp.allow_compiler_stderr) { | ||
| 1145 | std.log.info("{t} stderr:\n{s}", .{ header.tag, stderr.buffered() }); | ||
| 1146 | } else { | ||
| 1147 | return comp.runner.fail("{t} unexpected stderr:\n{s}", .{ | ||
| 1148 | header.tag, stderr.buffered(), | ||
| 1149 | }); | ||
| 1150 | } | ||
| 1151 | stderr.tossBuffered(); | ||
| 1152 | } | ||
| 1153 | return .{ .tag = header.tag, .body = body }; | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | const Check = enum { errors, stdout, exit, lldb }; | ||
| 1157 | fn checkSuccess( | ||
| 1158 | comp: *Compiler, | ||
| 1159 | check: Check, | ||
| 1160 | expected: []const u8, | ||
| 1161 | src_dir: std.Io.Dir, | ||
| 1162 | digest: *const std.Build.Cache.BinDigest, | ||
| 1163 | ) FailError!void { | ||
| 1164 | const runner = comp.runner; | ||
| 1165 | const gpa = runner.gpa; | ||
| 1166 | const arena = runner.arena.allocator(); | ||
| 1167 | const io = runner.io; | ||
| 1168 | const target = &comp.target.resolved; | ||
| 1169 | const config = &comp.config.?; | ||
| 1170 | const out_dir = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ | ||
| 1171 | "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); | ||
| 1172 | const out_path = try std.Io.Dir.path.join(arena, &.{ out_dir, comp.out_name }); | ||
| 1173 | const bin_path = switch (comp.target.backend.?) { | ||
| 1174 | .sema => return, | ||
| 1175 | .selfhosted, .llvm => out_path, | ||
| 1176 | .cbe => try comp.compileC(src_dir, out_path), | ||
| 1177 | }; | ||
| 1178 | switch (check) { | ||
| 1179 | .errors => return, | ||
| 1180 | .stdout, .exit, .lldb => {}, | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | var argv: std.ArrayList([]const u8) = .initBuffer(try arena.alloc([]const u8, 4)); | ||
| 1184 | var environ_map: std.process.Environ.Map = .init(gpa); | ||
| 1185 | defer environ_map.deinit(); | ||
| 1186 | const need_cross_libc = | ||
| 1187 | target.os.tag == .linux and config.flags.link_libc and config.flags.link_mode == .dynamic; | ||
| 1188 | const use_executor = use_executor: switch (std.zig.system.getExternalExecutor(io, target, .{ | ||
| 1189 | .host_cpu_arch = runner.host.cpu.arch, | ||
| 1190 | .host_os_tag = runner.host.os.tag, | ||
| 1191 | .qemu_fixes_dl = need_cross_libc and runner.args.libc_runtimes_dir != null, | ||
| 1192 | .link_mode = config.flags.link_mode, | ||
| 1193 | .link_libc = config.flags.link_libc, | ||
| 1194 | })) { | ||
| 1195 | .bad_dl, .bad_os_or_cpu => { | ||
| 1196 | // This binary cannot be executed on this host. | ||
| 1197 | if (!runner.args.quiet) std.log.warn("skipping execution because host {q} cannot " ++ | ||
| 1198 | "execute binaries for foreign target {q}", .{ | ||
| 1199 | try runner.host.zigTriple(arena), comp.target.triple, | ||
| 1200 | }); | ||
| 1201 | return; | ||
| 1202 | }, | ||
| 1203 | .native => false, | ||
| 1204 | .darling => |executor| if (runner.args.enable_darling) { | ||
| 1205 | argv.appendAssumeCapacity(executor); | ||
| 1206 | break :use_executor true; | ||
| 1207 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1208 | .qemu => |executor| if (runner.args.enable_qemu) { | ||
| 1209 | argv.appendAssumeCapacity(executor); | ||
| 1210 | if (need_cross_libc) { | ||
| 1211 | const libc_runtimes_dir = runner.args.libc_runtimes_dir orelse | ||
| 1212 | continue :use_executor .bad_os_or_cpu; | ||
| 1213 | var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; | ||
| 1214 | const libc_runtimes_path = path_buffer[0 .. libc_runtimes_dir.realPath( | ||
| 1215 | runner.io, | ||
| 1216 | &path_buffer, | ||
| 1217 | ) catch |err| switch (err) { | ||
| 1218 | error.Canceled => |e| return e, | ||
| 1219 | else => |e| return runner.fail("unable to get libc runtimes path: {t}", .{e}), | ||
| 1220 | }]; | ||
| 1221 | argv.appendSliceAssumeCapacity(&.{ "-L", try std.Io.Dir.path.join(arena, &.{ | ||
| 1222 | libc_runtimes_path, | ||
| 1223 | try if (target.isGnuLibC()) std.zig.target.glibcRuntimeTriple( | ||
| 1224 | arena, | ||
| 1225 | target.cpu.arch, | ||
| 1226 | target.os.tag, | ||
| 1227 | target.abi, | ||
| 1228 | ) else if (target.isMuslLibC()) std.zig.target.muslRuntimeTriple( | ||
| 1229 | arena, | ||
| 1230 | target.cpu.arch, | ||
| 1231 | target.abi, | ||
| 1232 | ) else unreachable, | ||
| 1233 | }) }); | ||
| 1234 | } | ||
| 1235 | break :use_executor true; | ||
| 1236 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1237 | .rosetta => if (runner.args.enable_rosetta) | ||
| 1238 | true | ||
| 1239 | else | ||
| 1240 | continue :use_executor .bad_os_or_cpu, | ||
| 1241 | .wine => |executor| if (runner.args.enable_wine) { | ||
| 1242 | if (!environ_map.contains("WINEDEBUG")) try environ_map.put("WINEDEBUG", "-all"); | ||
| 1243 | argv.appendAssumeCapacity(executor); | ||
| 1244 | break :use_executor true; | ||
| 1245 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1246 | .wasmtime => |executor| if (runner.args.enable_wasmtime) { | ||
| 1247 | if (!environ_map.contains("WASMTIME_BACKTRACE_DETAILS")) | ||
| 1248 | try environ_map.put("WASMTIME_BACKTRACE_DETAILS", "1"); | ||
| 1249 | argv.appendSliceAssumeCapacity(&.{ executor, "--dir=.", "-Sinherit-env" }); | ||
| 1250 | break :use_executor true; | ||
| 1251 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1252 | }; | ||
| 1253 | argv.appendAssumeCapacity(bin_path); | ||
| 1254 | |||
| 1255 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; | ||
| 1256 | const run_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "run {s}", .{ | ||
| 1257 | comp.out_name, | ||
| 1258 | }) catch &name_buffer, 0); | ||
| 1259 | defer run_prog_node.end(); | ||
| 1260 | |||
| 1261 | const bin_file = src_dir.openFile(io, bin_path, .{}) catch |err| switch (err) { | ||
| 1262 | error.Canceled => |e| return e, | ||
| 1263 | else => |e| return runner.fail("unable to open {q}: {t}", .{ bin_path, e }), | ||
| 1264 | }; | ||
| 1265 | defer bin_file.close(io); | ||
| 1266 | switch (check) { | ||
| 1267 | .errors => unreachable, | ||
| 1268 | .stdout, .exit => { | ||
| 1269 | const result = std.process.run(gpa, io, .{ | ||
| 1270 | .exe = if (use_executor) .search else .{ .file = bin_file }, | ||
| 1271 | .argv = argv.items, | ||
| 1272 | .cwd = .{ .dir = src_dir }, | ||
| 1273 | .environ_map = &environ_map, | ||
| 1274 | .progress_node = run_prog_node, | ||
| 1275 | }) catch |err| if (use_executor) { | ||
| 1276 | // Chances are the foreign executor isn't available. Skip this evaluation. | ||
| 1277 | if (!runner.args.quiet) std.log.warn( | ||
| 1278 | "skipping execution of {q} via executor for foreign target {q}: {t}", | ||
| 1279 | .{ bin_path, comp.target.triple, err }, | ||
| 1280 | ); | ||
| 1281 | return; | ||
| 1282 | } else return runner.fail("unable to run the generated executable {q}: {t}", .{ | ||
| 1283 | bin_path, err, | ||
| 1284 | }); | ||
| 1285 | defer { | ||
| 1286 | gpa.free(result.stdout); | ||
| 1287 | gpa.free(result.stderr); | ||
| 1288 | } | ||
| 1289 | switch (result.term) { | ||
| 1290 | .exited => |code| switch (check) { | ||
| 1291 | .errors, .lldb => unreachable, | ||
| 1292 | .stdout => { | ||
| 1293 | if (code != 0) return runner.fail( | ||
| 1294 | "generated executable {q} failed with code {d}", | ||
| 1295 | .{ bin_path, code }, | ||
| 1296 | ); | ||
| 1297 | std.testing.expectEqualStrings( | ||
| 1298 | expected, | ||
| 1299 | result.stdout, | ||
| 1300 | ) catch |err| switch (err) { | ||
| 1301 | error.TestExpectedEqual => return runner.fail( | ||
| 1302 | "stdout did not match expected", | ||
| 1303 | .{}, | ||
| 1304 | ), | ||
| 1305 | }; | ||
| 1306 | }, | ||
| 1307 | .exit => { | ||
| 1308 | var actual_code_buffer: [std.fmt.count("{d}", .{std.math.maxInt(u8)})]u8 = | ||
| 1309 | undefined; | ||
| 1310 | std.testing.expectEqualStrings( | ||
| 1311 | expected, | ||
| 1312 | std.mem.print(&actual_code_buffer, "{d}", .{code}) catch unreachable, | ||
| 1313 | ) catch |err| switch (err) { | ||
| 1314 | error.TestExpectedEqual => return runner.fail( | ||
| 1315 | "exit code did not match expected", | ||
| 1316 | .{}, | ||
| 1317 | ), | ||
| 1318 | }; | ||
| 1319 | }, | ||
| 1320 | }, | ||
| 1321 | .signal => |sig| return runner.fail( | ||
| 1322 | "generated executable {q} terminated with signal {t}", | ||
| 1323 | .{ bin_path, sig }, | ||
| 1324 | ), | ||
| 1325 | .stopped => |sig| return runner.fail( | ||
| 1326 | "generated executable {q} stopped with signal {t}", | ||
| 1327 | .{ bin_path, sig }, | ||
| 1328 | ), | ||
| 1329 | .unknown => return runner.fail( | ||
| 1330 | "generated executable {q} terminated unexpectedly", | ||
| 1331 | .{bin_path}, | ||
| 1332 | ), | ||
| 1333 | } | ||
| 1334 | if (!use_executor and result.stderr.len > 0) { | ||
| 1335 | std.log.err("generated executable {q} had unexpected stderr:\n{s}", .{ | ||
| 1336 | bin_path, result.stderr, | ||
| 1337 | }); | ||
| 1338 | } | ||
| 1339 | }, | ||
| 1340 | .lldb => {}, | ||
| 1341 | } | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | fn compileC(parent_comp: *Compiler, src_dir: std.Io.Dir, c_path: []const u8) FailError![]const u8 { | ||
| 1345 | const runner = parent_comp.runner; | ||
| 1346 | const arena = runner.arena.allocator(); | ||
| 1347 | var compiler: ?Compiler = null; | ||
| 1348 | var args_r: std.Io.Reader = .fixed(c_path); | ||
| 1349 | parent_comp.runner.spawnCompiler(&compiler, src_dir, parent_comp.output_mode, &args_r, .{ | ||
| 1350 | .triple = parent_comp.target.triple, | ||
| 1351 | .resolved = resolved: { | ||
| 1352 | var resolved = parent_comp.target.resolved; | ||
| 1353 | resolved.ofmt = .default(resolved.os.tag, resolved.cpu.arch); | ||
| 1354 | break :resolved resolved; | ||
| 1355 | }, | ||
| 1356 | .mode = .whole, | ||
| 1357 | .backend = null, | ||
| 1358 | }) catch |err| switch (err) { | ||
| 1359 | error.Canceled, error.OutOfMemory, error.AlreadyReported => |e| return e, | ||
| 1360 | error.ReadFailed => unreachable, // .fixed | ||
| 1361 | }; | ||
| 1362 | const comp = &compiler.?; | ||
| 1363 | comp.client.serveBodylessMessage(.update) catch |err| switch (err) { | ||
| 1364 | error.WriteFailed => switch (comp.fw.err.?) { | ||
| 1365 | error.Canceled => |e| return e, | ||
| 1366 | else => |e| return runner.fail("unable to send message: {t}", .{e}), | ||
| 1367 | }, | ||
| 1368 | }; | ||
| 1369 | comp.state = .update; | ||
| 1370 | var out_path: ?[]const u8 = null; | ||
| 1371 | while (try comp.receiveMessage()) |message| switch (message.tag) { | ||
| 1372 | else => {}, // Ignore other messages, | ||
| 1373 | .config => { | ||
| 1374 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 1375 | comp.config = | ||
| 1376 | body_r.takeStruct(std.zig.Server.Message.Config, .little) catch unreachable; | ||
| 1377 | }, | ||
| 1378 | .emit_digest => { | ||
| 1379 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 1380 | _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; | ||
| 1381 | const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch unreachable; | ||
| 1382 | |||
| 1383 | const out_dir = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ | ||
| 1384 | "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); | ||
| 1385 | out_path = try std.Io.Dir.path.join(arena, &.{ out_dir, comp.out_name }); | ||
| 1386 | }, | ||
| 1387 | .error_bundle => { | ||
| 1388 | const eb = std.zig.Server.allocErrorBundle( | ||
| 1389 | runner.arena.allocator(), | ||
| 1390 | message.body, | ||
| 1391 | ) catch |err| switch (err) { | ||
| 1392 | error.OutOfMemory => |e| return e, | ||
| 1393 | error.EndOfStream => return runner.fail("error bundle truncated", .{}), | ||
| 1394 | }; | ||
| 1395 | if (eb.errorMessageCount() > 0) { | ||
| 1396 | try runner.eb_wip.addBundleAsRoots(eb); | ||
| 1397 | return runner.fail("unexpected compile errors", .{}); | ||
| 1398 | } | ||
| 1399 | comp.state = .idle; | ||
| 1400 | break; | ||
| 1401 | }, | ||
| 1402 | }; | ||
| 1403 | try comp.exit(); | ||
| 1404 | return out_path.?; | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | fn exit(comp: *Compiler) FailError!void { | ||
| 1408 | const runner = comp.runner; | ||
| 1409 | comp.client.serveBodylessMessage(.exit) catch |err| switch (err) { | ||
| 1410 | error.WriteFailed => switch (comp.fw.err.?) { | ||
| 1411 | error.Canceled => |e| return e, | ||
| 1412 | error.BrokenPipe => {}, | ||
| 1413 | else => |e| return comp.runner.fail("unable to send exit: {t}", .{e}), | ||
| 1414 | }, | ||
| 1415 | }; | ||
| 1416 | comp.child.stdin.?.close(runner.io); | ||
| 1417 | comp.child.stdin = null; | ||
| 1418 | while (try comp.receiveMessage()) |_| {} | ||
| 1419 | if (comp.client.in.bufferedLen() > 0) return runner.fail("unable to receive messages", .{}); | ||
| 1420 | const term = comp.child.wait(runner.io) catch |err| | ||
| 1421 | return runner.fail("child process failed: {t}", .{err}); | ||
| 1422 | comp.prog_node.end(); | ||
| 1423 | switch (term) { | ||
| 1424 | .exited => |code| if (code != 0) | ||
| 1425 | return runner.fail("compiler failed with code {d}", .{code}), | ||
| 1426 | .signal => |sig| return runner.fail("compiler terminated with signal {t}", .{sig}), | ||
| 1427 | .stopped => |sig| return runner.fail("compiler stopped unexpectedly with signal {t}", .{ | ||
| 1428 | sig, | ||
| 1429 | }), | ||
| 1430 | .unknown => return runner.fail("compiler terminated unexpectedly", .{}), | ||
| 1431 | } | ||
| 1432 | } | ||
| 1433 | }; | ||
| 1434 | |||
| 1435 | const ClientArgs = struct { | ||
| 1436 | test_file: std.Io.File, | ||
| 1437 | test_kind: std.Io.File.Kind, | ||
| 1438 | zig_exe: std.Io.File, | ||
| 1439 | lib_dir: std.Io.Dir, | ||
| 1440 | src_dir: std.Io.Dir, | ||
| 1441 | targets: std.ArrayList([]const u8), | ||
| 1442 | keep_src: bool, | ||
| 1443 | libc_runtimes_dir: ?std.Io.Dir, | ||
| 1444 | enable_darling: bool, | ||
| 1445 | enable_qemu: bool, | ||
| 1446 | enable_rosetta: bool, | ||
| 1447 | enable_wine: bool, | ||
| 1448 | enable_wasmtime: bool, | ||
| 1449 | quiet: bool, | ||
| 1450 | }; | ||
| 1451 | pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelable)!u8 { | ||
| 1452 | const fatal = struct { | ||
| 1453 | fn fatal(comptime fmt: []const u8, args: anytype) noreturn { | ||
| 1454 | std.log.err(fmt, args); | ||
| 1455 | std.process.exit(1); | ||
| 1456 | } | ||
| 1457 | }.fatal; | ||
| 1458 | const arena = init.arena.allocator(); | ||
| 1459 | |||
| 1460 | var test_path_arg: ?[]const u8 = null; | ||
| 1461 | var zig_path_arg: ?[]const u8 = null; | ||
| 1462 | var lib_path_arg: ?[]const u8 = null; | ||
| 1463 | var libc_runtimes_path_arg: ?[]const u8 = null; | ||
| 1464 | var args: ClientArgs = .{ | ||
| 1465 | .test_file = undefined, | ||
| 1466 | .test_kind = undefined, | ||
| 1467 | .zig_exe = undefined, | ||
| 1468 | .lib_dir = undefined, | ||
| 1469 | .src_dir = undefined, | ||
| 1470 | .targets = .empty, | ||
| 1471 | .keep_src = false, | ||
| 1472 | .libc_runtimes_dir = null, | ||
| 1473 | .enable_darling = false, | ||
| 1474 | .enable_qemu = false, | ||
| 1475 | .enable_rosetta = false, | ||
| 1476 | .enable_wine = false, | ||
| 1477 | .enable_wasmtime = false, | ||
| 1478 | .quiet = false, | ||
| 1479 | }; | ||
| 1480 | defer args.targets.deinit(init.gpa); | ||
| 1481 | |||
| 1482 | var arg_it = try init.minimal.args.iterateAllocator(arena); | ||
| 1483 | const self = arg_it.next().?; | ||
| 1484 | while (arg_it.next()) |arg| { | ||
| 1485 | if (std.mem.eql(u8, arg, "--listen=-")) { | ||
| 1486 | var stdin_buffer: [512]u8 = undefined; | ||
| 1487 | var stdin: std.Io.File.Reader = .initStreaming(.stdin(), init.io, &stdin_buffer); | ||
| 1488 | var stdout_buffer: [512]u8 = undefined; | ||
| 1489 | var stdout: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer); | ||
| 1490 | var runner: Runner = .{ | ||
| 1491 | .gpa = init.gpa, | ||
| 1492 | .arena = init.arena, | ||
| 1493 | .io = init.io, | ||
| 1494 | .prog_node = std.Progress.start(init.io, .{}), | ||
| 1495 | .args = .{ | ||
| 1496 | .@"test" = null, | ||
| 1497 | .manifest_file = null, | ||
| 1498 | .zig_exe = null, | ||
| 1499 | .lib_dir = null, | ||
| 1500 | .src_dir = null, | ||
| 1501 | .targets = .empty, | ||
| 1502 | .target_bytes = .empty, | ||
| 1503 | .libc_runtimes_dir = null, | ||
| 1504 | .enable_darling = false, | ||
| 1505 | .enable_qemu = false, | ||
| 1506 | .enable_rosetta = false, | ||
| 1507 | .enable_wasmtime = false, | ||
| 1508 | .enable_wine = false, | ||
| 1509 | .quiet = false, | ||
| 1510 | }, | ||
| 1511 | .host = std.zig.system.resolveTargetQuery(init.io, .{}) catch |err| switch (err) { | ||
| 1512 | error.Canceled => |e| return e, | ||
| 1513 | else => |e| fatal("unable to resolve host target: {t}", .{e}), | ||
| 1514 | }, | ||
| 1515 | .server = .{ | ||
| 1516 | .in = &stdin.interface, | ||
| 1517 | .out = &stdout.interface, | ||
| 1518 | }, | ||
| 1519 | .eb_wip = try .init(init.gpa), | ||
| 1520 | }; | ||
| 1521 | defer runner.deinit(); | ||
| 1522 | switch (runner.runServer()) { | ||
| 1523 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1524 | error.ServerReadFailed => switch (stdin.err.?) { | ||
| 1525 | error.Canceled => |e| return e, | ||
| 1526 | else => |e| fatal("unable to receive message: {t}", .{e}), | ||
| 1527 | }, | ||
| 1528 | error.ServerWriteFailed => switch (stdout.err.?) { | ||
| 1529 | error.Canceled => |e| return e, | ||
| 1530 | else => |e| fatal("unable to send message: {t}", .{e}), | ||
| 1531 | }, | ||
| 1532 | error.ServerEndOfStream => fatal("no more messages", .{}), | ||
| 1533 | error.AlreadyReported => { | ||
| 1534 | var eb = try runner.eb_wip.toOwnedBundle(""); | ||
| 1535 | defer eb.deinit(init.gpa); | ||
| 1536 | eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { | ||
| 1537 | error.Canceled => |e| return e, | ||
| 1538 | else => {}, | ||
| 1539 | }; | ||
| 1540 | std.process.exit(1); | ||
| 1541 | }, | ||
| 1542 | } | ||
| 1543 | } else if (std.mem.eql(u8, arg, "--zig")) { | ||
| 1544 | zig_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1545 | } else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| { | ||
| 1546 | zig_path_arg = zig_path; | ||
| 1547 | } else if (std.mem.eql(u8, arg, "--lib")) { | ||
| 1548 | lib_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1549 | } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| { | ||
| 1550 | lib_path_arg = lib_path; | ||
| 1551 | } else if (std.mem.eql(u8, arg, "--target")) { | ||
| 1552 | try args.targets.append( | ||
| 1553 | init.gpa, | ||
| 1554 | try arena.dupe(u8, arg_it.next() orelse fatal("missing arg after {q}", .{arg})), | ||
| 1555 | ); | ||
| 1556 | } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| { | ||
| 1557 | try args.targets.append(init.gpa, try arena.dupe(u8, target)); | ||
| 1558 | } else if (std.mem.eql(u8, arg, "--keep-src")) { | ||
| 1559 | args.keep_src = true; | ||
| 1560 | } else if (std.mem.eql(u8, arg, "--libc-runtimes")) { | ||
| 1561 | libc_runtimes_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1562 | } else if (std.mem.cutPrefix(u8, arg, "--libc-runtimes=")) |libc_runtimes_path| { | ||
| 1563 | libc_runtimes_path_arg = libc_runtimes_path; | ||
| 1564 | } else if (std.mem.eql(u8, arg, "-fdarling")) { | ||
| 1565 | args.enable_darling = true; | ||
| 1566 | } else if (std.mem.eql(u8, arg, "-fqemu")) { | ||
| 1567 | args.enable_qemu = true; | ||
| 1568 | } else if (std.mem.eql(u8, arg, "-frosetta")) { | ||
| 1569 | args.enable_rosetta = true; | ||
| 1570 | } else if (std.mem.eql(u8, arg, "-fwine")) { | ||
| 1571 | args.enable_wine = true; | ||
| 1572 | } else if (std.mem.eql(u8, arg, "-fwasmtime")) { | ||
| 1573 | args.enable_wasmtime = true; | ||
| 1574 | } else if (std.mem.eql(u8, arg, "--quiet")) { | ||
| 1575 | args.quiet = true; | ||
| 1576 | } else { | ||
| 1577 | if (test_path_arg) |_| fatal("unexpected arg {q}", .{arg}); | ||
| 1578 | test_path_arg = arg; | ||
| 1579 | } | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | const test_path = test_path_arg orelse fatal("missing \"path/to/test\" arg", .{}); | ||
| 1583 | const zig_path = zig_path_arg orelse fatal("missing \"--zig=path/to/zig\" arg", .{}); | ||
| 1584 | const lib_path = lib_path_arg orelse fatal("missing \"--lib=path/to/lib\" arg", .{}); | ||
| 1585 | |||
| 1586 | const cwd: std.Io.Dir = .cwd(); | ||
| 1587 | args.test_file = cwd.openFile(init.io, test_path, .{ | ||
| 1588 | .allow_directory = true, | ||
| 1589 | }) catch |err| switch (err) { | ||
| 1590 | error.Canceled => |e| return e, | ||
| 1591 | else => |e| fatal("unable to open {q}: {t}", .{ test_path, e }), | ||
| 1592 | }; | ||
| 1593 | defer args.test_file.close(init.io); | ||
| 1594 | args.test_kind = (args.test_file.stat(init.io) catch |err| switch (err) { | ||
| 1595 | error.Canceled => |e| return e, | ||
| 1596 | else => |e| fatal("unable to stat {q}: {t}", .{ test_path, e }), | ||
| 1597 | }).kind; | ||
| 1598 | args.zig_exe = cwd.openFile(init.io, zig_path, .{}) catch |err| switch (err) { | ||
| 1599 | error.Canceled => |e| return e, | ||
| 1600 | else => |e| fatal("unable to open {q}: {t}", .{ zig_path, e }), | ||
| 1601 | }; | ||
| 1602 | defer args.zig_exe.close(init.io); | ||
| 1603 | args.lib_dir = cwd.openDir(init.io, lib_path, .{}) catch |err| switch (err) { | ||
| 1604 | error.Canceled => |e| return e, | ||
| 1605 | else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }), | ||
| 1606 | }; | ||
| 1607 | defer args.lib_dir.close(init.io); | ||
| 1608 | if (libc_runtimes_path_arg) |libc_runtimes_path| args.libc_runtimes_dir = cwd.openDir( | ||
| 1609 | init.io, | ||
| 1610 | libc_runtimes_path, | ||
| 1611 | .{}, | ||
| 1612 | ) catch |err| switch (err) { | ||
| 1613 | error.Canceled => |e| return e, | ||
| 1614 | else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }), | ||
| 1615 | }; | ||
| 1616 | defer if (args.libc_runtimes_dir) |libc_runtimes_dir| libc_runtimes_dir.close(init.io); | ||
| 1617 | |||
| 1618 | const src_dir_path = "src_" ++ std.fmt.hex(rand_int: { | ||
| 1619 | var rand_int: u64 = undefined; | ||
| 1620 | init.io.random(@ptrCast(&rand_int)); | ||
| 1621 | break :rand_int rand_int; | ||
| 1622 | }); | ||
| 1623 | args.src_dir = cwd.createDirPathOpen(init.io, src_dir_path, .{}) catch |err| switch (err) { | ||
| 1624 | error.Canceled => |e| return e, | ||
| 1625 | else => |e| fatal("unable to create {q}: {t}", .{ src_dir_path, e }), | ||
| 1626 | }; | ||
| 1627 | defer { | ||
| 1628 | args.src_dir.close(init.io); | ||
| 1629 | if (!args.keep_src) cwd.deleteTree(init.io, src_dir_path) catch |err| { | ||
| 1630 | std.log.warn("unable to delete tree {q}: {t}", .{ src_dir_path, err }); | ||
| 1631 | }; | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | var child = std.process.spawn(init.io, .{ | ||
| 1635 | .argv = &.{ self, "--listen=-" }, | ||
| 1636 | .stdin = .pipe, | ||
| 1637 | .stdout = .pipe, | ||
| 1638 | .inherit_dirs = &.{ args.lib_dir, args.src_dir }, | ||
| 1639 | .inherit_files = &.{ args.zig_exe, args.test_file }, | ||
| 1640 | }) catch |err| switch (err) { | ||
| 1641 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1642 | else => |e| fatal("unable to spawn runner: {t}", .{e}), | ||
| 1643 | }; | ||
| 1644 | |||
| 1645 | var child_reader_buffer: [512]u8 = undefined; | ||
| 1646 | var child_reader = child.stdout.?.readerStreaming(init.io, &child_reader_buffer); | ||
| 1647 | var child_writer_buffer: [512]u8 = undefined; | ||
| 1648 | var child_writer = child.stdin.?.writerStreaming(init.io, &child_writer_buffer); | ||
| 1649 | var client: std.zig.Client = .{ | ||
| 1650 | .in = &child_reader.interface, | ||
| 1651 | .out = &child_writer.interface, | ||
| 1652 | }; | ||
| 1653 | const success = runClient(init.gpa, arena, init.io, &client, &args) catch |err| switch (err) { | ||
| 1654 | error.OutOfMemory => |e| return e, | ||
| 1655 | error.ReadFailed => switch (child_reader.err.?) { | ||
| 1656 | error.Canceled => |e| return e, | ||
| 1657 | else => |e| fatal("unable to receive message: {t}", .{e}), | ||
| 1658 | }, | ||
| 1659 | error.WriteFailed => switch (child_writer.err.?) { | ||
| 1660 | error.Canceled => |e| return e, | ||
| 1661 | else => |e| fatal("unable to send message: {t}", .{e}), | ||
| 1662 | }, | ||
| 1663 | error.EndOfStream => fatal("no more messages", .{}), | ||
| 1664 | }; | ||
| 1665 | _ = child.wait(init.io) catch |err| switch (err) { | ||
| 1666 | error.Canceled => |e| return e, | ||
| 1667 | else => |e| fatal("unable to wait for runner: {t}", .{e}), | ||
| 1668 | }; | ||
| 1669 | return if (success) 0 else 1; | ||
| 1670 | } | ||
| 1671 | fn runClient( | ||
| 1672 | gpa: std.mem.Allocator, | ||
| 1673 | arena: std.mem.Allocator, | ||
| 1674 | io: std.Io, | ||
| 1675 | client: *std.zig.Client, | ||
| 1676 | args: *const ClientArgs, | ||
| 1677 | ) (std.mem.Allocator.Error || std.Io.Reader.Error || std.Io.Writer.Error)!bool { | ||
| 1678 | var stdout_buffer: [512]u8 = undefined; | ||
| 1679 | var stdout_fw: std.Io.File.Writer = .initStreaming(.stdout(), io, &stdout_buffer); | ||
| 1680 | const stdout = &stdout_fw.interface; | ||
| 1681 | var stderr_buffer: [512]u8 = undefined; | ||
| 1682 | var stderr_fw: std.Io.File.Writer = .initStreaming(.stderr(), io, &stderr_buffer); | ||
| 1683 | const stderr = &stderr_fw.interface; | ||
| 1684 | |||
| 1685 | var argv: std.ArrayList(u8) = .empty; | ||
| 1686 | defer argv.deinit(gpa); | ||
| 1687 | const Arg = std.zig.Client.Message.Arg; | ||
| 1688 | |||
| 1689 | try argv.append(gpa, @backingInt(@as(Arg, switch (args.test_kind) { | ||
| 1690 | else => unreachable, | ||
| 1691 | .file => .input_file, | ||
| 1692 | .directory => .input_dir, | ||
| 1693 | }))); | ||
| 1694 | try argv.appendSlice(gpa, @ptrCast(&args.test_file.handle)); | ||
| 1695 | |||
| 1696 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 1697 | try argv.appendSlice(gpa, "--zig="); | ||
| 1698 | try argv.append(gpa, 0); | ||
| 1699 | try argv.append(gpa, @backingInt(Arg.input_file)); | ||
| 1700 | try argv.appendSlice(gpa, @ptrCast(&args.zig_exe.handle)); | ||
| 1701 | |||
| 1702 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 1703 | try argv.appendSlice(gpa, "--lib="); | ||
| 1704 | try argv.append(gpa, 0); | ||
| 1705 | try argv.append(gpa, @backingInt(Arg.input_dir)); | ||
| 1706 | try argv.appendSlice(gpa, @ptrCast(&args.lib_dir.handle)); | ||
| 1707 | |||
| 1708 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 1709 | try argv.appendSlice(gpa, "--src="); | ||
| 1710 | try argv.append(gpa, 0); | ||
| 1711 | try argv.append(gpa, @backingInt(Arg.output_dir)); | ||
| 1712 | try argv.appendSlice(gpa, @ptrCast(&args.src_dir.handle)); | ||
| 1713 | |||
| 1714 | for (args.targets.items) |target| { | ||
| 1715 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 1716 | try argv.appendSlice(gpa, "--target"); | ||
| 1717 | try argv.append(gpa, 0); | ||
| 1718 | |||
| 1719 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 1720 | try argv.appendSlice(gpa, target); | ||
| 1721 | try argv.append(gpa, 0); | ||
| 1722 | } | ||
| 1723 | |||
| 1724 | if (args.libc_runtimes_dir) |libc_runtimes_dir| { | ||
| 1725 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 1726 | try argv.appendSlice(gpa, "--libc-runtimes="); | ||
| 1727 | try argv.append(gpa, 0); | ||
| 1728 | try argv.append(gpa, @backingInt(Arg.input_dir)); | ||
| 1729 | try argv.appendSlice(gpa, @ptrCast(&libc_runtimes_dir.handle)); | ||
| 1730 | } | ||
| 1731 | if (args.enable_darling) { | ||
| 1732 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 1733 | try argv.appendSlice(gpa, "-fdarling"); | ||
| 1734 | try argv.append(gpa, 0); | ||
| 1735 | } | ||
| 1736 | if (args.enable_qemu) { | ||
| 1737 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 1738 | try argv.appendSlice(gpa, "-fqemu"); | ||
| 1739 | try argv.append(gpa, 0); | ||
| 1740 | } | ||
| 1741 | if (args.enable_rosetta) { | ||
| 1742 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 1743 | try argv.appendSlice(gpa, "-frosetta"); | ||
| 1744 | try argv.append(gpa, 0); | ||
| 1745 | } | ||
| 1746 | if (args.enable_wine) { | ||
| 1747 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 1748 | try argv.appendSlice(gpa, "-fwine"); | ||
| 1749 | try argv.append(gpa, 0); | ||
| 1750 | } | ||
| 1751 | if (args.enable_wasmtime) { | ||
| 1752 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 1753 | try argv.appendSlice(gpa, "-fwasmtime"); | ||
| 1754 | try argv.append(gpa, 0); | ||
| 1755 | } | ||
| 1756 | |||
| 1757 | if (args.quiet) { | ||
| 1758 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 1759 | try argv.appendSlice(gpa, "--quiet"); | ||
| 1760 | try argv.append(gpa, 0); | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | try client.serveMessageHeader(.{ | ||
| 1764 | .tag = .args, | ||
| 1765 | .bytes_len = @intCast(argv.items.len), | ||
| 1766 | }); | ||
| 1767 | try client.out.writeAll(argv.items); | ||
| 1768 | |||
| 1769 | var success = true; | ||
| 1770 | var metadata: struct { | ||
| 1771 | index: u32, | ||
| 1772 | len: u32, | ||
| 1773 | names: []u32, | ||
| 1774 | expected_panic_msgs: []u32, | ||
| 1775 | string_bytes: []u8, | ||
| 1776 | |||
| 1777 | fn name(m: *@This()) []const u8 { | ||
| 1778 | return std.mem.sliceTo(m.string_bytes[m.names[m.index - 1]..], 0); | ||
| 1779 | } | ||
| 1780 | |||
| 1781 | fn expectedPanicMsg(m: *@This()) ?[:0]const u8 { | ||
| 1782 | return switch (m.expected_panic_msgs[m.index - 1]) { | ||
| 1783 | 0 => null, | ||
| 1784 | else => |offset| m.string_bytes[offset..std.mem.findScalarPos( | ||
| 1785 | u8, | ||
| 1786 | m.string_bytes, | ||
| 1787 | offset, | ||
| 1788 | 0, | ||
| 1789 | ).? :0], | ||
| 1790 | }; | ||
| 1791 | } | ||
| 1792 | |||
| 1793 | fn next(m: *@This()) ?u32 { | ||
| 1794 | if (m.len - m.index == 0) return null; | ||
| 1795 | defer m.index += 1; | ||
| 1796 | return m.index; | ||
| 1797 | } | ||
| 1798 | } = .{ .index = 0, .len = 0, .names = &.{}, .expected_panic_msgs = &.{}, .string_bytes = &.{} }; | ||
| 1799 | try client.serveBodylessMessage(.query_test_metadata); | ||
| 1800 | while (true) { | ||
| 1801 | const hdr = try client.receiveMessage(); | ||
| 1802 | switch (hdr.tag) { | ||
| 1803 | else => try client.in.discardAll(hdr.bytes_len), | ||
| 1804 | .zig_version => { | ||
| 1805 | const actual_version = try client.in.take(hdr.bytes_len); | ||
| 1806 | if (!std.mem.eql(u8, zig_version_string, actual_version)) { | ||
| 1807 | try stderr.print( | ||
| 1808 | "error: zig version mismatch compiler test runner vs compiler: {q} vs {q}\n", | ||
| 1809 | .{ zig_version_string, actual_version }, | ||
| 1810 | ); | ||
| 1811 | try stderr.flush(); | ||
| 1812 | std.process.exit(1); | ||
| 1813 | } | ||
| 1814 | }, | ||
| 1815 | .error_bundle => { | ||
| 1816 | const body = try arena.alloc(u8, hdr.bytes_len); | ||
| 1817 | try client.in.readSliceAll(body); | ||
| 1818 | const eb = std.zig.Server.allocErrorBundle(arena, body) catch |err| switch (err) { | ||
| 1819 | error.OutOfMemory => |e| return e, | ||
| 1820 | error.EndOfStream => { | ||
| 1821 | try stderr.writeAll("error bundle truncated\n"); | ||
| 1822 | try stderr.flush(); | ||
| 1823 | std.process.exit(1); | ||
| 1824 | }, | ||
| 1825 | }; | ||
| 1826 | success = false; | ||
| 1827 | eb.renderToStderr(io, .{}, .auto) catch {}; | ||
| 1828 | }, | ||
| 1829 | .test_metadata => { | ||
| 1830 | const tm_hdr = | ||
| 1831 | try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little); | ||
| 1832 | metadata = .{ | ||
| 1833 | .index = 0, | ||
| 1834 | .len = tm_hdr.tests_len, | ||
| 1835 | .names = try arena.alloc(u32, tm_hdr.tests_len), | ||
| 1836 | .expected_panic_msgs = try arena.alloc(u32, tm_hdr.tests_len), | ||
| 1837 | .string_bytes = try arena.alloc(u8, tm_hdr.string_bytes_len), | ||
| 1838 | }; | ||
| 1839 | try client.in.readSliceEndian(u32, metadata.names, .little); | ||
| 1840 | try client.in.readSliceEndian(u32, metadata.expected_panic_msgs, .little); | ||
| 1841 | try client.in.readSliceAll(metadata.string_bytes); | ||
| 1842 | try client.serveRunTest(metadata.next() orelse break); | ||
| 1843 | }, | ||
| 1844 | .test_results => { | ||
| 1845 | const tr = try client.in.takeStruct(std.zig.Server.Message.TestResults, .little); | ||
| 1846 | switch (tr.flags.status) { | ||
| 1847 | .pass, .skip => {}, | ||
| 1848 | .fail => success = false, | ||
| 1849 | } | ||
| 1850 | try stdout.print("[{t}] {s}\n", .{ tr.flags.status, metadata.name() }); | ||
| 1851 | try stdout.flush(); | ||
| 1852 | try client.serveRunTest(metadata.next() orelse break); | ||
| 1853 | }, | ||
| 1854 | } | ||
| 1855 | } | ||
| 1856 | try client.serveBodylessMessage(.exit); | ||
| 1857 | return success; | ||
| 1858 | } | ||
test/tests.zig+53-57| ... | @@ -2214,9 +2214,9 @@ const IncrementalTarget = struct { | ... | @@ -2214,9 +2214,9 @@ const IncrementalTarget = struct { |
| 2214 | 2214 | ||
| 2215 | /// These are passed to `incr-check` as `<target>-<backend>` strings. | 2215 | /// These are passed to `incr-check` as `<target>-<backend>` strings. |
| 2216 | /// | 2216 | /// |
| 2217 | /// If only one specific test is failing on a target, instead of entirely disabling the target here, | 2217 | /// If only one specific test is failing on a target, instead of entirely disabling the target here, you |
| 2218 | /// you can skip the target for that specific test only by adding a line like this to the manifest: | 2218 | /// can skip the target for that specific test only by adding a line like this to top of the manifest: |
| 2219 | /// #skip_target=x86_64-linux-selfhosted | 2219 | /// #skip x86_64-linux-selfhosted |
| 2220 | const incremental_targets = &[_]IncrementalTarget{ | 2220 | const incremental_targets = &[_]IncrementalTarget{ |
| 2221 | // Avoid adding more CBE or LLVM targets without good reason: they're a lot slower than others | 2221 | // Avoid adding more CBE or LLVM targets without good reason: they're a lot slower than others |
| 2222 | // to run due to the output (C source code or LLVM IR) being built non-incrementally (by Clang | 2222 | // to run due to the output (C source code or LLVM IR) being built non-incrementally (by Clang |
| ... | @@ -2502,7 +2502,7 @@ pub fn addStandaloneTests( | ... | @@ -2502,7 +2502,7 @@ pub fn addStandaloneTests( |
| 2502 | .simple_skip_release_small = mem.findScalar(OptimizeMode, optimize_modes, .small) == null, | 2502 | .simple_skip_release_small = mem.findScalar(OptimizeMode, optimize_modes, .small) == null, |
| 2503 | }); | 2503 | }); |
| 2504 | const test_cases_dep_step = test_cases_dep.builder.default_step; | 2504 | const test_cases_dep_step = test_cases_dep.builder.default_step; |
| 2505 | test_cases_dep_step.name = b.graph.dupeString(test_cases_dep_name); | 2505 | test_cases_dep_step.name = test_cases_dep_name; |
| 2506 | step.dependOn(test_cases_dep.builder.default_step); | 2506 | step.dependOn(test_cases_dep.builder.default_step); |
| 2507 | return step; | 2507 | return step; |
| 2508 | } | 2508 | } |
| ... | @@ -3344,41 +3344,41 @@ const IncrementalTestOptions = struct { | ... | @@ -3344,41 +3344,41 @@ const IncrementalTestOptions = struct { |
| 3344 | 3344 | ||
| 3345 | pub fn addIncrementalTests( | 3345 | pub fn addIncrementalTests( |
| 3346 | b: *std.Build, | 3346 | b: *std.Build, |
| 3347 | test_step: *Step, | 3347 | runner: *std.Build.Step.Compile, |
| 3348 | options: IncrementalTestOptions, | 3348 | options: IncrementalTestOptions, |
| 3349 | ) !void { | 3349 | ) !*Step { |
| 3350 | const io = b.graph.io; | 3350 | const tests_step = b.step("test-incremental", "Run the new incremental compilation test cases"); |
| 3351 | 3351 | ||
| 3352 | const incr_check = b.addExecutable(.{ | 3352 | const tests_path = b.path("test/incremental"); |
| 3353 | .name = "incr-check", | 3353 | b.dependOnDirectoryContents(tests_path); |
| 3354 | .root_module = b.createModule(.{ | 3354 | |
| 3355 | .root_source_file = b.path("tools/incr-check.zig"), | 3355 | var tests_dir = try b.root.openDir(b.graph.io, "test/incremental", .{ .iterate = true }); |
| 3356 | .target = b.graph.host, | 3356 | defer tests_dir.close(b.graph.io); |
| 3357 | .optimize = .debug, | 3357 | var test_it = tests_dir.iterate(); |
| 3358 | }), | 3358 | while (try test_it.next(b.graph.io)) |@"test"| { |
| 3359 | }); | 3359 | for (options.test_filters) |filter| { |
| 3360 | 3360 | if (std.mem.find(u8, @"test".name, filter) != null) break; | |
| 3361 | b.dependOnDirectoryContents(b.path("test/incremental")); | 3361 | } else if (options.test_filters.len > 0) continue; |
| 3362 | |||
| 3363 | var dir = try b.root.openDir(io, "test/incremental", .{ .iterate = true }); | ||
| 3364 | defer dir.close(io); | ||
| 3365 | 3362 | ||
| 3366 | var it = try dir.walk(b.graph.arena); | 3363 | const test_path = tests_path.path(b, @"test".name); |
| 3367 | while (try it.next(io)) |entry| { | 3364 | switch (@"test".kind) { |
| 3368 | if (std.mem.endsWith(u8, entry.basename, ".swp")) continue; | 3365 | else => continue, |
| 3366 | .file => if (isEditorFileName(@"test".name)) continue, | ||
| 3367 | .directory => {}, | ||
| 3368 | } | ||
| 3369 | 3369 | ||
| 3370 | for (options.test_filters) |test_filter| { | 3370 | const run = b.addRunArtifact(runner); |
| 3371 | if (std.mem.find(u8, entry.path, test_filter)) |_| break; | 3371 | run.setName(@"test".name); |
| 3372 | } else if (options.test_filters.len > 0) continue; | 3372 | run.enableProtocolMode(); |
| 3373 | 3373 | ||
| 3374 | switch (entry.kind) { | 3374 | switch (@"test".kind) { |
| 3375 | .file => {}, | ||
| 3376 | .directory => { | ||
| 3377 | b.dependOnDirectoryContents(b.path(b.pathJoin(&.{ "test", "incremental", entry.path }))); | ||
| 3378 | }, | ||
| 3379 | else => continue, | 3375 | else => continue, |
| 3376 | .file => run.addFileArg(test_path), | ||
| 3377 | .directory => run.addDirectoryArg(test_path), | ||
| 3380 | } | 3378 | } |
| 3381 | b.dependOnFileContents(b.path(b.pathJoin(&.{ "test", "incremental", entry.path }))); | 3379 | run.addPrefixedFileArg("--zig=", .zig_exe); |
| 3380 | run.addPrefixedDirectoryArg("--lib=", .zig_lib); | ||
| 3381 | _ = run.addPrefixedOutputDirectoryArg("--src=", "src"); | ||
| 3382 | 3382 | ||
| 3383 | for (incremental_targets) |test_target| { | 3383 | for (incremental_targets) |test_target| { |
| 3384 | const resolved_target = b.resolveTargetQuery(test_target.target); | 3384 | const resolved_target = b.resolveTargetQuery(test_target.target); |
| ... | @@ -3399,40 +3399,36 @@ pub fn addIncrementalTests( | ... | @@ -3399,40 +3399,36 @@ pub fn addIncrementalTests( |
| 3399 | 3399 | ||
| 3400 | if (options.skip_llvm and test_target.backend == .llvm) continue; | 3400 | if (options.skip_llvm and test_target.backend == .llvm) continue; |
| 3401 | 3401 | ||
| 3402 | const target_str = b.fmt("{s}-{t}", .{ | 3402 | const target_str = b.fmt("{s}-incremental-{t}", .{ |
| 3403 | resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"), | 3403 | resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"), |
| 3404 | test_target.backend, | 3404 | test_target.backend, |
| 3405 | }); | 3405 | }); |
| 3406 | 3406 | ||
| 3407 | if (options.test_target_filters.len > 0) { | 3407 | for (options.test_target_filters) |filter| { |
| 3408 | for (options.test_target_filters) |filter| { | 3408 | if (std.mem.find(u8, target_str, filter) != null) break; |
| 3409 | if (std.mem.find(u8, target_str, filter) != null) break; | 3409 | } else if (options.test_target_filters.len > 0) continue; |
| 3410 | } else continue; | ||
| 3411 | } | ||
| 3412 | |||
| 3413 | const run = b.addRunArtifact(incr_check); | ||
| 3414 | run.setName(b.fmt("incr-check {s} '{s}'", .{ target_str, entry.basename })); | ||
| 3415 | |||
| 3416 | run.addArg(b.graph.zig_exe); | ||
| 3417 | run.addFileArg(b.path("test/incremental/").path(b, entry.path)); | ||
| 3418 | |||
| 3419 | run.addArg("--zig-lib-dir"); | ||
| 3420 | run.addDirectoryArg(.zig_lib); | ||
| 3421 | 3410 | ||
| 3422 | run.addArgs(&.{ "--target", target_str }); | 3411 | run.addArgs(&.{ "--target", target_str }); |
| 3412 | } | ||
| 3423 | 3413 | ||
| 3424 | run.addArg("--quiet"); // don't fill stderr telling us about skipped tests etc | 3414 | run.addPrefixedDirectoryArg("--libc-runtimes=", .{ .relative = .{ .base = .libc_runtimes } }); |
| 3415 | run.addThirdPartyEnabledArgDarling(.{ .enabled = "-fdarling" }); | ||
| 3416 | run.addThirdPartyEnabledArgQemu(.{ .enabled = "-fqemu" }); | ||
| 3417 | run.addThirdPartyEnabledArgRosetta(.{ .enabled = "-frosetta" }); | ||
| 3418 | run.addThirdPartyEnabledArgWasmtime(.{ .enabled = "-fwasmtime" }); | ||
| 3419 | run.addThirdPartyEnabledArgWine(.{ .enabled = "-fwine" }); | ||
| 3425 | 3420 | ||
| 3426 | run.addThirdPartyEnabledArgDarling(.{ .enabled = "-fdarling" }); | 3421 | run.addArg("--quiet"); // don't fill stderr telling us about skipped tests etc |
| 3427 | run.addThirdPartyEnabledArgQemu(.{ .enabled = "-fqemu" }); | ||
| 3428 | run.addThirdPartyEnabledArgRosetta(.{ .enabled = "-frosetta" }); | ||
| 3429 | run.addThirdPartyEnabledArgWasmtime(.{ .enabled = "-fwasmtime" }); | ||
| 3430 | run.addThirdPartyEnabledArgWine(.{ .enabled = "-fwine" }); | ||
| 3431 | 3422 | ||
| 3432 | run.addCheck(.{ .expect_term = .{ .exited = 0 } }); | 3423 | tests_step.dependOn(&run.step); |
| 3433 | test_step.dependOn(&run.step); | ||
| 3434 | } | ||
| 3435 | } | 3424 | } |
| 3425 | |||
| 3426 | return tests_step; | ||
| 3427 | } | ||
| 3428 | |||
| 3429 | fn isEditorFileName(name: []const u8) bool { | ||
| 3430 | return (std.mem.startsWith(u8, name, "#") and std.mem.endsWith(u8, name, "#")) or | ||
| 3431 | std.mem.endsWith(u8, name, "~") or std.mem.endsWith(u8, name, ".swp"); | ||
| 3436 | } | 3432 | } |
| 3437 | 3433 | ||
| 3438 | pub fn addLlvmIrTests(b: *std.Build, options: LlvmIrContext.Options) ?*Step { | 3434 | pub fn addLlvmIrTests(b: *std.Build, options: LlvmIrContext.Options) ?*Step { |
tools/doctest.zig+1| ... | @@ -314,6 +314,7 @@ fn printOutput( | ... | @@ -314,6 +314,7 @@ fn printOutput( |
| 314 | switch (getExternalExecutor(io, &target, .{ | 314 | switch (getExternalExecutor(io, &target, .{ |
| 315 | .host_cpu_arch = host.cpu.arch, | 315 | .host_cpu_arch = host.cpu.arch, |
| 316 | .host_os_tag = host.os.tag, | 316 | .host_os_tag = host.os.tag, |
| 317 | .link_mode = code.link_mode orelse .dynamic, | ||
| 317 | .link_libc = code.link_libc, | 318 | .link_libc = code.link_libc, |
| 318 | })) { | 319 | })) { |
| 319 | .native => {}, | 320 | .native => {}, |
tools/gen_spirv_spec.zig+1-2| ... | @@ -86,8 +86,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -86,8 +86,7 @@ pub fn main(init: std.process.Init) !void { |
| 86 | 86 | ||
| 87 | var zir = try std.zig.AstGen.generate(arena, tree); | 87 | var zir = try std.zig.AstGen.generate(arena, tree); |
| 88 | if (zir.hasCompileErrors()) { | 88 | if (zir.hasCompileErrors()) { |
| 89 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 89 | var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena); |
| 90 | try wip_errors.init(arena); | ||
| 91 | defer wip_errors.deinit(); | 90 | defer wip_errors.deinit(); |
| 92 | try wip_errors.addZirErrorMessages(zir, tree, output, ""); | 91 | try wip_errors.addZirErrorMessages(zir, tree, output, ""); |
| 93 | var error_bundle = try wip_errors.toOwnedBundle(""); | 92 | var error_bundle = try wip_errors.toOwnedBundle(""); |
tools/incr-check.zig deleted-1032| ... | @@ -1,1032 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Io = std.Io; | ||
| 3 | const Dir = std.Io.Dir; | ||
| 4 | const Allocator = std.mem.Allocator; | ||
| 5 | const Cache = std.Build.Cache; | ||
| 6 | |||
| 7 | const usage = | ||
| 8 | \\Usage: incr-check <zig binary path> <input file> [options] | ||
| 9 | \\Options: | ||
| 10 | \\ --target triple-backend | ||
| 11 | \\ --quiet | ||
| 12 | \\ --zig-lib-dir /path/to/zig/lib | ||
| 13 | \\ --zig-cc-binary /path/to/zig | ||
| 14 | \\ -fqemu | ||
| 15 | \\ -fwine | ||
| 16 | \\ -fwasmtime | ||
| 17 | \\Debug Options: | ||
| 18 | \\ --preserve-tmp | ||
| 19 | \\ --debug-log foo | ||
| 20 | \\ --debug-link-snapshot | ||
| 21 | ; | ||
| 22 | |||
| 23 | pub const std_options: std.Options = .{ | ||
| 24 | .logFn = logImpl, | ||
| 25 | }; | ||
| 26 | var log_cur_update: ?*const Case.Update = null; | ||
| 27 | fn logImpl( | ||
| 28 | comptime level: std.log.Level, | ||
| 29 | comptime scope: @EnumLiteral(), | ||
| 30 | comptime format: []const u8, | ||
| 31 | args: anytype, | ||
| 32 | ) void { | ||
| 33 | const update = log_cur_update orelse { | ||
| 34 | return std.log.defaultLog(level, scope, format, args); | ||
| 35 | }; | ||
| 36 | std.log.defaultLog( | ||
| 37 | level, | ||
| 38 | scope, | ||
| 39 | "['{s}'] " ++ format, | ||
| 40 | .{update.name} ++ args, | ||
| 41 | ); | ||
| 42 | } | ||
| 43 | |||
| 44 | pub fn main(init: std.process.Init) !void { | ||
| 45 | const gpa = init.gpa; | ||
| 46 | const arena = init.arena.allocator(); | ||
| 47 | const io = init.io; | ||
| 48 | const environ_map = init.environ_map; | ||
| 49 | const cwd_path = try std.process.currentPathAlloc(io, arena); | ||
| 50 | |||
| 51 | var opt_zig_exe: ?[]const u8 = null; | ||
| 52 | var opt_input_file_name: ?[]const u8 = null; | ||
| 53 | var opt_lib_dir: ?[]const u8 = null; | ||
| 54 | var opt_cc_zig: ?[]const u8 = null; | ||
| 55 | var opt_target: ?struct { std.Target.Query, Backend } = null; | ||
| 56 | var preserve_tmp = false; | ||
| 57 | var enable_qemu: bool = false; | ||
| 58 | var enable_wine: bool = false; | ||
| 59 | var enable_wasmtime: bool = false; | ||
| 60 | var enable_darling: bool = false; | ||
| 61 | var quiet: bool = false; | ||
| 62 | |||
| 63 | var debug_log_args: std.ArrayList([]const u8) = .empty; | ||
| 64 | var debug_link_snapshot = false; | ||
| 65 | |||
| 66 | var arg_it = try init.minimal.args.iterateAllocator(arena); | ||
| 67 | _ = arg_it.skip(); | ||
| 68 | while (arg_it.next()) |arg| { | ||
| 69 | if (arg.len > 0 and arg[0] == '-') { | ||
| 70 | if (std.mem.eql(u8, arg, "--zig-lib-dir")) { | ||
| 71 | opt_lib_dir = arg_it.next() orelse badUsage("expected arg after --zig-lib-dir", .{}); | ||
| 72 | } else if (std.mem.eql(u8, arg, "--target")) { | ||
| 73 | const str = arg_it.next() orelse badUsage("expected arg after --zig-cc-binary", .{}); | ||
| 74 | opt_target = parseTargetQueryAndBackend(str, ""); | ||
| 75 | } else if (std.mem.eql(u8, arg, "--quiet")) { | ||
| 76 | quiet = true; | ||
| 77 | } else if (std.mem.eql(u8, arg, "--debug-log")) { | ||
| 78 | try debug_log_args.append( | ||
| 79 | arena, | ||
| 80 | arg_it.next() orelse badUsage("expected arg after --debug-log", .{}), | ||
| 81 | ); | ||
| 82 | } else if (std.mem.eql(u8, arg, "--debug-link-snapshot")) { | ||
| 83 | debug_link_snapshot = true; | ||
| 84 | } else if (std.mem.eql(u8, arg, "--preserve-tmp")) { | ||
| 85 | preserve_tmp = true; | ||
| 86 | } else if (std.mem.eql(u8, arg, "-fqemu")) { | ||
| 87 | enable_qemu = true; | ||
| 88 | } else if (std.mem.eql(u8, arg, "-fwine")) { | ||
| 89 | enable_wine = true; | ||
| 90 | } else if (std.mem.eql(u8, arg, "-fwasmtime")) { | ||
| 91 | enable_wasmtime = true; | ||
| 92 | } else if (std.mem.eql(u8, arg, "-fdarling")) { | ||
| 93 | enable_darling = true; | ||
| 94 | } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) { | ||
| 95 | opt_cc_zig = arg_it.next() orelse badUsage("expected arg after --zig-cc-binary", .{}); | ||
| 96 | } else { | ||
| 97 | badUsage("unknown option '{s}'", .{arg}); | ||
| 98 | } | ||
| 99 | continue; | ||
| 100 | } | ||
| 101 | if (opt_zig_exe == null) { | ||
| 102 | opt_zig_exe = arg; | ||
| 103 | } else if (opt_input_file_name == null) { | ||
| 104 | opt_input_file_name = arg; | ||
| 105 | } else { | ||
| 106 | badUsage("unknown argument '{s}'\n{s}", .{ arg, usage }); | ||
| 107 | } | ||
| 108 | } | ||
| 109 | const zig_exe = opt_zig_exe orelse badUsage("missing path to zig", .{}); | ||
| 110 | const input_file_name = opt_input_file_name orelse badUsage("missing input file", .{}); | ||
| 111 | const target_query, const backend = opt_target orelse badUsage("missing required option '--target'", .{}); | ||
| 112 | |||
| 113 | if (backend == .cbe and opt_lib_dir == null) { | ||
| 114 | std.process.fatal("'--zig-lib-dir' required when using backend 'cbe'", .{}); | ||
| 115 | } | ||
| 116 | |||
| 117 | const input_file_bytes = try Dir.cwd().readFileAlloc(io, input_file_name, arena, .limited(std.math.maxInt(u32))); | ||
| 118 | const case: Case = try .parse(arena, input_file_bytes); | ||
| 119 | |||
| 120 | for (case.skip_targets) |skip| { | ||
| 121 | if (target_query.eql(skip.query) and backend == skip.backend) { | ||
| 122 | if (!quiet) std.log.warn("skipping test because of a 'skip_target' match", .{}); | ||
| 123 | return; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | const target = try std.zig.system.resolveTargetQuery(io, target_query); | ||
| 128 | |||
| 129 | const prog_node = std.Progress.start(io, .{}); | ||
| 130 | defer prog_node.end(); | ||
| 131 | |||
| 132 | const rand_int = rand64(io); | ||
| 133 | const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int); | ||
| 134 | var tmp_dir = try Dir.cwd().createDirPathOpen(io, tmp_dir_path, .{}); | ||
| 135 | defer { | ||
| 136 | tmp_dir.close(io); | ||
| 137 | if (!preserve_tmp) { | ||
| 138 | Dir.cwd().deleteTree(io, tmp_dir_path) catch |err| { | ||
| 139 | std.log.warn("failed to delete tree '{s}': {t}", .{ tmp_dir_path, err }); | ||
| 140 | }; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | // Convert paths to be relative to the cwd of the subprocess. | ||
| 145 | const resolved_zig_exe = try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, zig_exe); | ||
| 146 | const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir| | ||
| 147 | try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, lib_dir) | ||
| 148 | else | ||
| 149 | null; | ||
| 150 | |||
| 151 | const host = try std.zig.system.resolveTargetQuery(io, .{}); | ||
| 152 | |||
| 153 | var child_args: std.ArrayList([]const u8) = .empty; | ||
| 154 | try child_args.appendSlice(arena, &.{ | ||
| 155 | resolved_zig_exe, "build-exe", | ||
| 156 | "-fincremental", "-fno-ubsan-rt", | ||
| 157 | "-target", try target_query.zigTriple(arena), | ||
| 158 | "--cache-dir", ".local-cache", | ||
| 159 | "--global-cache-dir", ".global-cache", | ||
| 160 | }); | ||
| 161 | |||
| 162 | if (opt_resolved_lib_dir) |resolved_lib_dir| { | ||
| 163 | try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir }); | ||
| 164 | } | ||
| 165 | switch (backend) { | ||
| 166 | .sema => try child_args.append(arena, "-fno-emit-bin"), | ||
| 167 | .selfhosted => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }), | ||
| 168 | .llvm => try child_args.appendSlice(arena, &.{ "-fllvm", "-flld" }), | ||
| 169 | .cbe => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }), | ||
| 170 | } | ||
| 171 | for (debug_log_args.items) |arg| { | ||
| 172 | try child_args.appendSlice(arena, &.{ "--debug-log", arg }); | ||
| 173 | } | ||
| 174 | if (debug_link_snapshot) { | ||
| 175 | try child_args.append(arena, "--debug-link-snapshot"); | ||
| 176 | } | ||
| 177 | for (case.modules) |mod| { | ||
| 178 | try child_args.appendSlice(arena, &.{ "--dep", mod.name }); | ||
| 179 | } | ||
| 180 | try child_args.append(arena, try std.fmt.allocPrint(arena, "-Mroot={s}", .{case.root_source_file})); | ||
| 181 | for (case.modules) |mod| { | ||
| 182 | try child_args.append(arena, try std.fmt.allocPrint(arena, "-M{s}={s}", .{ mod.name, mod.file })); | ||
| 183 | } | ||
| 184 | try child_args.append(arena, "--listen=-"); | ||
| 185 | |||
| 186 | const zig_prog_node = prog_node.start("zig", 0); | ||
| 187 | defer zig_prog_node.end(); | ||
| 188 | |||
| 189 | var cc_child_args: std.ArrayList([]const u8) = .empty; | ||
| 190 | if (backend == .cbe) { | ||
| 191 | const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe| | ||
| 192 | try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, cc_zig_exe) | ||
| 193 | else | ||
| 194 | resolved_zig_exe; | ||
| 195 | |||
| 196 | try cc_child_args.appendSlice(arena, &.{ | ||
| 197 | resolved_cc_zig_exe, "build-exe", | ||
| 198 | "-target", try target_query.zigTriple(arena), | ||
| 199 | "--cache-dir", ".local-cache", | ||
| 200 | "--global-cache-dir", ".global-cache", | ||
| 201 | "-I", opt_resolved_lib_dir.?, // verified earlier | ||
| 202 | "-lc", | ||
| 203 | }); | ||
| 204 | } | ||
| 205 | |||
| 206 | const allow_compiler_stderr = debug_log_args.items.len != 0; | ||
| 207 | |||
| 208 | if (allow_compiler_stderr) { | ||
| 209 | const cmd: std.zig.SubprocessCommand = .{ | ||
| 210 | .argv = child_args.items, | ||
| 211 | .cwd = tmp_dir_path, | ||
| 212 | }; | ||
| 213 | std.log.scoped(.spawn).info("{f}", .{cmd}); | ||
| 214 | } | ||
| 215 | |||
| 216 | var child = try std.process.spawn(io, .{ | ||
| 217 | .argv = child_args.items, | ||
| 218 | .stdin = .pipe, | ||
| 219 | .stdout = .pipe, | ||
| 220 | .stderr = .pipe, | ||
| 221 | .progress_node = zig_prog_node, | ||
| 222 | .cwd = .{ .path = tmp_dir_path }, | ||
| 223 | }); | ||
| 224 | defer child.kill(io); | ||
| 225 | |||
| 226 | const updates_prog_node = prog_node.start("updates", case.updates.len); | ||
| 227 | defer updates_prog_node.end(); | ||
| 228 | |||
| 229 | var eval: Eval = .{ | ||
| 230 | .arena = arena, | ||
| 231 | .io = io, | ||
| 232 | .case = case, | ||
| 233 | .host = host, | ||
| 234 | .target = target, | ||
| 235 | .backend = backend, | ||
| 236 | .tmp_dir = tmp_dir, | ||
| 237 | .tmp_dir_path = tmp_dir_path, | ||
| 238 | .child = &child, | ||
| 239 | .allow_compiler_stderr = allow_compiler_stderr, | ||
| 240 | .quiet = quiet, | ||
| 241 | .preserve_tmp_on_fatal = preserve_tmp, | ||
| 242 | .cc_child_args = &cc_child_args, | ||
| 243 | .enable_qemu = enable_qemu, | ||
| 244 | .enable_wine = enable_wine, | ||
| 245 | .enable_wasmtime = enable_wasmtime, | ||
| 246 | .enable_darling = enable_darling, | ||
| 247 | }; | ||
| 248 | |||
| 249 | var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined; | ||
| 250 | var multi_reader: Io.File.MultiReader = undefined; | ||
| 251 | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); | ||
| 252 | defer multi_reader.deinit(); | ||
| 253 | |||
| 254 | var update_mtime = Io.Clock.real.now(io); | ||
| 255 | for (case.updates) |update| { | ||
| 256 | var update_prog_node = updates_prog_node.start(update.name, 0); | ||
| 257 | defer update_prog_node.end(); | ||
| 258 | |||
| 259 | if (debug_log_args.items.len != 0) { | ||
| 260 | // Print a line separating the debug logs from the compiler in the stderr output. | ||
| 261 | std.log.scoped(.status).info("update: '{s}'", .{update.name}); | ||
| 262 | } | ||
| 263 | |||
| 264 | log_cur_update = &update; | ||
| 265 | defer log_cur_update = null; | ||
| 266 | |||
| 267 | eval.write(update, update_mtime); | ||
| 268 | update_mtime = update_mtime.addDuration(.fromSeconds(5)); | ||
| 269 | try eval.requestUpdate(); | ||
| 270 | try eval.check(&multi_reader, update, update_prog_node); | ||
| 271 | } | ||
| 272 | |||
| 273 | try eval.end(&multi_reader); | ||
| 274 | |||
| 275 | waitChild(&child, &eval); | ||
| 276 | } | ||
| 277 | |||
| 278 | const Eval = struct { | ||
| 279 | arena: Allocator, | ||
| 280 | io: Io, | ||
| 281 | case: Case, | ||
| 282 | host: std.Target, | ||
| 283 | target: std.Target, | ||
| 284 | backend: Backend, | ||
| 285 | tmp_dir: Dir, | ||
| 286 | tmp_dir_path: []const u8, | ||
| 287 | child: *std.process.Child, | ||
| 288 | allow_compiler_stderr: bool, | ||
| 289 | quiet: bool, | ||
| 290 | preserve_tmp_on_fatal: bool, | ||
| 291 | /// When `backend == .cbe`, this contains the first few arguments to `zig cc` to build the generated binary. | ||
| 292 | /// The arguments `out.c in.c` must be appended before spawning the subprocess. | ||
| 293 | cc_child_args: *std.ArrayList([]const u8), | ||
| 294 | |||
| 295 | enable_qemu: bool, | ||
| 296 | enable_wine: bool, | ||
| 297 | enable_wasmtime: bool, | ||
| 298 | enable_darling: bool, | ||
| 299 | |||
| 300 | /// Currently this function assumes the previous updates have already been written. | ||
| 301 | fn write(eval: *Eval, update: Case.Update, mtime: Io.Timestamp) void { | ||
| 302 | const io = eval.io; | ||
| 303 | for (update.changes) |full_contents| { | ||
| 304 | var update_file = eval.tmp_dir.createFile(io, full_contents.name, .{}) catch |err| { | ||
| 305 | eval.fatal("failed to create update '{s}': {t}", .{ full_contents.name, err }); | ||
| 306 | }; | ||
| 307 | defer update_file.close(io); | ||
| 308 | update_file.writeStreamingAll(io, full_contents.bytes) catch |err| { | ||
| 309 | eval.fatal("failed to write update '{s}': {t}", .{ full_contents.name, err }); | ||
| 310 | }; | ||
| 311 | update_file.setTimestamps(io, .{ .modify_timestamp = .{ .new = mtime } }) catch |err| { | ||
| 312 | eval.fatal("failed to set mtime for '{s}': {t}", .{ full_contents.name, err }); | ||
| 313 | }; | ||
| 314 | } | ||
| 315 | for (update.deletes) |doomed_name| { | ||
| 316 | eval.tmp_dir.deleteFile(io, doomed_name) catch |err| { | ||
| 317 | eval.fatal("failed to delete '{s}': {t}", .{ doomed_name, err }); | ||
| 318 | }; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | fn check(eval: *Eval, mr: *Io.File.MultiReader, update: Case.Update, prog_node: std.Progress.Node) !void { | ||
| 323 | const arena = eval.arena; | ||
| 324 | const stdout = mr.reader(0); | ||
| 325 | const stderr = mr.reader(1); | ||
| 326 | |||
| 327 | var client: std.zig.Client = .{ | ||
| 328 | .in = stdout, | ||
| 329 | .out = undefined, | ||
| 330 | }; | ||
| 331 | |||
| 332 | while (true) { | ||
| 333 | const header = client.receiveMessageWithMultiReader(mr, .none) catch |err| switch (err) { | ||
| 334 | error.Timeout => unreachable, | ||
| 335 | error.EndOfStream => break, | ||
| 336 | else => |e| return e, | ||
| 337 | }; | ||
| 338 | const body = client.in.take(header.bytes_len) catch unreachable; | ||
| 339 | |||
| 340 | switch (header.tag) { | ||
| 341 | .error_bundle => { | ||
| 342 | const result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body); | ||
| 343 | if (stderr.bufferedLen() > 0) { | ||
| 344 | if (eval.allow_compiler_stderr) { | ||
| 345 | std.log.info("error_bundle stderr:\n{s}", .{stderr.buffered()}); | ||
| 346 | } else { | ||
| 347 | eval.fatal("error_bundle unexpected stderr:\n{s}", .{stderr.buffered()}); | ||
| 348 | } | ||
| 349 | stderr.tossBuffered(); | ||
| 350 | } | ||
| 351 | if (result_error_bundle.errorMessageCount() != 0) { | ||
| 352 | try eval.checkErrorOutcome(update, result_error_bundle); | ||
| 353 | } | ||
| 354 | // This message indicates the end of the update. | ||
| 355 | return; | ||
| 356 | }, | ||
| 357 | .emit_digest => { | ||
| 358 | var r: std.Io.Reader = .fixed(body); | ||
| 359 | _ = r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; | ||
| 360 | |||
| 361 | if (stderr.bufferedLen() > 0) { | ||
| 362 | if (eval.allow_compiler_stderr) { | ||
| 363 | std.log.info("emit_digest stderr:\n{s}", .{stderr.buffered()}); | ||
| 364 | } else { | ||
| 365 | eval.fatal("emit_digest unexpected stderr:\n{s}", .{stderr.buffered()}); | ||
| 366 | } | ||
| 367 | stderr.tossBuffered(); | ||
| 368 | } | ||
| 369 | if (eval.backend == .sema) { | ||
| 370 | try eval.checkSuccessOutcome(update, null, prog_node); | ||
| 371 | continue; | ||
| 372 | } | ||
| 373 | |||
| 374 | const digest = r.takeArray(Cache.bin_digest_len) catch unreachable; | ||
| 375 | const result_dir = ".local-cache" ++ Dir.path.sep_str ++ "o" ++ Dir.path.sep_str ++ Cache.binToHex(digest.*); | ||
| 376 | |||
| 377 | const bin_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{ | ||
| 378 | .root_name = "root", // corresponds to the module name "root" | ||
| 379 | .cpu_arch = eval.target.cpu.arch, | ||
| 380 | .os_tag = eval.target.os.tag, | ||
| 381 | .ofmt = eval.target.ofmt, | ||
| 382 | .abi = eval.target.abi, | ||
| 383 | .output_mode = .Exe, | ||
| 384 | }); | ||
| 385 | const bin_path = try Dir.path.join(arena, &.{ result_dir, bin_name }); | ||
| 386 | |||
| 387 | try eval.checkSuccessOutcome(update, bin_path, prog_node); | ||
| 388 | }, | ||
| 389 | else => { | ||
| 390 | // Ignore other messages. | ||
| 391 | }, | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | const buffered_stderr = stderr.buffered(); | ||
| 396 | if (buffered_stderr.len > 0) { | ||
| 397 | if (eval.allow_compiler_stderr) { | ||
| 398 | std.log.info("stderr:\n{s}", .{buffered_stderr}); | ||
| 399 | } else { | ||
| 400 | eval.fatal("unexpected stderr:\n{s}", .{buffered_stderr}); | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | waitChild(eval.child, eval); | ||
| 405 | eval.fatal("compiler failed to send terminating error_bundle", .{}); | ||
| 406 | } | ||
| 407 | |||
| 408 | fn checkErrorOutcome(eval: *Eval, update: Case.Update, error_bundle: std.zig.ErrorBundle) !void { | ||
| 409 | const io = eval.io; | ||
| 410 | const expected = switch (update.outcome) { | ||
| 411 | .unknown => return, | ||
| 412 | .compile_errors => |ce| ce, | ||
| 413 | .stdout, .exit_code => { | ||
| 414 | try error_bundle.renderToStderr(io, .{}, .auto); | ||
| 415 | eval.fatal("unexpected compile errors", .{}); | ||
| 416 | }, | ||
| 417 | }; | ||
| 418 | |||
| 419 | var expected_idx: usize = 0; | ||
| 420 | |||
| 421 | for (error_bundle.getMessages()) |err_idx| { | ||
| 422 | if (expected_idx == expected.errors.len) { | ||
| 423 | try error_bundle.renderToStderr(io, .{}, .auto); | ||
| 424 | eval.fatal("more errors than expected", .{}); | ||
| 425 | } | ||
| 426 | try eval.checkOneError(error_bundle, expected.errors[expected_idx], false, err_idx); | ||
| 427 | expected_idx += 1; | ||
| 428 | |||
| 429 | for (error_bundle.getNotes(err_idx)) |note_idx| { | ||
| 430 | if (expected_idx == expected.errors.len) { | ||
| 431 | try error_bundle.renderToStderr(io, .{}, .auto); | ||
| 432 | eval.fatal("more error notes than expected", .{}); | ||
| 433 | } | ||
| 434 | try eval.checkOneError(error_bundle, expected.errors[expected_idx], true, note_idx); | ||
| 435 | expected_idx += 1; | ||
| 436 | } | ||
| 437 | } | ||
| 438 | |||
| 439 | if (!std.mem.eql(u8, error_bundle.getCompileLogOutput(), expected.compile_log_output)) { | ||
| 440 | try error_bundle.renderToStderr(io, .{}, .auto); | ||
| 441 | eval.fatal("unexpected compile log output", .{}); | ||
| 442 | } | ||
| 443 | } | ||
| 444 | |||
| 445 | fn checkOneError( | ||
| 446 | eval: *Eval, | ||
| 447 | eb: std.zig.ErrorBundle, | ||
| 448 | expected: Case.ExpectedError, | ||
| 449 | is_note: bool, | ||
| 450 | err_idx: std.zig.ErrorBundle.MessageIndex, | ||
| 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 | } | ||