| ... | ... | @@ -5676,23 +5676,18 @@ fn updateCObject( |
| 5676 | 5676 | else => log.warn("failed to delete {q}: {t}", .{ dep_file_path, err }), |
| 5677 | 5677 | }; |
| 5678 | 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 | 5689 | if (comp.clang_passthrough_mode) { |
| 5680 | | var child = std.process.spawn(io, .{ |
| 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) { |
| 5690 | switch (result.term) { |
| 5696 | 5691 | .exited => |code| { |
| 5697 | 5692 | if (code != 0) { |
| 5698 | 5693 | std.process.exit(code); |
| ... | ... | @@ -5705,67 +5700,27 @@ fn updateCObject( |
| 5705 | 5700 | else => std.process.abort(), |
| 5706 | 5701 | } |
| 5707 | 5702 | } else { |
| 5708 | | var child = std.process.spawn(io, .{ |
| 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) { |
| 5703 | switch (result.term) { |
| 5749 | 5704 | .exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| { |
| 5750 | 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 | 5707 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); |
| 5753 | 5708 | }; |
| 5754 | 5709 | return comp.failCObjWithOwnedDiagBundle(c_object, bundle); |
| 5755 | 5710 | } else { |
| 5756 | | log.err("clang failed with stderr: {s}", .{stderr}); |
| 5711 | log.err("clang failed with stderr: {s}", .{result.stderr}); |
| 5757 | 5712 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); |
| 5758 | 5713 | }, |
| 5759 | 5714 | .signal => |sig| { |
| 5760 | | log.err("clang failed with stderr: {s}", .{stderr}); |
| 5715 | log.err("clang failed with stderr: {s}", .{result.stderr}); |
| 5761 | 5716 | return comp.failCObj(c_object, "clang terminated with signal {t}", .{sig}); |
| 5762 | 5717 | }, |
| 5763 | 5718 | .stopped => |sig| { |
| 5764 | | log.err("clang failed with stderr: {s}", .{stderr}); |
| 5719 | log.err("clang failed with stderr: {s}", .{result.stderr}); |
| 5765 | 5720 | return comp.failCObj(c_object, "clang stopped with signal {t}", .{sig}); |
| 5766 | 5721 | }, |
| 5767 | 5722 | .unknown => { |
| 5768 | | log.err("clang terminated with stderr: {s}", .{stderr}); |
| 5723 | log.err("clang terminated with stderr: {s}", .{result.stderr}); |
| 5769 | 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 | 7718 | pub fn compilerRtStrip(comp: Compilation) bool { |
| 7764 | 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 | } |