diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig index a002c9c61574..7b8ad93547e3 100644 --- a/lib/compiler/resinator/cli.zig +++ b/lib/compiler/resinator/cli.zig @@ -5,6 +5,7 @@ const lang = @import("lang.zig"); const res = @import("res.zig"); const Allocator = std.mem.Allocator; const lex = @import("lex.zig"); +const cvtres = @import("cvtres.zig"); /// This is what /SL 100 will set the maximum string literal length to pub const max_string_literal_length_100_percent = 8192; @@ -59,6 +60,20 @@ pub const usage_string_after_command_name = \\ the .rc includes or otherwise depends on. \\ /:depfile-fmt Output format of the depfile, if /:depfile is set. \\ json (default) A top-level JSON array of paths + \\ /:input-format If not specified, the input format is inferred. + \\ rc (default if input format cannot be inferred) + \\ res Compiled .rc file, implies /:output-format coff + \\ rcpp Preprocessed .rc file, implies /:no-preprocess + \\ /:output-format If not specified, the output format is inferred. + \\ res (default if output format cannot be inferred) + \\ coff COFF object file (extension: .obj or .o) + \\ rcpp Preprocessed .rc file, implies /p + \\ /:target Set the target machine for COFF object files. + \\ Can be specified either as PE/COFF machine constant + \\ name (X64, ARM64, etc) or Zig/LLVM CPU name (x86_64, + \\ aarch64, etc). The default is X64 (aka x86_64). + \\ Also accepts a full Zig/LLVM triple, but everything + \\ except the architecture is ignored. \\ \\Note: For compatibility reasons, all custom options start with : \\ @@ -131,8 +146,8 @@ pub const Diagnostics = struct { pub const Options = struct { allocator: Allocator, - input_filename: []const u8 = &[_]u8{}, - output_filename: []const u8 = &[_]u8{}, + input_source: IoSource = .{ .filename = &[_]u8{} }, + output_source: IoSource = .{ .filename = &[_]u8{} }, extra_include_paths: std.ArrayListUnmanaged([]const u8) = .empty, ignore_include_env_var: bool = false, preprocess: Preprocess = .yes, @@ -149,9 +164,30 @@ pub const Options = struct { auto_includes: AutoIncludes = .any, depfile_path: ?[]const u8 = null, depfile_fmt: DepfileFormat = .json, + input_format: InputFormat = .rc, + output_format: OutputFormat = .res, + coff_options: cvtres.CoffOptions = .{}, + pub const IoSource = union(enum) { + stdio: std.fs.File, + filename: []const u8, + }; pub const AutoIncludes = enum { any, msvc, gnu, none }; pub const DepfileFormat = enum { json }; + pub const InputFormat = enum { rc, res, rcpp }; + pub const OutputFormat = enum { + res, + coff, + rcpp, + + pub fn extension(format: OutputFormat) []const u8 { + return switch (format) { + .rcpp => ".rcpp", + .coff => ".obj", + .res => ".res", + }; + } + }; pub const Preprocess = enum { no, yes, only }; pub const SymbolAction = enum { define, undefine }; pub const SymbolValue = union(SymbolAction) { @@ -198,9 +234,10 @@ pub const Options = struct { try self.symbols.put(self.allocator, duped_key, .{ .undefine = {} }); } - /// If the current input filename both: + /// If the current input filename: /// - does not have an extension, and - /// - does not exist in the cwd + /// - does not exist in the cwd, and + /// - the input format is .rc /// then this function will append `.rc` to the input filename /// /// Note: This behavior is different from the Win32 compiler. @@ -213,14 +250,18 @@ pub const Options = struct { /// of the .rc extension being omitted from the CLI args, but still /// work fine if the file itself does not have an extension. pub fn maybeAppendRC(options: *Options, cwd: std.fs.Dir) !void { - if (std.fs.path.extension(options.input_filename).len == 0) { - cwd.access(options.input_filename, .{}) catch |err| switch (err) { + switch (options.input_source) { + .stdio => return, + .filename => {}, + } + if (options.input_format == .rc and std.fs.path.extension(options.input_source.filename).len == 0) { + cwd.access(options.input_source.filename, .{}) catch |err| switch (err) { error.FileNotFound => { - var filename_bytes = try options.allocator.alloc(u8, options.input_filename.len + 3); - @memcpy(filename_bytes[0..options.input_filename.len], options.input_filename); + var filename_bytes = try options.allocator.alloc(u8, options.input_source.filename.len + 3); + @memcpy(filename_bytes[0..options.input_source.filename.len], options.input_source.filename); @memcpy(filename_bytes[filename_bytes.len - 3 ..], ".rc"); - options.allocator.free(options.input_filename); - options.input_filename = filename_bytes; + options.allocator.free(options.input_source.filename); + options.input_source = .{ .filename = filename_bytes }; }, else => {}, }; @@ -232,8 +273,14 @@ pub const Options = struct { self.allocator.free(extra_include_path); } self.extra_include_paths.deinit(self.allocator); - self.allocator.free(self.input_filename); - self.allocator.free(self.output_filename); + switch (self.input_source) { + .stdio => {}, + .filename => |filename| self.allocator.free(filename), + } + switch (self.output_source) { + .stdio => {}, + .filename => |filename| self.allocator.free(filename), + } var symbol_it = self.symbols.iterator(); while (symbol_it.next()) |entry| { self.allocator.free(entry.key_ptr.*); @@ -243,11 +290,26 @@ pub const Options = struct { if (self.depfile_path) |depfile_path| { self.allocator.free(depfile_path); } + if (self.coff_options.define_external_symbol) |symbol_name| { + self.allocator.free(symbol_name); + } } pub fn dumpVerbose(self: *const Options, writer: anytype) !void { - try writer.print("Input filename: {s}\n", .{self.input_filename}); - try writer.print("Output filename: {s}\n", .{self.output_filename}); + const input_source_name = switch (self.input_source) { + .stdio => "", + .filename => |filename| filename, + }; + const output_source_name = switch (self.output_source) { + .stdio => "", + .filename => |filename| filename, + }; + try writer.print("Input filename: {s} (format={s})\n", .{ input_source_name, @tagName(self.input_format) }); + try writer.print("Output filename: {s} (format={s})\n", .{ output_source_name, @tagName(self.output_format) }); + if (self.output_format == .coff) { + try writer.print(" Target machine type for COFF: {s}\n", .{@tagName(self.coff_options.target)}); + } + if (self.extra_include_paths.items.len > 0) { try writer.writeAll(" Extra include paths:\n"); for (self.extra_include_paths.items) |extra_include_path| { @@ -331,6 +393,7 @@ pub const Arg = struct { } pub fn optionWithoutPrefix(self: Arg, option_len: usize) []const u8 { + if (option_len == 0) return self.name(); return self.name()[0..option_len]; } @@ -380,6 +443,8 @@ pub const Arg = struct { pub const Value = struct { slice: []const u8, + /// Amount to increment the arg index to skip over both the option and the value arg(s) + /// e.g. 1 if /