Skip to content

Commit 160445e

Browse files
authored
Merge pull request #22522 from squeek502/resinator-sync
resinator: Sync with upstream
2 parents 0d6b17b + 289e9c3 commit 160445e

17 files changed

+2145
-949
lines changed

lib/compiler/resinator/ast.zig

+49-49
Large diffs are not rendered by default.

lib/compiler/resinator/bmp.zig

+10-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,16 @@ pub const BitmapInfo = struct {
6060
}
6161

6262
pub fn getBitmasksByteLen(self: *const BitmapInfo) u8 {
63-
return switch (self.compression) {
64-
.BI_BITFIELDS => 12,
65-
.BI_ALPHABITFIELDS => 16,
63+
// Only BITMAPINFOHEADER (3.1) has trailing bytes for the BITFIELDS
64+
// The 2.0 format doesn't have a compression field and 4.0+ has dedicated
65+
// fields for the masks in the header.
66+
const dib_version = BitmapHeader.Version.get(self.dib_header_size);
67+
return switch (dib_version) {
68+
.@"nt3.1" => switch (self.compression) {
69+
.BI_BITFIELDS => 12,
70+
.BI_ALPHABITFIELDS => 16,
71+
else => 0,
72+
},
6673
else => 0,
6774
};
6875
}

lib/compiler/resinator/cli.zig

+92-35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const std = @import("std");
2-
const CodePage = @import("code_pages.zig").CodePage;
2+
const code_pages = @import("code_pages.zig");
3+
const SupportedCodePage = code_pages.SupportedCodePage;
34
const lang = @import("lang.zig");
45
const res = @import("res.zig");
56
const Allocator = std.mem.Allocator;
@@ -14,6 +15,8 @@ pub const usage_string_after_command_name =
1415
\\The sequence -- can be used to signify when to stop parsing options.
1516
\\This is necessary when the input path begins with a forward slash.
1617
\\
18+
\\Supported option prefixes are /, -, and --, so e.g. /h, -h, and --h all work.
19+
\\
1720
\\Supported Win32 RC Options:
1821
\\ /?, /h Print this help and exit.
1922
\\ /v Verbose (print progress messages).
@@ -56,8 +59,6 @@ pub const usage_string_after_command_name =
5659
\\ the .rc includes or otherwise depends on.
5760
\\ /:depfile-fmt <value> Output format of the depfile, if /:depfile is set.
5861
\\ json (default) A top-level JSON array of paths
59-
\\ /:mingw-includes <path> Path to a directory containing MinGW include files. If
60-
\\ not specified, bundled MinGW include files will be used.
6162
\\
6263
\\Note: For compatibility reasons, all custom options start with :
6364
\\
@@ -136,7 +137,7 @@ pub const Options = struct {
136137
ignore_include_env_var: bool = false,
137138
preprocess: Preprocess = .yes,
138139
default_language_id: ?u16 = null,
139-
default_code_page: ?CodePage = null,
140+
default_code_page: ?SupportedCodePage = null,
140141
verbose: bool = false,
141142
symbols: std.StringArrayHashMapUnmanaged(SymbolValue) = .empty,
142143
null_terminate_string_table_strings: bool = false,
@@ -148,7 +149,6 @@ pub const Options = struct {
148149
auto_includes: AutoIncludes = .any,
149150
depfile_path: ?[]const u8 = null,
150151
depfile_fmt: DepfileFormat = .json,
151-
mingw_includes_dir: ?[]const u8 = null,
152152

153153
pub const AutoIncludes = enum { any, msvc, gnu, none };
154154
pub const DepfileFormat = enum { json };
@@ -243,9 +243,6 @@ pub const Options = struct {
243243
if (self.depfile_path) |depfile_path| {
244244
self.allocator.free(depfile_path);
245245
}
246-
if (self.mingw_includes_dir) |mingw_includes_dir| {
247-
self.allocator.free(mingw_includes_dir);
248-
}
249246
}
250247

251248
pub fn dumpVerbose(self: *const Options, writer: anytype) !void {
@@ -358,6 +355,29 @@ pub const Arg = struct {
358355
};
359356
}
360357

358+
pub fn looksLikeFilepath(self: Arg) bool {
359+
const meets_min_requirements = self.prefix == .slash and isSupportedInputExtension(std.fs.path.extension(self.full));
360+
if (!meets_min_requirements) return false;
361+
362+
const could_be_fo_option = could_be_fo_option: {
363+
var window_it = std.mem.window(u8, self.full[1..], 2, 1);
364+
while (window_it.next()) |window| {
365+
if (std.ascii.eqlIgnoreCase(window, "fo")) break :could_be_fo_option true;
366+
// If we see '/' before "fo", then it's not possible for this to be a valid
367+
// `/fo` option.
368+
if (window[0] == '/') break;
369+
}
370+
break :could_be_fo_option false;
371+
};
372+
if (!could_be_fo_option) return true;
373+
374+
// It's still possible for a file path to look like a /fo option but not actually
375+
// be one, e.g. `/foo/bar.rc`. As a last ditch effort to reduce false negatives,
376+
// check if the file path exists and, if so, then we ignore the 'could be /fo option'-ness
377+
std.fs.accessAbsolute(self.full, .{}) catch return false;
378+
return true;
379+
}
380+
361381
pub const Value = struct {
362382
slice: []const u8,
363383
index_increment: u2 = 1,
@@ -432,6 +452,16 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
432452
}
433453
}
434454

455+
const args_remaining = args.len - arg_i;
456+
if (args_remaining <= 2 and arg.looksLikeFilepath()) {
457+
var err_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = true, .arg_index = arg_i };
458+
var msg_writer = err_details.msg.writer(allocator);
459+
try msg_writer.writeAll("this argument was inferred to be a filepath, so argument parsing was terminated");
460+
try diagnostics.append(err_details);
461+
462+
break;
463+
}
464+
435465
while (arg.name().len > 0) {
436466
const arg_name = arg.name();
437467
// Note: These cases should be in order from longest to shortest, since
@@ -440,24 +470,6 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
440470
if (std.ascii.startsWithIgnoreCase(arg_name, ":no-preprocess")) {
441471
options.preprocess = .no;
442472
arg.name_offset += ":no-preprocess".len;
443-
} else if (std.ascii.startsWithIgnoreCase(arg_name, ":mingw-includes")) {
444-
const value = arg.value(":mingw-includes".len, arg_i, args) catch {
445-
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
446-
var msg_writer = err_details.msg.writer(allocator);
447-
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":mingw-includes".len) });
448-
try diagnostics.append(err_details);
449-
arg_i += 1;
450-
break :next_arg;
451-
};
452-
if (options.mingw_includes_dir) |overwritten_path| {
453-
allocator.free(overwritten_path);
454-
options.mingw_includes_dir = null;
455-
}
456-
const path = try allocator.dupe(u8, value.slice);
457-
errdefer allocator.free(path);
458-
options.mingw_includes_dir = path;
459-
arg_i += value.index_increment;
460-
continue :next_arg;
461473
} else if (std.ascii.startsWithIgnoreCase(arg_name, ":auto-includes")) {
462474
const value = arg.value(":auto-includes".len, arg_i, args) catch {
463475
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
@@ -769,7 +781,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
769781
arg_i += value.index_increment;
770782
continue :next_arg;
771783
};
772-
options.default_code_page = CodePage.getByIdentifierEnsureSupported(code_page_id) catch |err| switch (err) {
784+
options.default_code_page = code_pages.getByIdentifierEnsureSupported(code_page_id) catch |err| switch (err) {
773785
error.InvalidCodePage => {
774786
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
775787
var msg_writer = err_details.msg.writer(allocator);
@@ -782,7 +794,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
782794
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
783795
var msg_writer = err_details.msg.writer(allocator);
784796
try msg_writer.print("unsupported code page: {s} (id={})", .{
785-
@tagName(CodePage.getByIdentifier(code_page_id) catch unreachable),
797+
@tagName(code_pages.getByIdentifier(code_page_id) catch unreachable),
786798
code_page_id,
787799
});
788800
try diagnostics.append(err_details);
@@ -900,18 +912,20 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
900912

901913
const positionals = args[arg_i..];
902914

903-
if (positionals.len < 1) {
915+
if (positionals.len == 0) {
904916
var err_details = Diagnostics.ErrorDetails{ .print_args = false, .arg_index = arg_i };
905917
var msg_writer = err_details.msg.writer(allocator);
906918
try msg_writer.writeAll("missing input filename");
907919
try diagnostics.append(err_details);
908920

909-
const last_arg = args[args.len - 1];
910-
if (arg_i > 0 and last_arg.len > 0 and last_arg[0] == '/' and std.ascii.endsWithIgnoreCase(last_arg, ".rc")) {
911-
var note_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = true, .arg_index = arg_i - 1 };
912-
var note_writer = note_details.msg.writer(allocator);
913-
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");
914-
try diagnostics.append(note_details);
921+
if (args.len > 0) {
922+
const last_arg = args[args.len - 1];
923+
if (arg_i > 0 and last_arg.len > 0 and last_arg[0] == '/' and std.ascii.endsWithIgnoreCase(last_arg, ".rc")) {
924+
var note_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = true, .arg_index = arg_i - 1 };
925+
var note_writer = note_details.msg.writer(allocator);
926+
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");
927+
try diagnostics.append(note_details);
928+
}
915929
}
916930

917931
// This is a fatal enough problem to justify an early return, since
@@ -969,6 +983,12 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
969983
return options;
970984
}
971985

986+
pub fn isSupportedInputExtension(ext: []const u8) bool {
987+
if (std.ascii.eqlIgnoreCase(ext, ".rc")) return true;
988+
if (std.ascii.eqlIgnoreCase(ext, ".rcpp")) return true;
989+
return false;
990+
}
991+
972992
/// Returns true if the str is a valid C identifier for use in a #define/#undef macro
973993
pub fn isValidIdentifier(str: []const u8) bool {
974994
for (str, 0..) |c, i| switch (c) {
@@ -1271,6 +1291,43 @@ test "parse errors: basic" {
12711291
);
12721292
}
12731293

1294+
test "inferred absolute filepaths" {
1295+
{
1296+
var options = try testParseWarning(&.{ "/fo", "foo.res", "/home/absolute/path.rc" },
1297+
\\<cli>: note: this argument was inferred to be a filepath, so argument parsing was terminated
1298+
\\ ... /home/absolute/path.rc
1299+
\\ ^~~~~~~~~~~~~~~~~~~~~~
1300+
\\
1301+
);
1302+
defer options.deinit();
1303+
}
1304+
{
1305+
var options = try testParseWarning(&.{ "/home/absolute/path.rc", "foo.res" },
1306+
\\<cli>: note: this argument was inferred to be a filepath, so argument parsing was terminated
1307+
\\ ... /home/absolute/path.rc ...
1308+
\\ ^~~~~~~~~~~~~~~~~~~~~~
1309+
\\
1310+
);
1311+
defer options.deinit();
1312+
}
1313+
{
1314+
// Only the last two arguments are checked, so the /h is parsed as an option
1315+
var options = try testParse(&.{ "/home/absolute/path.rc", "foo.rc", "foo.res" });
1316+
defer options.deinit();
1317+
1318+
try std.testing.expect(options.print_help_and_exit);
1319+
}
1320+
{
1321+
var options = try testParse(&.{ "/xvFO/some/absolute/path.res", "foo.rc" });
1322+
defer options.deinit();
1323+
1324+
try std.testing.expectEqual(true, options.verbose);
1325+
try std.testing.expectEqual(true, options.ignore_include_env_var);
1326+
try std.testing.expectEqualStrings("foo.rc", options.input_source.filename);
1327+
try std.testing.expectEqualStrings("/some/absolute/path.res", options.output_source.filename);
1328+
}
1329+
}
1330+
12741331
test "parse errors: /ln" {
12751332
try testParseError(&.{ "/ln", "invalid", "foo.rc" },
12761333
\\<cli>: error: invalid language tag: invalid

0 commit comments

Comments
 (0)