diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 028dc21df..0613634c6 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -114,6 +114,7 @@ const Error = extern enum { InvalidAbiVersion, InvalidOperatingSystemVersion, UnknownClangOption, + NestedResponseFile, }; const FILE = std.c.FILE; @@ -1259,6 +1260,7 @@ pub const ClangArgIterator = extern struct { argv_ptr: [*]const [*:0]const u8, argv_len: usize, next_index: usize, + root_args: ?*Args, // ABI warning pub const ZigEquivalent = extern enum { @@ -1289,7 +1291,13 @@ pub const ClangArgIterator = extern struct { no_rtti, }; - fn init(argv: []const [*:0]const u8) ClangArgIterator { + const Args = struct { + next_index: usize, + argv_ptr: [*]const [*:0]const u8, + argv_len: usize, + }; + + pub fn init(argv: []const [*:0]const u8) ClangArgIterator { return .{ .next_index = 2, // `zig cc foo` this points to `foo` .has_next = argv.len > 2, @@ -1300,22 +1308,74 @@ pub const ClangArgIterator = extern struct { .other_args_len = undefined, .argv_ptr = argv.ptr, .argv_len = argv.len, + .root_args = null, }; } - fn next(self: *ClangArgIterator) !void { + pub fn next(self: *ClangArgIterator) !void { assert(self.has_next); assert(self.next_index < self.argv_len); // In this state we know that the parameter we are looking at is a root parameter // rather than an argument to a parameter. self.other_args_ptr = self.argv_ptr + self.next_index; self.other_args_len = 1; // We adjust this value below when necessary. - const arg = mem.span(self.argv_ptr[self.next_index]); - self.next_index += 1; - defer { - if (self.next_index >= self.argv_len) self.has_next = false; - } + var arg = mem.span(self.argv_ptr[self.next_index]); + self.incrementArgIndex(); + if (mem.startsWith(u8, arg, "@")) { + if (self.root_args != null) return error.NestedResponseFile; + + // This is a "compiler response file". We must parse the file and treat its + // contents as command line parameters. + const allocator = std.heap.c_allocator; + const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit + const resp_file_path = arg[1..]; + const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| { + std.debug.warn("unable to read response file '{}': {}\n", .{ resp_file_path, @errorName(err) }); + process.exit(1); + }; + defer allocator.free(resp_contents); + // TODO is there a specification for this file format? Let's find it and make this parsing more robust + // at the very least I'm guessing this needs to handle quotes and `#` comments. + var it = mem.tokenize(resp_contents, " \t\r\n"); + var resp_arg_list = std.ArrayList([*:0]const u8).init(allocator); + defer resp_arg_list.deinit(); + { + errdefer { + for (resp_arg_list.span()) |item| { + allocator.free(mem.span(item)); + } + } + while (it.next()) |token| { + const dupe_token = try mem.dupeZ(allocator, u8, token); + errdefer allocator.free(dupe_token); + try resp_arg_list.append(dupe_token); + } + const args = try allocator.create(Args); + errdefer allocator.destroy(args); + args.* = .{ + .next_index = self.next_index, + .argv_ptr = self.argv_ptr, + .argv_len = self.argv_len, + }; + self.root_args = args; + } + const resp_arg_slice = resp_arg_list.toOwnedSlice(); + self.next_index = 0; + self.argv_ptr = resp_arg_slice.ptr; + self.argv_len = resp_arg_slice.len; + + if (resp_arg_slice.len == 0) { + self.resolveRespFileArgs(); + return; + } + + self.has_next = true; + self.other_args_ptr = self.argv_ptr + self.next_index; + self.other_args_len = 1; // We adjust this value below when necessary. + arg = mem.span(self.argv_ptr[self.next_index]); + self.incrementArgIndex(); + } if (!mem.startsWith(u8, arg, "-")) { self.zig_equivalent = .positional; self.only_arg = arg.ptr; @@ -1352,7 +1412,7 @@ pub const ClangArgIterator = extern struct { process.exit(1); } self.only_arg = self.argv_ptr[self.next_index]; - self.next_index += 1; + self.incrementArgIndex(); self.other_args_len += 1; self.zig_equivalent = clang_arg.zig_equivalent; @@ -1374,7 +1434,7 @@ pub const ClangArgIterator = extern struct { process.exit(1); } self.second_arg = self.argv_ptr[self.next_index]; - self.next_index += 1; + self.incrementArgIndex(); self.other_args_len += 1; self.zig_equivalent = clang_arg.zig_equivalent; break :find_clang_arg; @@ -1386,7 +1446,7 @@ pub const ClangArgIterator = extern struct { process.exit(1); } self.only_arg = self.argv_ptr[self.next_index]; - self.next_index += 1; + self.incrementArgIndex(); self.other_args_len += 1; self.zig_equivalent = clang_arg.zig_equivalent; break :find_clang_arg; @@ -1406,6 +1466,28 @@ pub const ClangArgIterator = extern struct { process.exit(1); } } + + fn incrementArgIndex(self: *ClangArgIterator) void { + self.next_index += 1; + self.resolveRespFileArgs(); + } + + fn resolveRespFileArgs(self: *ClangArgIterator) void { + const allocator = std.heap.c_allocator; + if (self.next_index >= self.argv_len) { + if (self.root_args) |root_args| { + self.next_index = root_args.next_index; + self.argv_ptr = root_args.argv_ptr; + self.argv_len = root_args.argv_len; + + allocator.destroy(root_args); + self.root_args = null; + } + if (self.next_index >= self.argv_len) { + self.has_next = false; + } + } + } }; export fn stage2_clang_arg_iterator( @@ -1418,7 +1500,8 @@ export fn stage2_clang_arg_iterator( export fn stage2_clang_arg_next(it: *ClangArgIterator) Error { it.next() catch |err| switch (err) { - error.UnknownClangOption => return .UnknownClangOption, + error.NestedResponseFile => return .NestedResponseFile, + error.OutOfMemory => return .OutOfMemory, }; return .None; } diff --git a/src/error.cpp b/src/error.cpp index c246ea5f3..76b510ecf 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -84,6 +84,7 @@ const char *err_str(Error err) { case ErrorInvalidAbiVersion: return "invalid C ABI version"; case ErrorInvalidOperatingSystemVersion: return "invalid operating system version"; case ErrorUnknownClangOption: return "unknown Clang option"; + case ErrorNestedResponseFile: return "nested response file"; } return "(invalid error)"; } diff --git a/src/stage2.h b/src/stage2.h index 9549f45ff..ed7caebcb 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -106,6 +106,7 @@ enum Error { ErrorInvalidAbiVersion, ErrorInvalidOperatingSystemVersion, ErrorUnknownClangOption, + ErrorNestedResponseFile, }; // ABI warning @@ -361,6 +362,7 @@ struct Stage2ClangArgIterator { const char **argv_ptr; size_t argv_len; size_t next_index; + size_t root_args; }; // ABI warning