diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig index 59843585a2..f35e70ce6c 100644 --- a/lib/compiler/resinator/cli.zig +++ b/lib/compiler/resinator/cli.zig @@ -128,15 +128,19 @@ pub const Diagnostics = struct { pub fn renderToStderr(self: *Diagnostics, io: Io, args: []const []const u8) Io.Cancelable!void { const stderr = try io.lockStderr(&.{}, null); defer io.unlockStderr(); - self.renderToWriter(args, stderr.terminal()) catch return; + self.renderToTerminal(stderr.terminal(), args) catch return; } - pub fn renderToWriter(self: *Diagnostics, args: []const []const u8, t: Io.Terminal) !void { + pub fn renderToTerminal(self: *Diagnostics, terminal: Io.Terminal, args: []const []const u8) !void { for (self.errors.items) |err_details| { - try renderErrorMessage(t, err_details, args); + try renderErrorMessage(terminal, err_details, args); } } + pub fn renderToWriter(self: *Diagnostics, writer: *Io.Writer, args: []const []const u8) !void { + return self.renderToTerminal(.{ .writer = writer, .mode = .no_color }, args); + } + pub fn hasError(self: *const Diagnostics) bool { for (self.errors.items) |err| { if (err.type == .err) return true; @@ -1475,9 +1479,9 @@ fn testParseOutput(args: []const []const u8, expected_output: []const u8) !?Opti var output: std.Io.Writer.Allocating = .init(std.testing.allocator); defer output.deinit(); - var options = parse(std.testing.allocator, args, &diagnostics) catch |err| switch (err) { + var options = parse(std.testing.allocator, std.testing.io, args, &diagnostics) catch |err| switch (err) { error.ParseError => { - try diagnostics.renderToWriter(args, &output.writer, .no_color); + try diagnostics.renderToWriter(&output.writer, args); try std.testing.expectEqualStrings(expected_output, output.written()); return null; }, @@ -1485,7 +1489,7 @@ fn testParseOutput(args: []const []const u8, expected_output: []const u8) !?Opti }; errdefer options.deinit(); - try diagnostics.renderToWriter(args, &output.writer, .no_color); + try diagnostics.renderToWriter(&output.writer, args); try std.testing.expectEqualStrings(expected_output, output.written()); return options; } diff --git a/lib/compiler/resinator/compile.zig b/lib/compiler/resinator/compile.zig index a315ea488e..e891e3f5aa 100644 --- a/lib/compiler/resinator/compile.zig +++ b/lib/compiler/resinator/compile.zig @@ -59,6 +59,7 @@ pub const CompileOptions = struct { max_string_literal_codepoints: u15 = lex.default_max_string_literal_codepoints, silent_duplicate_control_ids: bool = false, warn_instead_of_error_on_invalid_code_page: bool = false, + include_env_value: ?[]const u8 = null, }; pub const Dependencies = struct { @@ -80,7 +81,7 @@ pub const Dependencies = struct { } }; -pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io.Writer, options: CompileOptions, environ_map: *const std.process.Environ.Map) !void { +pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io.Writer, options: CompileOptions) !void { var lexer = lex.Lexer.init(source, .{ .default_code_page = options.default_code_page, .source_mappings = options.source_mappings, @@ -148,7 +149,7 @@ pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io try search_dirs.append(allocator, .{ .dir = dir, .path = try allocator.dupe(u8, system_include_path) }); } if (!options.ignore_include_env_var) { - const INCLUDE = environ_map.get("INCLUDE") orelse ""; + const INCLUDE = options.include_env_value orelse ""; // The only precedence here is llvm-rc which also uses the platform-specific // delimiter. There's no precedence set by `rc.exe` since it's Windows-only. @@ -405,7 +406,7 @@ pub const Compiler = struct { // `/test.bin` relative to include paths and instead only treats it as // an absolute path. if (std.fs.path.isAbsolute(path)) { - const file = try utils.openFileNotDir(Io.Dir.cwd(), io, path, .{}); + const file = try Io.Dir.cwd().openFile(io, path, .{ .allow_directory = false }); errdefer file.close(io); if (self.dependencies) |dependencies| { @@ -417,7 +418,7 @@ pub const Compiler = struct { var first_error: ?(std.Io.File.OpenError || std.Io.File.StatError) = null; for (self.search_dirs) |search_dir| { - if (utils.openFileNotDir(search_dir.dir, io, path, .{})) |file| { + if (search_dir.dir.openFile(io, path, .{ .allow_directory = false })) |file| { errdefer file.close(io); if (self.dependencies) |dependencies| { diff --git a/lib/compiler/resinator/errors.zig b/lib/compiler/resinator/errors.zig index 4f41a5b56d..9270cc74b8 100644 --- a/lib/compiler/resinator/errors.zig +++ b/lib/compiler/resinator/errors.zig @@ -24,12 +24,10 @@ pub const Diagnostics = struct { /// Expects to own all strings within the list. strings: std.ArrayList([]const u8) = .empty, allocator: Allocator, - io: Io, - pub fn init(allocator: Allocator, io: Io) Diagnostics { + pub fn init(allocator: Allocator) Diagnostics { return .{ .allocator = allocator, - .io = io, }; } @@ -67,8 +65,7 @@ pub const Diagnostics = struct { return @intCast(index); } - pub fn renderToStderr(self: *Diagnostics, cwd: Io.Dir, source: []const u8, source_mappings: ?SourceMappings) Io.Cancelable!void { - const io = self.io; + pub fn renderToStderr(self: *Diagnostics, io: Io, cwd: Io.Dir, source: []const u8, source_mappings: ?SourceMappings) Io.Cancelable!void { const stderr = try io.lockStderr(&.{}, null); defer io.unlockStderr(); for (self.errors.items) |err_details| { @@ -1120,7 +1117,7 @@ const CorrespondingLines = struct { var corresponding_lines = CorrespondingLines{ .span = corresponding_span, - .file = try utils.openFileNotDir(cwd, io, corresponding_file, .{}), + .file = try cwd.openFile(io, corresponding_file, .{ .allow_directory = false }), .code_page = err_details.code_page, .file_reader = undefined, }; diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index d1aaa24264..ce0194759c 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -12,7 +12,6 @@ const Diagnostics = @import("errors.zig").Diagnostics; const cli = @import("cli.zig"); const preprocess = @import("preprocess.zig"); const renderErrorMessage = @import("utils.zig").renderErrorMessage; -const openFileNotDir = @import("utils.zig").openFileNotDir; const cvtres = @import("cvtres.zig"); const hasDisjointCodePage = @import("disjoint_code_page.zig").hasDisjointCodePage; const fmtResourceType = @import("res.zig").NameOrOrdinal.fmtResourceType; @@ -141,7 +140,7 @@ pub fn main(init: std.process.Init.Minimal) !void { } }; defer { diagnostics.deinit(); - if (!zig_integration) std.debug.unlockStderr(); + if (!zig_integration) io.unlockStderr(); } var comp = aro.Compilation.init(aro_arena, aro_arena, io, &diagnostics, Io.Dir.cwd()); @@ -152,7 +151,7 @@ pub fn main(init: std.process.Init.Minimal) !void { try argv.append(aro_arena, "arocc"); // dummy command name const resolved_include_paths = try include_paths.get(&error_handler, &environ_map); - try preprocess.appendAroArgs(aro_arena, &argv, options, resolved_include_paths, &environ_map); + try preprocess.appendAroArgs(aro_arena, &argv, options, resolved_include_paths, environ_map.get("INCLUDE")); try argv.append(aro_arena, switch (options.input_source) { .stdio => "-", .filename => |filename| filename, @@ -194,13 +193,13 @@ pub fn main(init: std.process.Init.Minimal) !void { .stdio => |file| { var file_reader = file.reader(io, &.{}); break :full_input file_reader.interface.allocRemaining(gpa, .unlimited) catch |err| { - try error_handler.emitMessage(gpa, io, .err, "unable to read input from stdin: {s}", .{@errorName(err)}); + try error_handler.emitMessage(gpa, io, .err, "unable to read input from stdin: {t}", .{file_reader.err orelse err}); std.process.exit(1); }; }, .filename => |input_filename| { break :full_input Io.Dir.cwd().readFileAlloc(io, input_filename, gpa, .unlimited) catch |err| { - try error_handler.emitMessage(gpa, io, .err, "unable to read input file path '{s}': {s}", .{ input_filename, @errorName(err) }); + try error_handler.emitMessage(gpa, io, .err, "unable to read input file path '{s}': {t}", .{ input_filename, err }); std.process.exit(1); }; }, @@ -271,7 +270,7 @@ pub fn main(init: std.process.Init.Minimal) !void { const final_input = try removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings); - var diagnostics = Diagnostics.init(gpa, io); + var diagnostics = Diagnostics.init(gpa); defer diagnostics.deinit(); var output_buffer: [4096]u8 = undefined; @@ -295,9 +294,10 @@ pub fn main(init: std.process.Init.Minimal) !void { .max_string_literal_codepoints = options.max_string_literal_codepoints, .silent_duplicate_control_ids = options.silent_duplicate_control_ids, .warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page, - }, &environ_map) catch |err| switch (err) { + .include_env_value = environ_map.get("INCLUDE"), + }) catch |err| switch (err) { error.ParseError, error.CompileError => { - try error_handler.emitDiagnostics(gpa, Io.Dir.cwd(), final_input, &diagnostics, mapping_results.mappings); + try error_handler.emitDiagnostics(gpa, io, Io.Dir.cwd(), final_input, &diagnostics, mapping_results.mappings); // Delete the output file on error res_stream.cleanupAfterError(io); std.process.exit(1); @@ -309,7 +309,7 @@ pub fn main(init: std.process.Init.Minimal) !void { // print any warnings/notes if (!zig_integration) { - try diagnostics.renderToStderr(Io.Dir.cwd(), final_input, mapping_results.mappings); + try diagnostics.renderToStderr(io, Io.Dir.cwd(), final_input, mapping_results.mappings); } // write the depfile @@ -460,7 +460,7 @@ const IoStream = struct { switch (source) { .filename => |filename| return .{ .file = switch (io_direction) { - .input => try openFileNotDir(Io.Dir.cwd(), io, filename, .{}), + .input => try Io.Dir.cwd().openFile(io, filename, .{ .allow_directory = false }), .output => try Io.Dir.cwd().createFile(io, filename, .{}), }, }, @@ -733,6 +733,7 @@ const ErrorHandler = union(enum) { pub fn emitDiagnostics( self: *ErrorHandler, allocator: Allocator, + io: Io, cwd: Io.Dir, source: []const u8, diagnostics: *Diagnostics, @@ -745,7 +746,7 @@ const ErrorHandler = union(enum) { try server.serveErrorBundle(error_bundle); }, - .stderr => return diagnostics.renderToStderr(cwd, source, mappings), + .stderr => return diagnostics.renderToStderr(io, cwd, source, mappings), } } diff --git a/lib/compiler/resinator/preprocess.zig b/lib/compiler/resinator/preprocess.zig index 1d8c038b60..0b9a2ef59e 100644 --- a/lib/compiler/resinator/preprocess.zig +++ b/lib/compiler/resinator/preprocess.zig @@ -84,9 +84,9 @@ fn hasAnyErrors(comp: *aro.Compilation) bool { return comp.diagnostics.errors != 0; } -/// `arena` is used for temporary -D argument strings and the INCLUDE environment variable. +/// `arena` is used for temporary -D argument strings. /// The arena should be kept alive at least as long as `argv`. -pub fn appendAroArgs(arena: Allocator, argv: *std.ArrayList([]const u8), options: cli.Options, system_include_paths: []const []const u8, environ_map: *const std.process.Environ.Map) !void { +pub fn appendAroArgs(arena: Allocator, argv: *std.ArrayList([]const u8), options: cli.Options, system_include_paths: []const []const u8, include_env_value: ?[]const u8) !void { try argv.appendSlice(arena, &.{ "-E", "--comments", @@ -109,7 +109,7 @@ pub fn appendAroArgs(arena: Allocator, argv: *std.ArrayList([]const u8), options } if (!options.ignore_include_env_var) { - const INCLUDE = environ_map.get("INCLUDE") orelse ""; + const INCLUDE = include_env_value orelse ""; // The only precedence here is llvm-rc which also uses the platform-specific // delimiter. There's no precedence set by `rc.exe` since it's Windows-only. diff --git a/lib/compiler/resinator/utils.zig b/lib/compiler/resinator/utils.zig index 42d3cc5e31..41cbb00d75 100644 --- a/lib/compiler/resinator/utils.zig +++ b/lib/compiler/resinator/utils.zig @@ -1,5 +1,3 @@ -const builtin = @import("builtin"); - const std = @import("std"); const Io = std.Io; @@ -25,27 +23,6 @@ pub const UncheckedSliceWriter = struct { } }; -/// Cross-platform 'Io.Dir.openFile' wrapper that will always return IsDir if -/// a directory is attempted to be opened. -/// TODO: Remove once https://github.com/ziglang/zig/issues/5732 is addressed. -pub fn openFileNotDir( - cwd: Io.Dir, - io: Io, - path: []const u8, - flags: Io.File.OpenFlags, -) (Io.File.OpenError || Io.File.StatError)!Io.File { - const file = try cwd.openFile(io, path, flags); - errdefer file.close(io); - // https://github.com/ziglang/zig/issues/5732 - if (builtin.os.tag != .windows) { - const stat = try file.stat(io); - - if (stat.kind == .directory) - return error.IsDir; - } - return file; -} - /// Emulates the Windows implementation of `iswdigit`, but only returns true /// for the non-ASCII digits that `iswdigit` on Windows would return true for. pub fn isNonAsciiDigit(c: u21) bool { @@ -90,6 +67,12 @@ pub fn isNonAsciiDigit(c: u21) bool { pub const ErrorMessageType = enum { err, warning, note }; +pub fn renderErrorMessageToStderr(io: std.Io, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void { + var stderr = try io.lockStderr(&.{}, null); + defer io.unlockStderr(); + try renderErrorMessage(stderr.terminal(), msg_type, format, args); +} + /// Used for generic colored errors/warnings/notes, more context-specific error messages /// are handled elsewhere. pub fn renderErrorMessage(t: Io.Terminal, msg_type: ErrorMessageType, comptime format: []const u8, args: anytype) !void {