| ... | @@ -5676,23 +5676,18 @@ fn updateCObject( | ... | @@ -5676,23 +5676,18 @@ fn updateCObject( |
| 5676 | else => log.warn("failed to delete {q}: {t}", .{ dep_file_path, err }), | 5676 | else => log.warn("failed to delete {q}: {t}", .{ dep_file_path, err }), |
| 5677 | }; | 5677 | }; |
| 5678 | if (std.process.can_spawn) { | 5678 | if (std.process.can_spawn) { |
| | 5679 | var diags: EvalZigLlvmProcessDiagnostics = undefined; |
| | 5680 | const result = comp.evalZigLlvmProcess(arena, &diags, argv.items) catch |err| switch (err) { |
| | 5681 | else => |e| return e, |
| | 5682 | error.EvalZigLlvmFail => return comp.failCObj( |
| | 5683 | c_object, |
| | 5684 | "failed to evaluate zig clang '{s}': {f}", |
| | 5685 | .{ argv.items[0], diags }, |
| | 5686 | ), |
| | 5687 | }; |
| | 5688 | |
| 5679 | if (comp.clang_passthrough_mode) { | 5689 | if (comp.clang_passthrough_mode) { |
| 5680 | var child = std.process.spawn(io, .{ | 5690 | switch (result.term) { |
| 5681 | .argv = argv.items, | | |
| 5682 | .stdin = .inherit, | | |
| 5683 | .stdout = .inherit, | | |
| 5684 | .stderr = .inherit, | | |
| 5685 | }) catch |err| { | | |
| 5686 | return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {t}", .{ | | |
| 5687 | argv.items[0], err, | | |
| 5688 | }); | | |
| 5689 | }; | | |
| 5690 | const term = child.wait(io) catch |err| { | | |
| 5691 | return comp.failCObj(c_object, "failed to wait zig clang (passthrough mode) {s}: {t}", .{ | | |
| 5692 | argv.items[0], err, | | |
| 5693 | }); | | |
| 5694 | }; | | |
| 5695 | switch (term) { | | |
| 5696 | .exited => |code| { | 5691 | .exited => |code| { |
| 5697 | if (code != 0) { | 5692 | if (code != 0) { |
| 5698 | std.process.exit(code); | 5693 | std.process.exit(code); |
| ... | @@ -5705,67 +5700,27 @@ fn updateCObject( | ... | @@ -5705,67 +5700,27 @@ fn updateCObject( |
| 5705 | else => std.process.abort(), | 5700 | else => std.process.abort(), |
| 5706 | } | 5701 | } |
| 5707 | } else { | 5702 | } else { |
| 5708 | var child = std.process.spawn(io, .{ | 5703 | switch (result.term) { |
| 5709 | .argv = argv.items, | | |
| 5710 | .stdin = .ignore, | | |
| 5711 | .stdout = .ignore, | | |
| 5712 | .stderr = .pipe, | | |
| 5713 | }) catch |err| switch (err) { | | |
| 5714 | error.Canceled, | | |
| 5715 | error.OutOfMemory, | | |
| 5716 | => |e| return e, | | |
| 5717 | else => |e| return comp.failCObjRetryable( | | |
| 5718 | c_object, | | |
| 5719 | "failed to spawn zig clang '{s}': {t}", | | |
| 5720 | .{ argv.items[0], e }, | | |
| 5721 | ), | | |
| 5722 | }; | | |
| 5723 | | | |
| 5724 | var stderr_reader = child.stderr.?.readerStreaming(io, &.{}); | | |
| 5725 | const stderr = stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32))) catch |err| switch (err) { | | |
| 5726 | error.OutOfMemory => |e| return e, | | |
| 5727 | error.StreamTooLong => return comp.failCObjRetryable( | | |
| 5728 | c_object, | | |
| 5729 | "failed to read stderr of zig clang '{s}': exceeded maximum size", | | |
| 5730 | .{argv.items[0]}, | | |
| 5731 | ), | | |
| 5732 | error.ReadFailed => return comp.failCObjRetryable( | | |
| 5733 | c_object, | | |
| 5734 | "failed to read stderr of zig clang '{s}': {t}", | | |
| 5735 | .{ argv.items[0], stderr_reader.err.? }, | | |
| 5736 | ), | | |
| 5737 | }; | | |
| 5738 | | | |
| 5739 | const term = child.wait(io) catch |err| switch (err) { | | |
| 5740 | error.Canceled => |e| return e, | | |
| 5741 | else => |e| return comp.failCObj( | | |
| 5742 | c_object, | | |
| 5743 | "failed to spawn zig clang '{s}': {t}", | | |
| 5744 | .{ argv.items[0], e }, | | |
| 5745 | ), | | |
| 5746 | }; | | |
| 5747 | | | |
| 5748 | switch (term) { | | |
| 5749 | .exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| { | 5704 | .exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| { |
| 5750 | const bundle = CObject.Diag.Bundle.parse(gpa, io, diag_file_path) catch |err| { | 5705 | const bundle = CObject.Diag.Bundle.parse(gpa, io, diag_file_path) catch |err| { |
| 5751 | log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr }); | 5706 | log.err("{}: failed to parse clang diagnostics: {s}", .{ err, result.stderr }); |
| 5752 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); | 5707 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); |
| 5753 | }; | 5708 | }; |
| 5754 | return comp.failCObjWithOwnedDiagBundle(c_object, bundle); | 5709 | return comp.failCObjWithOwnedDiagBundle(c_object, bundle); |
| 5755 | } else { | 5710 | } else { |
| 5756 | log.err("clang failed with stderr: {s}", .{stderr}); | 5711 | log.err("clang failed with stderr: {s}", .{result.stderr}); |
| 5757 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); | 5712 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); |
| 5758 | }, | 5713 | }, |
| 5759 | .signal => |sig| { | 5714 | .signal => |sig| { |
| 5760 | log.err("clang failed with stderr: {s}", .{stderr}); | 5715 | log.err("clang failed with stderr: {s}", .{result.stderr}); |
| 5761 | return comp.failCObj(c_object, "clang terminated with signal {t}", .{sig}); | 5716 | return comp.failCObj(c_object, "clang terminated with signal {t}", .{sig}); |
| 5762 | }, | 5717 | }, |
| 5763 | .stopped => |sig| { | 5718 | .stopped => |sig| { |
| 5764 | log.err("clang failed with stderr: {s}", .{stderr}); | 5719 | log.err("clang failed with stderr: {s}", .{result.stderr}); |
| 5765 | return comp.failCObj(c_object, "clang stopped with signal {t}", .{sig}); | 5720 | return comp.failCObj(c_object, "clang stopped with signal {t}", .{sig}); |
| 5766 | }, | 5721 | }, |
| 5767 | .unknown => { | 5722 | .unknown => { |
| 5768 | log.err("clang terminated with stderr: {s}", .{stderr}); | 5723 | log.err("clang terminated with stderr: {s}", .{result.stderr}); |
| 5769 | return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); | 5724 | return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); |
| 5770 | }, | 5725 | }, |
| 5771 | } | 5726 | } |
| ... | @@ -7763,3 +7718,165 @@ pub fn compilerRtOptMode(comp: Compilation) std.lang.Optimize { | ... | @@ -7763,3 +7718,165 @@ pub fn compilerRtOptMode(comp: Compilation) std.lang.Optimize { |
| 7763 | pub fn compilerRtStrip(comp: Compilation) bool { | 7718 | pub fn compilerRtStrip(comp: Compilation) bool { |
| 7764 | return comp.root_mod.strip; | 7719 | return comp.root_mod.strip; |
| 7765 | } | 7720 | } |
| | 7721 | |
| | 7722 | pub const EvalZigLlvmProcessDiagnostics = union(enum) { |
| | 7723 | spawn_err: std.process.SpawnError, |
| | 7724 | read_stderr_err: Io.File.Reader.Error, |
| | 7725 | write_rsp_file_err: struct { |
| | 7726 | path: []const u8, |
| | 7727 | err: (Io.File.OpenError || Io.File.Writer.Error), |
| | 7728 | }, |
| | 7729 | pub fn format(diags: EvalZigLlvmProcessDiagnostics, w: *Writer) Writer.Error!void { |
| | 7730 | switch (diags) { |
| | 7731 | .spawn_err => |err| try w.print( |
| | 7732 | "failed to spawn: {t}", |
| | 7733 | .{err}, |
| | 7734 | ), |
| | 7735 | .read_stderr_err => |err| try w.print( |
| | 7736 | "failed to read stderr: {t}", |
| | 7737 | .{err}, |
| | 7738 | ), |
| | 7739 | .write_rsp_file_err => |write_rsp_file| try w.print( |
| | 7740 | "failed to write '{s}': {t}", |
| | 7741 | .{ write_rsp_file.path, write_rsp_file.err }, |
| | 7742 | ), |
| | 7743 | } |
| | 7744 | } |
| | 7745 | }; |
| | 7746 | pub const EvalZigLlvmProcessResult = struct { |
| | 7747 | term: std.process.Child.Term, |
| | 7748 | stderr: []const u8, |
| | 7749 | }; |
| | 7750 | |
| | 7751 | /// Assumes that `opts.argv` is a Zig LLVM wrapper subcommand (e.g. `zig clang ...`), and attempts |
| | 7752 | /// to spawn it; first trying to spawn it directly, but upon `error.NameTooLong`, falling back to |
| | 7753 | /// writing a response file and passing it to the command (e.g. `zig clang @path/to/args.rsp`). |
| | 7754 | pub fn evalZigLlvmProcess( |
| | 7755 | comp: *const Compilation, |
| | 7756 | arena: Allocator, |
| | 7757 | diags: *EvalZigLlvmProcessDiagnostics, |
| | 7758 | argv: []const []const u8, |
| | 7759 | ) error{ EvalZigLlvmFail, OutOfMemory, Canceled }!EvalZigLlvmProcessResult { |
| | 7760 | const io = comp.io; |
| | 7761 | |
| | 7762 | if (std.process.spawn(io, .{ |
| | 7763 | .argv = argv, |
| | 7764 | .stdin = if (comp.clang_passthrough_mode) .inherit else .ignore, |
| | 7765 | .stdout = if (comp.clang_passthrough_mode) .inherit else .ignore, |
| | 7766 | .stderr = if (comp.clang_passthrough_mode) .inherit else .pipe, |
| | 7767 | })) |child| { |
| | 7768 | return comp.finishEvalZigLlvmProcess(arena, child, diags); |
| | 7769 | } else |err| switch (err) { |
| | 7770 | error.Canceled => |e| return e, |
| | 7771 | error.NameTooLong => {}, // fallback logic below |
| | 7772 | else => |e| { |
| | 7773 | diags.* = .{ .spawn_err = e }; |
| | 7774 | return error.EvalZigLlvmFail; |
| | 7775 | }, |
| | 7776 | } |
| | 7777 | |
| | 7778 | // Main command line was too long; try a response file. |
| | 7779 | const rand_int: u64 = r: { |
| | 7780 | var x: u64 = undefined; |
| | 7781 | io.random(@ptrCast(&x)); |
| | 7782 | break :r x; |
| | 7783 | }; |
| | 7784 | const rsp_cache_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int) ++ ".rsp"; |
| | 7785 | |
| | 7786 | const rsp_file = comp.dirs.local_cache.handle.createFile(io, rsp_cache_sub_path, .{}) catch |err| switch (err) { |
| | 7787 | error.Canceled => |e| return e, |
| | 7788 | else => |e| { |
| | 7789 | diags.* = .{ .write_rsp_file_err = .{ |
| | 7790 | .path = try arena.dupe(u8, rsp_cache_sub_path), |
| | 7791 | .err = e, |
| | 7792 | } }; |
| | 7793 | return error.EvalZigLlvmFail; |
| | 7794 | }, |
| | 7795 | }; |
| | 7796 | defer comp.dirs.local_cache.handle.deleteFile(io, rsp_cache_sub_path) catch |err| |
| | 7797 | log.warn("failed to delete response file {s}: {t}", .{ rsp_cache_sub_path, err }); |
| | 7798 | |
| | 7799 | { |
| | 7800 | defer rsp_file.close(io); |
| | 7801 | var buf: [1024]u8 = undefined; |
| | 7802 | var rsp_fw = rsp_file.writer(io, &buf); |
| | 7803 | writeResponseFile(argv[2..], &rsp_fw.interface) catch |err| switch (err) { |
| | 7804 | error.WriteFailed => switch (rsp_fw.err.?) { |
| | 7805 | error.Canceled => |e| return e, |
| | 7806 | else => |e| { |
| | 7807 | diags.* = .{ .write_rsp_file_err = .{ |
| | 7808 | .path = try arena.dupe(u8, rsp_cache_sub_path), |
| | 7809 | .err = e, |
| | 7810 | } }; |
| | 7811 | return error.EvalZigLlvmFail; |
| | 7812 | }, |
| | 7813 | }, |
| | 7814 | }; |
| | 7815 | } |
| | 7816 | |
| | 7817 | const rsp_arg = try arena.print("@{s}{c}{s}", .{ |
| | 7818 | comp.dirs.local_cache.path orelse ".", |
| | 7819 | fs.path.sep, |
| | 7820 | rsp_cache_sub_path, |
| | 7821 | }); |
| | 7822 | |
| | 7823 | if (std.process.spawn(io, .{ |
| | 7824 | .argv = &.{ argv[0], argv[1], rsp_arg }, |
| | 7825 | .stdin = if (comp.clang_passthrough_mode) .inherit else .ignore, |
| | 7826 | .stdout = if (comp.clang_passthrough_mode) .inherit else .ignore, |
| | 7827 | .stderr = if (comp.clang_passthrough_mode) .inherit else .pipe, |
| | 7828 | })) |child| { |
| | 7829 | return comp.finishEvalZigLlvmProcess(arena, child, diags); |
| | 7830 | } else |err| switch (err) { |
| | 7831 | error.Canceled => |e| return e, |
| | 7832 | else => |e| { |
| | 7833 | diags.* = .{ .spawn_err = e }; |
| | 7834 | return error.EvalZigLlvmFail; |
| | 7835 | }, |
| | 7836 | } |
| | 7837 | } |
| | 7838 | fn writeResponseFile(args: []const []const u8, w: *Writer) Writer.Error!void { |
| | 7839 | for (args) |arg| { |
| | 7840 | try w.writeByte('"'); |
| | 7841 | for (arg) |c| { |
| | 7842 | switch (c) { |
| | 7843 | '\"', '\\' => try w.writeByte('\\'), |
| | 7844 | else => {}, |
| | 7845 | } |
| | 7846 | try w.writeByte(c); |
| | 7847 | } |
| | 7848 | try w.writeAll("\"\n"); |
| | 7849 | } |
| | 7850 | try w.flush(); |
| | 7851 | } |
| | 7852 | fn finishEvalZigLlvmProcess( |
| | 7853 | comp: *const Compilation, |
| | 7854 | arena: Allocator, |
| | 7855 | child_arg: std.process.Child, |
| | 7856 | diags: *EvalZigLlvmProcessDiagnostics, |
| | 7857 | ) error{ EvalZigLlvmFail, OutOfMemory, Canceled }!EvalZigLlvmProcessResult { |
| | 7858 | const io = comp.io; |
| | 7859 | var child = child_arg; |
| | 7860 | defer child.kill(io); |
| | 7861 | if (comp.clang_passthrough_mode) { |
| | 7862 | const term = child.wait(io) catch |err| { |
| | 7863 | diags.* = .{ .spawn_err = err }; |
| | 7864 | return error.EvalZigLlvmFail; |
| | 7865 | }; |
| | 7866 | return .{ .stderr = &.{}, .term = term }; |
| | 7867 | } |
| | 7868 | var stderr_reader = child.stderr.?.readerStreaming(io, &.{}); |
| | 7869 | const stderr = stderr_reader.interface.allocRemaining(arena, .unlimited) catch |err| switch (err) { |
| | 7870 | error.StreamTooLong => unreachable, // unlimited |
| | 7871 | error.OutOfMemory => |e| return e, |
| | 7872 | error.ReadFailed => { |
| | 7873 | diags.* = .{ .read_stderr_err = stderr_reader.err.? }; |
| | 7874 | return error.EvalZigLlvmFail; |
| | 7875 | }, |
| | 7876 | }; |
| | 7877 | const term = child.wait(io) catch |err| { |
| | 7878 | diags.* = .{ .spawn_err = err }; |
| | 7879 | return error.EvalZigLlvmFail; |
| | 7880 | }; |
| | 7881 | return .{ .stderr = stderr, .term = term }; |
| | 7882 | } |