Skip to content

Commit

Permalink
Add cvtres subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
squeek502 committed Nov 6, 2024
1 parent 188e10b commit 90f2587
Show file tree
Hide file tree
Showing 5 changed files with 300 additions and 11 deletions.
34 changes: 26 additions & 8 deletions src/cli.zig
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ pub const usage_string2_after_command_name =
\\
\\Subcommands:
\\ targets Output a list of all supported /:target values.
\\ cvtres A .res to .obj (COFF object file) converter that
\\ has drop-in CLI compatibility with cvtres.exe.
\\ Use the /? option with this subcommand to see the usage.
\\
;

Expand Down Expand Up @@ -178,8 +181,12 @@ pub const Options = struct {
depfile_fmt: DepfileFormat = .json,
input_format: InputFormat = .rc,
output_format: OutputFormat = .res,
target: std.coff.MachineType = .X64,
coff_options: cvtres.CoffOptions = .{},
/// Currently only used by the cvtres subcommand
additional_inputs: std.ArrayListUnmanaged([]const u8) = .empty,
subcommand: Subcommand = .none,

pub const Subcommand = enum { none, targets, cvtres };
pub const AutoIncludes = enum { any, msvc, gnu, none };
pub const DepfileFormat = enum { json };
pub const InputFormat = enum { rc, res, rcpp };
Expand Down Expand Up @@ -288,14 +295,25 @@ pub const Options = struct {
if (self.depfile_path) |depfile_path| {
self.allocator.free(depfile_path);
}
for (self.additional_inputs.items) |additional_input| {
self.allocator.free(additional_input);
}
self.additional_inputs.deinit(self.allocator);
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} (format={s})\n", .{ self.input_filename, @tagName(self.input_format) });
try writer.print("Output filename: {s} (format={s})\n", .{ self.output_filename, @tagName(self.output_format) });
if (self.output_format == .coff) {
try writer.print(" Target machine type for COFF: {s}\n", .{@tagName(self.target)});
try writer.print(" Target machine type for COFF: {s}\n", .{@tagName(self.coff_options.target)});
}

// The rest is irrelevant for the cvtres subcommand
if (self.subcommand == .cvtres) return;

if (self.extra_include_paths.items.len > 0) {
try writer.writeAll(" Extra include paths:\n");
for (self.extra_include_paths.items) |extra_include_path| {
Expand Down Expand Up @@ -606,12 +624,12 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
const arch = cvtres.supported_targets.Arch.fromStringIgnoreCase(arch_str) orelse {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator);
try msg_writer.print("invalid or unsupported target architecture: {s} ", .{arch_str});
try msg_writer.print("invalid or unsupported target architecture: {s}", .{arch_str});
try diagnostics.append(err_details);
arg_i += value.index_increment;
continue :next_arg;
};
options.target = arch.toCoffMachineType();
options.coff_options.target = arch.toCoffMachineType();
arg_i += value.index_increment;
continue :next_arg;
} else if (std.ascii.startsWithIgnoreCase(arg_name, "nologo")) {
Expand Down Expand Up @@ -1014,7 +1032,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
if (arg_i > 0 and last_arg.len > 0 and last_arg[0] == '/' and isSupportedInputExtension(std.fs.path.extension(last_arg))) {
var note_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = true, .arg_index = arg_i - 1 };
var note_writer = note_details.msg.writer(allocator);
try note_writer.writeAll("if this argument was intended to be the input filename, then -- should be specified in front of it to exclude it from option parsing");
try note_writer.writeAll("if this argument was intended to be the input filename, adding -- in front of it will exclude it from option parsing");
try diagnostics.append(note_details);
}
}
Expand Down Expand Up @@ -1576,7 +1594,7 @@ test "parse errors: basic" {
\\ ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\\<cli>: error: missing input filename
\\
\\<cli>: note: if this argument was intended to be the input filename, then -- should be specified in front of it to exclude it from option parsing
\\<cli>: note: if this argument was intended to be the input filename, adding -- in front of it will exclude it from option parsing
\\ ... /some/absolute/path/parsed/as/an/option.rc
\\ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\\
Expand All @@ -1587,7 +1605,7 @@ test "parse errors: basic" {
\\ ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\\<cli>: error: missing input filename
\\
\\<cli>: note: if this argument was intended to be the input filename, then -- should be specified in front of it to exclude it from option parsing
\\<cli>: note: if this argument was intended to be the input filename, adding -- in front of it will exclude it from option parsing
\\ ... /some/absolute/path/parsed/as/an/option.res
\\ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\\
Expand All @@ -1598,7 +1616,7 @@ test "parse errors: basic" {
\\ ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\\<cli>: error: missing input filename
\\
\\<cli>: note: if this argument was intended to be the input filename, then -- should be specified in front of it to exclude it from option parsing
\\<cli>: note: if this argument was intended to be the input filename, adding -- in front of it will exclude it from option parsing
\\ ... /some/absolute/path/parsed/as/an/option.rcpp
\\ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\\
Expand Down
2 changes: 2 additions & 0 deletions src/cvtres.zig
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ pub const CoffOptions = struct {
read_only: bool = false,
/// If non-null, a symbol with this name and storage class EXTERNAL will be added to the symbol table.
define_external_symbol: ?[]const u8 = null,
/// Re-use data offsets for resources with data that is identical.
fold_duplicate_data: bool = false,
};

pub fn writeCoff(allocator: Allocator, writer: anytype, resources: []const Resource, options: CoffOptions) !void {
Expand Down
33 changes: 30 additions & 3 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,33 @@ pub fn main() !void {
if (args.len > 0 and std.mem.eql(u8, args[0], "targets")) {
try subcommands.targets.run();
return;
} else if (args.len > 0 and std.mem.eql(u8, args[0], "cvtres")) {
const subcommand_args = args[1..];
var cli_diagnostics = cli.Diagnostics.init(allocator);
defer cli_diagnostics.deinit();
const options = subcommands.cvtres.parseCli(allocator, subcommand_args, &cli_diagnostics) catch |err| switch (err) {
error.ParseError => {
cli_diagnostics.renderToStdErr(subcommand_args, stderr_config);
std.process.exit(1);
},
else => |e| return e,
};

// print any warnings/notes
cli_diagnostics.renderToStdErr(subcommand_args, stderr_config);
// If there was something printed, then add an extra newline separator
// so that there is a clear separation between the cli diagnostics and whatever
// gets printed after
if (cli_diagnostics.errors.items.len > 0) {
std.debug.print("\n", .{});
}

if (options.print_help_and_exit) {
try subcommands.cvtres.writeUsage(stderr.writer(), "resinator cvtres");
return;
}

break :options options;
}

var cli_diagnostics = cli.Diagnostics.init(allocator);
Expand Down Expand Up @@ -173,6 +200,8 @@ pub fn main() !void {
const need_intermediate_res = options.output_format == .coff and options.input_format != .res;
const res_filename = if (need_intermediate_res)
try cli.filepathWithExtension(allocator, options.input_filename, ".res")
else if (options.input_format == .res)
options.input_filename
else
options.output_filename;
defer if (need_intermediate_res) allocator.free(res_filename);
Expand Down Expand Up @@ -352,9 +381,7 @@ pub fn main() !void {

var coff_output_buffered_stream = std.io.bufferedWriter(coff_output_file.writer());

cvtres.writeCoff(allocator, coff_output_buffered_stream.writer(), resources, .{
.target = options.target,
}) catch |err| {
cvtres.writeCoff(allocator, coff_output_buffered_stream.writer(), resources, options.coff_options) catch |err| {
// TODO: Better errors
try renderErrorMessage(stderr.writer(), stderr_config, .err, "unable to write coff output file '{s}': {s}", .{ coff_output_filename, @errorName(err) });
// Delete the output file on error
Expand Down
1 change: 1 addition & 0 deletions src/subcommands.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const std = @import("std");

pub const targets = @import("subcommands/targets.zig");
pub const cvtres = @import("subcommands/cvtres.zig");

test {
_ = std.testing.refAllDecls(@This());
Expand Down
Loading

0 comments on commit 90f2587

Please sign in to comment.