| ... | ... | @@ -22,6 +22,8 @@ step: Step, |
| 22 | 22 | argv: ArrayList(Arg), |
| 23 | 23 | |
| 24 | 24 | /// Set this to modify the current working directory |
| 25 | /// TODO change this to a Build.Cache.Directory to better integrate with |
| 26 | /// future child process cwd API. |
| 25 | 27 | cwd: ?[]const u8, |
| 26 | 28 | |
| 27 | 29 | /// Override this field to modify the environment, or use setEnvironmentVariable |
| ... | ... | @@ -89,7 +91,7 @@ pub const StdIo = union(enum) { |
| 89 | 91 | expect_stderr_match: []const u8, |
| 90 | 92 | expect_stdout_exact: []const u8, |
| 91 | 93 | expect_stdout_match: []const u8, |
| 92 | | expect_term: std.ChildProcess.Term, |
| 94 | expect_term: std.process.Child.Term, |
| 93 | 95 | }; |
| 94 | 96 | }; |
| 95 | 97 | |
| ... | ... | @@ -401,7 +403,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 401 | 403 | } |
| 402 | 404 | |
| 403 | 405 | fn formatTerm( |
| 404 | | term: ?std.ChildProcess.Term, |
| 406 | term: ?std.process.Child.Term, |
| 405 | 407 | comptime fmt: []const u8, |
| 406 | 408 | options: std.fmt.FormatOptions, |
| 407 | 409 | writer: anytype, |
| ... | ... | @@ -417,11 +419,11 @@ fn formatTerm( |
| 417 | 419 | try writer.writeAll("exited with any code"); |
| 418 | 420 | } |
| 419 | 421 | } |
| 420 | | fn fmtTerm(term: ?std.ChildProcess.Term) std.fmt.Formatter(formatTerm) { |
| 422 | fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Formatter(formatTerm) { |
| 421 | 423 | return .{ .data = term }; |
| 422 | 424 | } |
| 423 | 425 | |
| 424 | | fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) bool { |
| 426 | fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term) bool { |
| 425 | 427 | return if (expected) |e| switch (e) { |
| 426 | 428 | .Exited => |expected_code| switch (actual) { |
| 427 | 429 | .Exited => |actual_code| expected_code == actual_code, |
| ... | ... | @@ -453,10 +455,7 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) |
| 453 | 455 | try step.handleChildProcUnsupported(self.cwd, argv); |
| 454 | 456 | try Step.handleVerbose(step.owner, self.cwd, argv); |
| 455 | 457 | |
| 456 | | var stdout_bytes: ?[]const u8 = null; |
| 457 | | var stderr_bytes: ?[]const u8 = null; |
| 458 | | |
| 459 | | const term = spawnChildAndCollect(self, argv, &stdout_bytes, &stderr_bytes, has_side_effects) catch |err| term: { |
| 458 | const result = spawnChildAndCollect(self, argv, has_side_effects) catch |err| term: { |
| 460 | 459 | if (err == error.InvalidExe) interpret: { |
| 461 | 460 | // TODO: learn the target from the binary directly rather than from |
| 462 | 461 | // relying on it being a CompileStep. This will make this logic |
| ... | ... | @@ -571,11 +570,9 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) |
| 571 | 570 | |
| 572 | 571 | try Step.handleVerbose(step.owner, self.cwd, interp_argv.items); |
| 573 | 572 | |
| 574 | | assert(stdout_bytes == null); |
| 575 | | assert(stderr_bytes == null); |
| 576 | | break :term spawnChildAndCollect(self, interp_argv.items, &stdout_bytes, &stderr_bytes, has_side_effects) catch |inner_err| { |
| 573 | break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects) catch |e| { |
| 577 | 574 | return step.fail("unable to spawn {s}: {s}", .{ |
| 578 | | interp_argv.items[0], @errorName(inner_err), |
| 575 | interp_argv.items[0], @errorName(e), |
| 579 | 576 | }); |
| 580 | 577 | }; |
| 581 | 578 | } |
| ... | ... | @@ -586,7 +583,8 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) |
| 586 | 583 | switch (self.stdio) { |
| 587 | 584 | .check => |checks| for (checks.items) |check| switch (check) { |
| 588 | 585 | .expect_stderr_exact => |expected_bytes| { |
| 589 | | if (!mem.eql(u8, expected_bytes, stderr_bytes.?)) { |
| 586 | assert(!result.stderr_null); |
| 587 | if (!mem.eql(u8, expected_bytes, result.stderr)) { |
| 590 | 588 | return step.fail( |
| 591 | 589 | \\ |
| 592 | 590 | \\========= expected this stderr: ========= |
| ... | ... | @@ -597,13 +595,14 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) |
| 597 | 595 | \\{s} |
| 598 | 596 | , .{ |
| 599 | 597 | expected_bytes, |
| 600 | | stderr_bytes.?, |
| 598 | result.stderr, |
| 601 | 599 | try Step.allocPrintCmd(arena, self.cwd, argv), |
| 602 | 600 | }); |
| 603 | 601 | } |
| 604 | 602 | }, |
| 605 | 603 | .expect_stderr_match => |match| { |
| 606 | | if (mem.indexOf(u8, stderr_bytes.?, match) == null) { |
| 604 | assert(!result.stderr_null); |
| 605 | if (mem.indexOf(u8, result.stderr, match) == null) { |
| 607 | 606 | return step.fail( |
| 608 | 607 | \\ |
| 609 | 608 | \\========= expected to find in stderr: ========= |
| ... | ... | @@ -614,13 +613,14 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) |
| 614 | 613 | \\{s} |
| 615 | 614 | , .{ |
| 616 | 615 | match, |
| 617 | | stderr_bytes.?, |
| 616 | result.stderr, |
| 618 | 617 | try Step.allocPrintCmd(arena, self.cwd, argv), |
| 619 | 618 | }); |
| 620 | 619 | } |
| 621 | 620 | }, |
| 622 | 621 | .expect_stdout_exact => |expected_bytes| { |
| 623 | | if (!mem.eql(u8, expected_bytes, stdout_bytes.?)) { |
| 622 | assert(!result.stdout_null); |
| 623 | if (!mem.eql(u8, expected_bytes, result.stdout)) { |
| 624 | 624 | return step.fail( |
| 625 | 625 | \\ |
| 626 | 626 | \\========= expected this stdout: ========= |
| ... | ... | @@ -631,13 +631,14 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) |
| 631 | 631 | \\{s} |
| 632 | 632 | , .{ |
| 633 | 633 | expected_bytes, |
| 634 | | stdout_bytes.?, |
| 634 | result.stdout, |
| 635 | 635 | try Step.allocPrintCmd(arena, self.cwd, argv), |
| 636 | 636 | }); |
| 637 | 637 | } |
| 638 | 638 | }, |
| 639 | 639 | .expect_stdout_match => |match| { |
| 640 | | if (mem.indexOf(u8, stdout_bytes.?, match) == null) { |
| 640 | assert(!result.stdout_null); |
| 641 | if (mem.indexOf(u8, result.stdout, match) == null) { |
| 641 | 642 | return step.fail( |
| 642 | 643 | \\ |
| 643 | 644 | \\========= expected to find in stdout: ========= |
| ... | ... | @@ -648,15 +649,15 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) |
| 648 | 649 | \\{s} |
| 649 | 650 | , .{ |
| 650 | 651 | match, |
| 651 | | stdout_bytes.?, |
| 652 | result.stdout, |
| 652 | 653 | try Step.allocPrintCmd(arena, self.cwd, argv), |
| 653 | 654 | }); |
| 654 | 655 | } |
| 655 | 656 | }, |
| 656 | 657 | .expect_term => |expected_term| { |
| 657 | | if (!termMatches(expected_term, term)) { |
| 658 | if (!termMatches(expected_term, result.term)) { |
| 658 | 659 | return step.fail("the following command {} (expected {}):\n{s}", .{ |
| 659 | | fmtTerm(term), |
| 660 | fmtTerm(result.term), |
| 660 | 661 | fmtTerm(expected_term), |
| 661 | 662 | try Step.allocPrintCmd(arena, self.cwd, argv), |
| 662 | 663 | }); |
| ... | ... | @@ -664,24 +665,36 @@ fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) |
| 664 | 665 | }, |
| 665 | 666 | }, |
| 666 | 667 | else => { |
| 667 | | try step.handleChildProcessTerm(term, self.cwd, argv); |
| 668 | try step.handleChildProcessTerm(result.term, self.cwd, argv); |
| 668 | 669 | }, |
| 669 | 670 | } |
| 670 | 671 | } |
| 671 | 672 | |
| 673 | const ChildProcResult = struct { |
| 674 | // These use boolean flags instead of optionals as a workaround for |
| 675 | // https://github.com/ziglang/zig/issues/14783 |
| 676 | stdout: []const u8, |
| 677 | stderr: []const u8, |
| 678 | stdout_null: bool, |
| 679 | stderr_null: bool, |
| 680 | term: std.process.Child.Term, |
| 681 | }; |
| 682 | |
| 672 | 683 | fn spawnChildAndCollect( |
| 673 | 684 | self: *RunStep, |
| 674 | 685 | argv: []const []const u8, |
| 675 | | stdout_bytes: *?[]const u8, |
| 676 | | stderr_bytes: *?[]const u8, |
| 677 | 686 | has_side_effects: bool, |
| 678 | | ) !std.ChildProcess.Term { |
| 687 | ) !ChildProcResult { |
| 679 | 688 | const b = self.step.owner; |
| 680 | 689 | const arena = b.allocator; |
| 681 | | const cwd = if (self.cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path; |
| 682 | 690 | |
| 683 | | var child = std.ChildProcess.init(argv, arena); |
| 684 | | child.cwd = cwd; |
| 691 | var child = std.process.Child.init(argv, arena); |
| 692 | if (self.cwd) |cwd| { |
| 693 | child.cwd = b.pathFromRoot(cwd); |
| 694 | } else { |
| 695 | child.cwd = b.build_root.path; |
| 696 | child.cwd_dir = b.build_root.handle; |
| 697 | } |
| 685 | 698 | child.env_map = self.env_map orelse b.env_map; |
| 686 | 699 | |
| 687 | 700 | child.stdin_behavior = switch (self.stdio) { |
| ... | ... | @@ -704,6 +717,13 @@ fn spawnChildAndCollect( |
| 704 | 717 | argv[0], @errorName(err), |
| 705 | 718 | }); |
| 706 | 719 | |
| 720 | // These are not optionals, as a workaround for |
| 721 | // https://github.com/ziglang/zig/issues/14783 |
| 722 | var stdout_bytes: []const u8 = undefined; |
| 723 | var stderr_bytes: []const u8 = undefined; |
| 724 | var stdout_null = true; |
| 725 | var stderr_null = true; |
| 726 | |
| 707 | 727 | if (child.stdout) |stdout| { |
| 708 | 728 | if (child.stderr) |stderr| { |
| 709 | 729 | var poller = std.io.poll(arena, enum { stdout, stderr }, .{ |
| ... | ... | @@ -719,26 +739,36 @@ fn spawnChildAndCollect( |
| 719 | 739 | return error.StderrStreamTooLong; |
| 720 | 740 | } |
| 721 | 741 | |
| 722 | | stdout_bytes.* = try poller.fifo(.stdout).toOwnedSlice(); |
| 723 | | stderr_bytes.* = try poller.fifo(.stderr).toOwnedSlice(); |
| 742 | stdout_bytes = try poller.fifo(.stdout).toOwnedSlice(); |
| 743 | stderr_bytes = try poller.fifo(.stderr).toOwnedSlice(); |
| 744 | stdout_null = false; |
| 745 | stderr_null = false; |
| 724 | 746 | } else { |
| 725 | | stdout_bytes.* = try stdout.reader().readAllAlloc(arena, self.max_stdio_size); |
| 747 | stdout_bytes = try stdout.reader().readAllAlloc(arena, self.max_stdio_size); |
| 748 | stdout_null = false; |
| 726 | 749 | } |
| 727 | 750 | } else if (child.stderr) |stderr| { |
| 728 | | stderr_bytes.* = try stderr.reader().readAllAlloc(arena, self.max_stdio_size); |
| 751 | stderr_bytes = try stderr.reader().readAllAlloc(arena, self.max_stdio_size); |
| 752 | stderr_null = false; |
| 729 | 753 | } |
| 730 | 754 | |
| 731 | | if (stderr_bytes.*) |stderr| if (stderr.len > 0) { |
| 755 | if (!stderr_null and stderr_bytes.len > 0) { |
| 732 | 756 | const stderr_is_diagnostic = switch (self.stdio) { |
| 733 | 757 | .check => |checks| !checksContainStderr(checks.items), |
| 734 | 758 | else => true, |
| 735 | 759 | }; |
| 736 | 760 | if (stderr_is_diagnostic) { |
| 737 | | try self.step.result_error_msgs.append(arena, stderr); |
| 761 | try self.step.result_error_msgs.append(arena, stderr_bytes); |
| 738 | 762 | } |
| 739 | | }; |
| 763 | } |
| 740 | 764 | |
| 741 | | return child.wait(); |
| 765 | return .{ |
| 766 | .stdout = stdout_bytes, |
| 767 | .stderr = stderr_bytes, |
| 768 | .stdout_null = stdout_null, |
| 769 | .stderr_null = stderr_null, |
| 770 | .term = try child.wait(), |
| 771 | }; |
| 742 | 772 | } |
| 743 | 773 | |
| 744 | 774 | fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { |