Commit 3b5be9fb6e

Jacob Young <jacobly0@users.noreply.github.com>
2024-05-03 16:50:39
lld: use a response file on `NameTooLong`
1 parent 44db92d
Changed files (4)
src/link/Coff/lld.zig
@@ -9,7 +9,6 @@ const Cache = std.Build.Cache;
 
 const mingw = @import("../../mingw.zig");
 const link = @import("../../link.zig");
-const lldMain = @import("../../main.zig").lldMain;
 const trace = @import("../../tracy.zig").trace;
 
 const Allocator = mem.Allocator;
@@ -502,74 +501,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node)
             return error.DllImportLibraryNotFound;
         }
 
-        if (comp.verbose_link) {
-            // Skip over our own name so that the LLD linker name is the first argv item.
-            Compilation.dump_argv(argv.items[1..]);
-        }
-
-        if (std.process.can_spawn) {
-            // If possible, we run LLD as a child process because it does not always
-            // behave properly as a library, unfortunately.
-            // https://github.com/ziglang/zig/issues/3825
-            var child = std.ChildProcess.init(argv.items, arena);
-            if (comp.clang_passthrough_mode) {
-                child.stdin_behavior = .Inherit;
-                child.stdout_behavior = .Inherit;
-                child.stderr_behavior = .Inherit;
-
-                const term = child.spawnAndWait() catch |err| {
-                    log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
-                    return error.UnableToSpawnSelf;
-                };
-                switch (term) {
-                    .Exited => |code| {
-                        if (code != 0) {
-                            std.process.exit(code);
-                        }
-                    },
-                    else => std.process.abort(),
-                }
-            } else {
-                child.stdin_behavior = .Ignore;
-                child.stdout_behavior = .Ignore;
-                child.stderr_behavior = .Pipe;
-
-                try child.spawn();
-
-                const stderr = try child.stderr.?.reader().readAllAlloc(arena, std.math.maxInt(usize));
-
-                const term = child.wait() catch |err| {
-                    log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
-                    return error.UnableToSpawnSelf;
-                };
-
-                switch (term) {
-                    .Exited => |code| {
-                        if (code != 0) {
-                            comp.lockAndParseLldStderr(linker_command, stderr);
-                            return error.LLDReportedFailure;
-                        }
-                    },
-                    else => {
-                        log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
-                        return error.LLDCrashed;
-                    },
-                }
-
-                if (stderr.len != 0) {
-                    log.warn("unexpected LLD stderr:\n{s}", .{stderr});
-                }
-            }
-        } else {
-            const exit_code = try lldMain(arena, argv.items, false);
-            if (exit_code != 0) {
-                if (comp.clang_passthrough_mode) {
-                    std.process.exit(exit_code);
-                } else {
-                    return error.LLDReportedFailure;
-                }
-            }
-        }
+        try link.spawnLld(comp, arena, argv.items);
     }
 
     if (!self.base.disable_lld_caching) {
src/link/Elf.zig
@@ -2726,74 +2726,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi
             try argv.append("-Bsymbolic");
         }
 
-        if (comp.verbose_link) {
-            // Skip over our own name so that the LLD linker name is the first argv item.
-            Compilation.dump_argv(argv.items[1..]);
-        }
-
-        if (std.process.can_spawn) {
-            // If possible, we run LLD as a child process because it does not always
-            // behave properly as a library, unfortunately.
-            // https://github.com/ziglang/zig/issues/3825
-            var child = std.ChildProcess.init(argv.items, arena);
-            if (comp.clang_passthrough_mode) {
-                child.stdin_behavior = .Inherit;
-                child.stdout_behavior = .Inherit;
-                child.stderr_behavior = .Inherit;
-
-                const term = child.spawnAndWait() catch |err| {
-                    log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
-                    return error.UnableToSpawnSelf;
-                };
-                switch (term) {
-                    .Exited => |code| {
-                        if (code != 0) {
-                            std.process.exit(code);
-                        }
-                    },
-                    else => std.process.abort(),
-                }
-            } else {
-                child.stdin_behavior = .Ignore;
-                child.stdout_behavior = .Ignore;
-                child.stderr_behavior = .Pipe;
-
-                try child.spawn();
-
-                const stderr = try child.stderr.?.reader().readAllAlloc(arena, std.math.maxInt(usize));
-
-                const term = child.wait() catch |err| {
-                    log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
-                    return error.UnableToSpawnSelf;
-                };
-
-                switch (term) {
-                    .Exited => |code| {
-                        if (code != 0) {
-                            comp.lockAndParseLldStderr(linker_command, stderr);
-                            return error.LLDReportedFailure;
-                        }
-                    },
-                    else => {
-                        log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
-                        return error.LLDCrashed;
-                    },
-                }
-
-                if (stderr.len != 0) {
-                    log.warn("unexpected LLD stderr:\n{s}", .{stderr});
-                }
-            }
-        } else {
-            const exit_code = try lldMain(arena, argv.items, false);
-            if (exit_code != 0) {
-                if (comp.clang_passthrough_mode) {
-                    std.process.exit(exit_code);
-                } else {
-                    return error.LLDReportedFailure;
-                }
-            }
-        }
+        try link.spawnLld(comp, arena, argv.items);
     }
 
     if (!self.base.disable_lld_caching) {
@@ -6500,7 +6433,6 @@ const eh_frame = @import("Elf/eh_frame.zig");
 const gc = @import("Elf/gc.zig");
 const glibc = @import("../glibc.zig");
 const link = @import("../link.zig");
-const lldMain = @import("../main.zig").lldMain;
 const merge_section = @import("Elf/merge_section.zig");
 const musl = @import("../musl.zig");
 const relocatable = @import("Elf/relocatable.zig");
src/Compilation.zig
@@ -5091,7 +5091,7 @@ fn spawnZigRc(
     }
 }
 
-pub fn tmpFilePath(comp: *Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
+pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
     const s = std.fs.path.sep_str;
     const rand_int = std.crypto.random.int(u64);
     if (comp.local_cache_directory.path) |p| {
@@ -5894,14 +5894,16 @@ pub fn lockAndSetMiscFailure(
     return setMiscFailure(comp, tag, format, args);
 }
 
-fn parseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []const u8) Allocator.Error!void {
+fn parseLldStderr(comp: *Compilation, prefix: []const u8, stderr: []const u8) Allocator.Error!void {
     var context_lines = std.ArrayList([]const u8).init(comp.gpa);
     defer context_lines.deinit();
 
     var current_err: ?*LldError = null;
     var lines = mem.splitSequence(u8, stderr, if (builtin.os.tag == .windows) "\r\n" else "\n");
     while (lines.next()) |line| {
-        if (mem.startsWith(u8, line, prefix ++ ":")) {
+        if (line.len > prefix.len + ":".len and
+            mem.eql(u8, line[0..prefix.len], prefix) and line[prefix.len] == ':')
+        {
             if (current_err) |err| {
                 err.context_lines = try context_lines.toOwnedSlice();
             }
@@ -5933,7 +5935,7 @@ fn parseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []con
     }
 }
 
-pub fn lockAndParseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []const u8) void {
+pub fn lockAndParseLldStderr(comp: *Compilation, prefix: []const u8, stderr: []const u8) void {
     comp.mutex.lock();
     defer comp.mutex.unlock();
 
src/link.zig
@@ -19,6 +19,8 @@ const InternPool = @import("InternPool.zig");
 const Type = @import("type.zig").Type;
 const Value = @import("Value.zig");
 const LlvmObject = @import("codegen/llvm.zig").Object;
+const lldMain = @import("main.zig").lldMain;
+const Package = @import("Package.zig");
 
 /// When adding a new field, remember to update `hashAddSystemLibs`.
 /// These are *always* dynamically linked. Static libraries will be
@@ -982,3 +984,111 @@ pub const File = struct {
     pub const NvPtx = @import("link/NvPtx.zig");
     pub const Dwarf = @import("link/Dwarf.zig");
 };
+
+pub fn spawnLld(
+    comp: *Compilation,
+    arena: Allocator,
+    argv: []const []const u8,
+) !void {
+    if (comp.verbose_link) {
+        // Skip over our own name so that the LLD linker name is the first argv item.
+        Compilation.dump_argv(argv[1..]);
+    }
+
+    // If possible, we run LLD as a child process because it does not always
+    // behave properly as a library, unfortunately.
+    // https://github.com/ziglang/zig/issues/3825
+    if (!std.process.can_spawn) {
+        const exit_code = try lldMain(arena, argv, false);
+        if (exit_code == 0) return;
+        if (comp.clang_passthrough_mode) std.process.exit(exit_code);
+        return error.LLDReportedFailure;
+    }
+
+    var stderr: []u8 = &.{};
+    defer comp.gpa.free(stderr);
+
+    var child = std.process.Child.init(argv, arena);
+    const term = (if (comp.clang_passthrough_mode) term: {
+        child.stdin_behavior = .Inherit;
+        child.stdout_behavior = .Inherit;
+        child.stderr_behavior = .Inherit;
+
+        break :term child.spawnAndWait();
+    } else term: {
+        child.stdin_behavior = .Ignore;
+        child.stdout_behavior = .Ignore;
+        child.stderr_behavior = .Pipe;
+
+        child.spawn() catch |err| break :term err;
+        stderr = try child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize));
+        break :term child.wait();
+    }) catch |first_err| term: {
+        const err = switch (first_err) {
+            error.NameTooLong => err: {
+                const s = fs.path.sep_str;
+                const rand_int = std.crypto.random.int(u64);
+                const rsp_path = "tmp" ++ s ++ Package.Manifest.hex64(rand_int) ++ ".rsp";
+
+                const rsp_file = try comp.local_cache_directory.handle.createFileZ(rsp_path, .{});
+                defer comp.local_cache_directory.handle.deleteFileZ(rsp_path) catch |err|
+                    log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) });
+                {
+                    defer rsp_file.close();
+                    var rsp_buf = std.io.bufferedWriter(rsp_file.writer());
+                    const rsp_writer = rsp_buf.writer();
+                    for (argv[2..]) |arg| {
+                        try rsp_writer.writeByte('"');
+                        for (arg) |c| {
+                            switch (c) {
+                                '\"', '\\' => try rsp_writer.writeByte('\\'),
+                                else => {},
+                            }
+                            try rsp_writer.writeByte(c);
+                        }
+                        try rsp_writer.writeByte('"');
+                        try rsp_writer.writeByte('\n');
+                    }
+                    try rsp_buf.flush();
+                }
+
+                var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint(
+                    arena,
+                    "@{s}",
+                    .{try comp.local_cache_directory.join(arena, &.{rsp_path})},
+                ) }, arena);
+                if (comp.clang_passthrough_mode) {
+                    rsp_child.stdin_behavior = .Inherit;
+                    rsp_child.stdout_behavior = .Inherit;
+                    rsp_child.stderr_behavior = .Inherit;
+
+                    break :term rsp_child.spawnAndWait() catch |err| break :err err;
+                } else {
+                    rsp_child.stdin_behavior = .Ignore;
+                    rsp_child.stdout_behavior = .Ignore;
+                    rsp_child.stderr_behavior = .Pipe;
+
+                    rsp_child.spawn() catch |err| break :err err;
+                    stderr = try rsp_child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize));
+                    break :term rsp_child.wait() catch |err| break :err err;
+                }
+            },
+            else => first_err,
+        };
+        log.err("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
+        return error.UnableToSpawnSelf;
+    };
+
+    switch (term) {
+        .Exited => |code| if (code != 0) {
+            comp.lockAndParseLldStderr(argv[1], stderr);
+            return error.LLDReportedFailure;
+        },
+        else => {
+            log.err("{s} terminated with stderr:\n{s}", .{ argv[0], stderr });
+            return error.LLDCrashed;
+        },
+    }
+
+    if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});
+}