diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index 4d0182bf75c9..ce8532ae9f0f 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -13,6 +13,7 @@ const cvtres = @import("cvtres.zig"); const hasDisjointCodePage = @import("disjoint_code_page.zig").hasDisjointCodePage; const fmtResourceType = @import("res.zig").NameOrOrdinal.fmtResourceType; const aro = @import("aro"); +const compiler_util = @import("../util.zig"); pub fn main() !void { var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; @@ -671,7 +672,11 @@ const ErrorHandler = union(enum) { ) !void { switch (self.*) { .server => |*server| { - var error_bundle = try aroDiagnosticsToErrorBundle(allocator, fail_msg, comp); + var error_bundle = try compiler_util.aroDiagnosticsToErrorBundle( + comp.diagnostics, + allocator, + fail_msg, + ); defer error_bundle.deinit(allocator); try server.serveErrorBundle(error_bundle); @@ -753,7 +758,7 @@ fn cliDiagnosticsToErrorBundle( switch (err_details.type) { .err => { if (cur_err) |err| { - try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items); + try bundle.addRootErrorMessageWithNotes(err, cur_notes.items); } cur_err = .{ .msg = try bundle.addString(err_details.msg.items), @@ -771,7 +776,7 @@ fn cliDiagnosticsToErrorBundle( } } if (cur_err) |err| { - try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items); + try bundle.addRootErrorMessageWithNotes(err, cur_notes.items); } return try bundle.toOwnedBundle(""); @@ -840,7 +845,7 @@ fn diagnosticsToErrorBundle( switch (err_details.type) { .err => { if (cur_err) |err| { - try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items); + try bundle.addRootErrorMessageWithNotes(err, cur_notes.items); } cur_err = .{ .msg = try bundle.addString(msg_buf.written()), @@ -859,20 +864,12 @@ fn diagnosticsToErrorBundle( } } if (cur_err) |err| { - try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items); + try bundle.addRootErrorMessageWithNotes(err, cur_notes.items); } return try bundle.toOwnedBundle(""); } -fn flushErrorMessageIntoBundle(wip: *ErrorBundle.Wip, msg: ErrorBundle.ErrorMessage, notes: []const ErrorBundle.ErrorMessage) !void { - try wip.addRootErrorMessage(msg); - const notes_start = try wip.reserveNotes(@intCast(notes.len)); - for (notes_start.., notes) |i, note| { - wip.extra.items[i] = @intFromEnum(wip.addErrorMessageAssumeCapacity(note)); - } -} - fn errorStringToErrorBundle(allocator: std.mem.Allocator, comptime format: []const u8, args: anytype) !ErrorBundle { @branchHint(.cold); var bundle: ErrorBundle.Wip = undefined; @@ -883,75 +880,3 @@ fn errorStringToErrorBundle(allocator: std.mem.Allocator, comptime format: []con }); return try bundle.toOwnedBundle(""); } - -fn aroDiagnosticsToErrorBundle( - gpa: std.mem.Allocator, - fail_msg: []const u8, - comp: *aro.Compilation, -) !ErrorBundle { - @branchHint(.cold); - - var bundle: ErrorBundle.Wip = undefined; - try bundle.init(gpa); - errdefer bundle.deinit(); - - try bundle.addRootErrorMessage(.{ - .msg = try bundle.addString(fail_msg), - }); - - var cur_err: ?ErrorBundle.ErrorMessage = null; - var cur_notes: std.ArrayList(ErrorBundle.ErrorMessage) = .empty; - defer cur_notes.deinit(gpa); - for (comp.diagnostics.output.to_list.messages.items) |msg| { - switch (msg.kind) { - // Clear the current error so that notes don't bleed into unassociated errors - .off, .warning => { - cur_err = null; - continue; - }, - .note => if (cur_err == null) continue, - .@"fatal error", .@"error" => {}, - } - - const src_loc = src_loc: { - if (msg.location) |location| { - break :src_loc try bundle.addSourceLocation(.{ - .src_path = try bundle.addString(location.path), - .line = location.line_no - 1, // 1-based -> 0-based - .column = location.col - 1, // 1-based -> 0-based - .span_start = location.width, - .span_main = location.width, - .span_end = location.width, - .source_line = try bundle.addString(location.line), - }); - } - break :src_loc ErrorBundle.SourceLocationIndex.none; - }; - - switch (msg.kind) { - .@"fatal error", .@"error" => { - if (cur_err) |err| { - try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items); - } - cur_err = .{ - .msg = try bundle.addString(msg.text), - .src_loc = src_loc, - }; - cur_notes.clearRetainingCapacity(); - }, - .note => { - cur_err.?.notes_len += 1; - try cur_notes.append(gpa, .{ - .msg = try bundle.addString(msg.text), - .src_loc = src_loc, - }); - }, - .off, .warning => unreachable, - } - } - if (cur_err) |err| { - try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items); - } - - return try bundle.toOwnedBundle(""); -} diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index b6696b568034..e80c3d3dcd18 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -345,20 +345,7 @@ fn buildWasmBinary( } }, .error_bundle => { - const EbHdr = std.zig.Server.Message.ErrorBundle; - const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body)); - const extra_bytes = - body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; - const string_bytes = - body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; - // TODO: use @ptrCast when the compiler supports it - const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); - const extra_array = try arena.alloc(u32, unaligned_extra.len); - @memcpy(extra_array, unaligned_extra); - result_error_bundle = .{ - .string_bytes = try arena.dupe(u8, string_bytes), - .extra = extra_array, - }; + result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body); }, .emit_digest => { const EmitDigest = std.zig.Server.Message.EmitDigest; diff --git a/lib/compiler/translate-c/main.zig b/lib/compiler/translate-c/main.zig index b8b1a2a3bf5e..3a3fd7577e93 100644 --- a/lib/compiler/translate-c/main.zig +++ b/lib/compiler/translate-c/main.zig @@ -3,6 +3,7 @@ const assert = std.debug.assert; const mem = std.mem; const process = std.process; const aro = @import("aro"); +const compiler_util = @import("../util.zig"); const Translator = @import("Translator.zig"); const fast_exit = @import("builtin").mode != .Debug; @@ -13,24 +14,33 @@ pub fn main() u8 { const gpa = general_purpose_allocator.allocator(); defer _ = general_purpose_allocator.deinit(); - var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); + var arena_instance = std.heap.ArenaAllocator.init(gpa); defer arena_instance.deinit(); const arena = arena_instance.allocator(); - const args = process.argsAlloc(arena) catch { + var args = process.argsAlloc(arena) catch { std.debug.print("ran out of memory allocating arguments\n", .{}); if (fast_exit) process.exit(1); return 1; }; + var zig_integration = false; + if (args.len > 1 and std.mem.eql(u8, args[1], "--zig-integration")) { + zig_integration = true; + } + var stderr_buf: [1024]u8 = undefined; var stderr = std.fs.File.stderr().writer(&stderr_buf); - var diagnostics: aro.Diagnostics = .{ - .output = .{ .to_writer = .{ + var diagnostics: aro.Diagnostics = switch (zig_integration) { + false => .{ .output = .{ .to_writer = .{ .color = .detect(stderr.file), .writer = &stderr.interface, - } }, + } } }, + true => .{ .output = .{ .to_list = .{ + .arena = .init(gpa), + } } }, }; + defer diagnostics.deinit(); var comp = aro.Compilation.initDefault(gpa, arena, &diagnostics, std.fs.cwd()) catch |err| switch (err) { error.OutOfMemory => { @@ -47,13 +57,22 @@ pub fn main() u8 { var toolchain: aro.Toolchain = .{ .driver = &driver, .filesystem = .{ .real = comp.cwd } }; defer toolchain.deinit(); - translate(&driver, &toolchain, args) catch |err| switch (err) { + translate(&driver, &toolchain, args, zig_integration) catch |err| switch (err) { error.OutOfMemory => { std.debug.print("ran out of memory translating\n", .{}); if (fast_exit) process.exit(1); return 1; }, - error.FatalError => { + error.FatalError => if (zig_integration) { + serveErrorBundle(arena, &diagnostics) catch |bundle_err| { + std.debug.print("unable to serve error bundle: {}\n", .{bundle_err}); + if (fast_exit) process.exit(1); + return 1; + }; + + if (fast_exit) process.exit(0); + return 0; + } else { if (fast_exit) process.exit(1); return 1; }, @@ -63,10 +82,27 @@ pub fn main() u8 { return 1; }, }; + + assert(comp.diagnostics.errors == 0 or !zig_integration); if (fast_exit) process.exit(@intFromBool(comp.diagnostics.errors != 0)); return @intFromBool(comp.diagnostics.errors != 0); } +fn serveErrorBundle(arena: std.mem.Allocator, diagnostics: *const aro.Diagnostics) !void { + const error_bundle = try compiler_util.aroDiagnosticsToErrorBundle( + diagnostics, + arena, + "translation failure", + ); + var stdout_buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + var server: std.zig.Server = .{ + .out = &stdout_writer.interface, + .in = undefined, + }; + try server.serveErrorBundle(error_bundle); +} + pub const usage = \\Usage {s}: [options] file [CC options] \\ @@ -79,7 +115,7 @@ pub const usage = \\ ; -fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8) !void { +fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration: bool) !void { const gpa = d.comp.gpa; const aro_args = args: { @@ -99,6 +135,9 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8) !void { try stdout.interface.writeAll("0.0.0-dev\n"); try stdout.interface.flush(); return; + } else if (mem.eql(u8, arg, "--zig-integration")) { + if (i != 1 or !zig_integration) + return d.fatal("--zig-integration must be the first argument", .{}); } else { i += 1; } @@ -116,6 +155,14 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8) !void { return d.fatal("user provided macro source exceeded max size", .{}); } + const has_output_file = if (d.output_name) |path| + !std.mem.eql(u8, path, "-") + else + false; + if (zig_integration and !has_output_file) { + return d.fatal("--zig-integration requires specifying an output file", .{}); + } + const content = try macro_buf.toOwnedSlice(gpa); errdefer gpa.free(content); @@ -160,7 +207,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8) !void { defer c_tree.deinit(); if (d.diagnostics.errors != 0) { - if (fast_exit) process.exit(1); + if (fast_exit and !zig_integration) process.exit(1); return error.FatalError; } @@ -212,7 +259,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8) !void { if (out_writer.err) |write_err| return d.fatal("failed to write result to '{s}': {s}", .{ out_file_path, aro.Driver.errorDescription(write_err) }); - if (fast_exit) process.exit(0); + if (fast_exit and !zig_integration) process.exit(0); } test { diff --git a/lib/compiler/util.zig b/lib/compiler/util.zig new file mode 100644 index 000000000000..05570abc3bfd --- /dev/null +++ b/lib/compiler/util.zig @@ -0,0 +1,82 @@ +//! Utilities shared between compiler sub-commands +const std = @import("std"); +const aro = @import("aro"); +const ErrorBundle = std.zig.ErrorBundle; + +pub fn aroDiagnosticsToErrorBundle( + d: *const aro.Diagnostics, + gpa: std.mem.Allocator, + fail_msg: ?[]const u8, +) !ErrorBundle { + @branchHint(.cold); + + var bundle: ErrorBundle.Wip = undefined; + try bundle.init(gpa); + errdefer bundle.deinit(); + + if (fail_msg) |msg| { + try bundle.addRootErrorMessage(.{ + .msg = try bundle.addString(msg), + }); + } + + var cur_err: ?ErrorBundle.ErrorMessage = null; + var cur_notes: std.ArrayList(ErrorBundle.ErrorMessage) = .empty; + defer cur_notes.deinit(gpa); + for (d.output.to_list.messages.items) |msg| { + switch (msg.kind) { + .off, .warning => { + // Emit any pending error and clear everything so that notes don't bleed into unassociated errors + if (cur_err) |err| { + try bundle.addRootErrorMessageWithNotes(err, cur_notes.items); + cur_err = null; + } + cur_notes.clearRetainingCapacity(); + continue; + }, + .note => if (cur_err == null) continue, + .@"fatal error", .@"error" => {}, + } + + const src_loc = src_loc: { + if (msg.location) |location| { + break :src_loc try bundle.addSourceLocation(.{ + .src_path = try bundle.addString(location.path), + .line = location.line_no - 1, // 1-based -> 0-based + .column = location.col - 1, // 1-based -> 0-based + .span_start = location.width, + .span_main = location.width, + .span_end = location.width, + .source_line = try bundle.addString(location.line), + }); + } + break :src_loc ErrorBundle.SourceLocationIndex.none; + }; + + switch (msg.kind) { + .@"fatal error", .@"error" => { + if (cur_err) |err| { + try bundle.addRootErrorMessageWithNotes(err, cur_notes.items); + } + cur_err = .{ + .msg = try bundle.addString(msg.text), + .src_loc = src_loc, + }; + cur_notes.clearRetainingCapacity(); + }, + .note => { + cur_err.?.notes_len += 1; + try cur_notes.append(gpa, .{ + .msg = try bundle.addString(msg.text), + .src_loc = src_loc, + }); + }, + .off, .warning => unreachable, + } + } + if (cur_err) |err| { + try bundle.addRootErrorMessageWithNotes(err, cur_notes.items); + } + + return try bundle.toOwnedBundle(""); +} diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 8e9e12248aab..6e7e9c470258 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -524,22 +524,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build. } }, .error_bundle => { - const EbHdr = std.zig.Server.Message.ErrorBundle; - const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body)); - const extra_bytes = - body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; - const string_bytes = - body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; - // TODO: use @ptrCast when the compiler supports it - const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); - { - s.result_error_bundle = .{ .string_bytes = &.{}, .extra = &.{} }; - errdefer s.result_error_bundle.deinit(gpa); - s.result_error_bundle.string_bytes = try gpa.dupe(u8, string_bytes); - const extra = try gpa.alloc(u32, unaligned_extra.len); - @memcpy(extra, unaligned_extra); - s.result_error_bundle.extra = extra; - } + s.result_error_bundle = try std.zig.Server.allocErrorBundle(gpa, body); // This message indicates the end of the update. if (watch) break :poll; }, diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig index 8f91a8580d26..f05d8490d31f 100644 --- a/lib/std/Build/WebServer.zig +++ b/lib/std/Build/WebServer.zig @@ -595,19 +595,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim } }, .error_bundle => { - const EbHdr = std.zig.Server.Message.ErrorBundle; - const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body)); - const extra_bytes = - body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; - const string_bytes = - body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; - const unaligned_extra: []align(1) const u32 = @ptrCast(extra_bytes); - const extra_array = try arena.alloc(u32, unaligned_extra.len); - @memcpy(extra_array, unaligned_extra); - result_error_bundle = .{ - .string_bytes = try arena.dupe(u8, string_bytes), - .extra = extra_array, - }; + result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body); }, .emit_digest => { const EmitDigest = std.zig.Server.Message.EmitDigest; diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index 356b9fe1379c..8ec3ae18b608 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -416,6 +416,18 @@ pub const Wip = struct { wip.root_list.appendAssumeCapacity(try addErrorMessage(wip, em)); } + pub fn addRootErrorMessageWithNotes( + wip: *Wip, + msg: ErrorMessage, + notes: []const ErrorMessage, + ) !void { + try wip.addRootErrorMessage(msg); + const notes_start = try wip.reserveNotes(@intCast(notes.len)); + for (notes_start.., notes) |i, note| { + wip.extra.items[i] = @intFromEnum(wip.addErrorMessageAssumeCapacity(note)); + } + } + pub fn addErrorMessage(wip: *Wip, em: ErrorMessage) Allocator.Error!MessageIndex { return @enumFromInt(try addExtra(wip, em)); } diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig index c035cbdec28e..06b9afae915e 100644 --- a/lib/std/zig/Server.zig +++ b/lib/std/zig/Server.zig @@ -231,6 +231,28 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { try s.out.flush(); } +pub fn allocErrorBundle(allocator: std.mem.Allocator, body: []const u8) !std.zig.ErrorBundle { + const eb_hdr = @as(*align(1) const OutMessage.ErrorBundle, @ptrCast(body)); + const extra_bytes = + body[@sizeOf(OutMessage.ErrorBundle)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; + const string_bytes = + body[@sizeOf(OutMessage.ErrorBundle) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; + const unaligned_extra: []align(1) const u32 = @ptrCast(extra_bytes); + + var error_bundle: std.zig.ErrorBundle = .{ + .string_bytes = &.{}, + .extra = &.{}, + }; + errdefer error_bundle.deinit(allocator); + + error_bundle.string_bytes = try allocator.dupe(u8, string_bytes); + const extra = try allocator.alloc(u32, unaligned_extra.len); + @memcpy(extra, unaligned_extra); + error_bundle.extra = extra; + + return error_bundle; +} + pub const TestMetadata = struct { names: []const u32, expected_panic_msgs: []const u32, diff --git a/src/Compilation.zig b/src/Compilation.zig index 7c5f699d8353..e7bcc35b2900 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -5640,6 +5640,7 @@ pub fn obtainWin32ResourceCacheManifest(comp: *const Compilation) Cache.Manifest } pub const CImportResult = struct { + // Only valid if `errors` is not empty digest: [Cache.bin_digest_len]u8, cache_hit: bool, errors: std.zig.ErrorBundle, @@ -5649,76 +5650,184 @@ pub const CImportResult = struct { } }; -/// Caller owns returned memory. -pub fn cImport( +pub fn translateC( comp: *Compilation, - c_src: []const u8, + arena: Allocator, + man: *Cache.Manifest, + ext: FileExt, + source: union(enum) { + path: []const u8, + c_src: []const u8, + }, + translated_basename: []const u8, owner_mod: *Package.Module, prog_node: std.Progress.Node, ) !CImportResult { dev.check(.translate_c_command); - const cimport_basename = "cimport.h"; - const translated_basename = "cimport.zig"; + const tmp_basename = std.fmt.hex(std.crypto.random.int(u64)); + const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename; + const cache_dir = comp.dirs.local_cache.handle; + var cache_tmp_dir = try cache_dir.makeOpenPath(tmp_sub_path, .{}); + defer cache_tmp_dir.close(); + + const translated_path = try comp.dirs.local_cache.join(arena, &.{ tmp_sub_path, translated_basename }); + const source_path = switch (source) { + .c_src => |c_src| path: { + const cimport_basename = "cimport.h"; + const out_h_sub_path = tmp_sub_path ++ fs.path.sep_str ++ cimport_basename; + const out_h_path = try comp.dirs.local_cache.join(arena, &.{out_h_sub_path}); + if (comp.verbose_cimport) log.info("writing C import source to {s}", .{out_h_path}); + try cache_dir.writeFile(.{ .sub_path = out_h_sub_path, .data = c_src }); + break :path out_h_path; + }, + .path => |p| p, + }; - var man = comp.obtainCObjectCacheManifest(owner_mod); - defer man.deinit(); + const out_dep_path: ?[]const u8 = blk: { + if (comp.disable_c_depfile) break :blk null; + const c_src_basename = fs.path.basename(source_path); + const dep_basename = try std.fmt.allocPrint(arena, "{s}.d", .{c_src_basename}); + const out_dep_path = try comp.dirs.local_cache.join(arena, &.{ tmp_sub_path, dep_basename }); + break :blk out_dep_path; + }; - man.hash.add(@as(u16, 0x7dd9)); // Random number to distinguish translate-c from compiling C objects - man.hash.addBytes(c_src); + var argv = std.array_list.Managed([]const u8).init(arena); + { + const target = &owner_mod.resolved_target.result; + try argv.appendSlice(&.{ "--zig-integration", "-x", "c" }); - const digest, const is_hit = if (try man.hit()) .{ man.finalBin(), true } else digest: { - var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); + const resource_path = try comp.dirs.zig_lib.join(arena, &.{ "compiler", "aro", "include" }); + try argv.appendSlice(&.{ "-isystem", resource_path }); + try comp.addCommonCCArgs(arena, &argv, ext, out_dep_path, owner_mod, .aro); + try argv.appendSlice(&[_][]const u8{ "-target", try target.zigTriple(arena) }); - const tmp_basename = std.fmt.hex(std.crypto.random.int(u64)); - const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename; - const cache_dir = comp.dirs.local_cache.handle; - const out_h_sub_path = tmp_sub_path ++ fs.path.sep_str ++ cimport_basename; + const mcpu = mcpu: { + var buf: std.ArrayListUnmanaged(u8) = .empty; + defer buf.deinit(comp.gpa); + + try buf.print(comp.gpa, "-mcpu={s}", .{target.cpu.model.name}); + + // TODO better serialization https://github.com/ziglang/zig/issues/4584 + const all_features_list = target.cpu.arch.allFeaturesList(); + try argv.ensureUnusedCapacity(all_features_list.len * 4); + for (all_features_list, 0..) |feature, index_usize| { + const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); + const is_enabled = target.cpu.features.isEnabled(index); - try cache_dir.makePath(tmp_sub_path); + const plus_or_minus = "-+"[@intFromBool(is_enabled)]; + try buf.print(comp.gpa, "{c}{s}", .{ plus_or_minus, feature.name }); + } + break :mcpu try buf.toOwnedSlice(arena); + }; + try argv.append(mcpu); - const out_h_path = try comp.dirs.local_cache.join(arena, &.{out_h_sub_path}); - const translated_path = try comp.dirs.local_cache.join(arena, &.{ tmp_sub_path, translated_basename }); - const out_dep_path = try std.fmt.allocPrint(arena, "{s}.d", .{out_h_path}); + try argv.appendSlice(comp.global_cc_argv); + try argv.appendSlice(owner_mod.cc_argv); + try argv.appendSlice(&.{ source_path, "-o", translated_path }); + if (comp.verbose_cimport) dump_argv(argv.items); + } - if (comp.verbose_cimport) log.info("writing C import source to {s}", .{out_h_path}); - try cache_dir.writeFile(.{ .sub_path = out_h_sub_path, .data = c_src }); + var stdout: []u8 = undefined; + try @import("main.zig").translateC(comp.gpa, arena, argv.items, prog_node, &stdout); - var argv = std.array_list.Managed([]const u8).init(comp.gpa); - defer argv.deinit(); - try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path, owner_mod); - try argv.appendSlice(&.{ out_h_path, "-o", translated_path }); + if (out_dep_path) |dep_file_path| add_deps: { + if (comp.verbose_cimport) log.info("processing dep file at {s}", .{dep_file_path}); - if (comp.verbose_cc) dump_argv(argv.items); - var stdout: []u8 = undefined; - try @import("main.zig").translateC(comp.gpa, arena, argv.items, prog_node, &stdout); - if (comp.verbose_cimport and stdout.len != 0) log.info("unexpected stdout: {s}", .{stdout}); + const dep_basename = fs.path.basename(dep_file_path); + // Add the files depended on to the cache system, if a dep file was emitted + man.addDepFilePost(cache_tmp_dir, dep_basename) catch |err| switch (err) { + error.FileNotFound => break :add_deps, + else => |e| return e, + }; - const dep_sub_path = out_h_sub_path ++ ".d"; - if (comp.verbose_cimport) log.info("processing dep file at {s}", .{dep_sub_path}); - try man.addDepFilePost(cache_dir, dep_sub_path); switch (comp.cache_use) { .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| { whole.cache_manifest_mutex.lock(); defer whole.cache_manifest_mutex.unlock(); - try whole_cache_manifest.addDepFilePost(cache_dir, dep_sub_path); + try whole_cache_manifest.addDepFilePost(cache_tmp_dir, dep_basename); }, .incremental, .none => {}, } - const bin_digest = man.finalBin(); - const hex_digest = Cache.binToHex(bin_digest); - const o_sub_path = "o" ++ fs.path.sep_str ++ hex_digest; + // Just to save disk space, we delete the file because it is never needed again. + cache_tmp_dir.deleteFile(dep_basename) catch |err| { + log.warn("failed to delete '{s}': {t}", .{ dep_file_path, err }); + }; + } + + if (stdout.len > 0) { + var reader: std.Io.Reader = .fixed(stdout); + const MessageHeader = std.zig.Server.Message.Header; + const header = reader.takeStruct(MessageHeader, .little) catch |err| + fatal("unable to read translate-c MessageHeader: {s}", .{@errorName(err)}); + const body = reader.take(header.bytes_len) catch |err| + fatal("unable to read {}-byte translate-c message body: {s}", .{ header.bytes_len, @errorName(err) }); + switch (header.tag) { + .error_bundle => { + const error_bundle = try std.zig.Server.allocErrorBundle(comp.gpa, body); + return .{ + .digest = undefined, + .cache_hit = false, + .errors = error_bundle, + }; + }, + else => fatal("unexpected message type received from translate-c: {s}", .{@tagName(header.tag)}), + } + } + + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); + const o_sub_path = "o" ++ fs.path.sep_str ++ hex_digest; - if (comp.verbose_cimport) log.info("renaming {s} to {s}", .{ tmp_sub_path, o_sub_path }); - try renameTmpIntoCache(comp.dirs.local_cache, tmp_sub_path, o_sub_path); + if (comp.verbose_cimport) log.info("renaming {s} to {s}", .{ tmp_sub_path, o_sub_path }); + try renameTmpIntoCache(comp.dirs.local_cache, tmp_sub_path, o_sub_path); - break :digest .{ bin_digest, false }; + return .{ + .digest = bin_digest, + .cache_hit = false, + .errors = ErrorBundle.empty, }; +} - if (man.have_exclusive_lock) { +/// Caller owns returned memory. +pub fn cImport( + comp: *Compilation, + c_src: []const u8, + owner_mod: *Package.Module, + prog_node: std.Progress.Node, +) !CImportResult { + dev.check(.translate_c_command); + + const translated_basename = "cimport.zig"; + + var man = comp.obtainCObjectCacheManifest(owner_mod); + defer man.deinit(); + + man.hash.add(@as(u16, 0x7dd9)); // Random number to distinguish c-import from compiling C objects + man.hash.addBytes(c_src); + + const result: CImportResult = if (try man.hit()) .{ + .digest = man.finalBin(), + .cache_hit = true, + .errors = ErrorBundle.empty, + } else result: { + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + break :result try comp.translateC( + arena, + &man, + .c, + .{ .c_src = c_src }, + translated_basename, + owner_mod, + prog_node, + ); + }; + + if (result.errors.errorMessageCount() == 0 and man.have_exclusive_lock) { // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is // possible we had a hit and the manifest is dirty, for example if the file mtime changed but // the contents were the same, we hit the cache but the manifest is dirty and we need to update @@ -5728,11 +5837,7 @@ pub fn cImport( }; } - return .{ - .digest = digest, - .cache_hit = is_hit, - .errors = std.zig.ErrorBundle.empty, - }; + return result; } fn workerUpdateCObject( @@ -6622,19 +6727,7 @@ fn spawnZigRc( // We expect exactly one ErrorBundle, and if any error_bundle header is // sent then it's a fatal error. .error_bundle => { - const EbHdr = std.zig.Server.Message.ErrorBundle; - const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body)); - const extra_bytes = - body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; - const string_bytes = - body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; - const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); - const extra_array = try comp.gpa.alloc(u32, unaligned_extra.len); - @memcpy(extra_array, unaligned_extra); - const error_bundle = std.zig.ErrorBundle{ - .string_bytes = try comp.gpa.dupe(u8, string_bytes), - .extra = extra_array, - }; + const error_bundle = try std.zig.Server.allocErrorBundle(comp.gpa, body); return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle); }, else => {}, // ignore other messages @@ -6672,49 +6765,6 @@ pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error } } -pub fn addTranslateCCArgs( - comp: *Compilation, - arena: Allocator, - argv: *std.array_list.Managed([]const u8), - ext: FileExt, - out_dep_path: ?[]const u8, - owner_mod: *Package.Module, -) !void { - const target = &owner_mod.resolved_target.result; - - try argv.appendSlice(&.{ "-x", "c" }); - - const resource_path = try comp.dirs.zig_lib.join(arena, &.{"compiler/aro/include"}); - try argv.appendSlice(&.{ "-isystem", resource_path }); - - try comp.addCommonCCArgs(arena, argv, ext, out_dep_path, owner_mod, .aro); - - try argv.appendSlice(&[_][]const u8{ "-target", try target.zigTriple(arena) }); - - const mcpu = mcpu: { - var buf: std.ArrayListUnmanaged(u8) = .empty; - defer buf.deinit(comp.gpa); - - try buf.print(comp.gpa, "-mcpu={s}", .{target.cpu.model.name}); - - // TODO better serialization https://github.com/ziglang/zig/issues/4584 - const all_features_list = target.cpu.arch.allFeaturesList(); - try argv.ensureUnusedCapacity(all_features_list.len * 4); - for (all_features_list, 0..) |feature, index_usize| { - const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); - const is_enabled = target.cpu.features.isEnabled(index); - - const plus_or_minus = "-+"[@intFromBool(is_enabled)]; - try buf.print(comp.gpa, "{c}{s}", .{ plus_or_minus, feature.name }); - } - break :mcpu try buf.toOwnedSlice(arena); - }; - try argv.append(mcpu); - - try argv.appendSlice(comp.global_cc_argv); - try argv.appendSlice(owner_mod.cc_argv); -} - /// Add common C compiler args between translate-c and C object compilation. fn addCommonCCArgs( comp: *const Compilation, diff --git a/src/main.zig b/src/main.zig index 89a552de0b1b..f84fc36d8082 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4075,7 +4075,7 @@ fn serve( while (true) { const hdr = try server.receiveMessage(); - // Lock the debug server while hanling the message. + // Lock the debug server while handling the message. if (comp.debugIncremental()) ids.mutex.lock(); defer if (comp.debugIncremental()) ids.mutex.unlock(); @@ -4092,7 +4092,11 @@ fn serve( var output: Compilation.CImportResult = undefined; try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node); defer output.deinit(gpa); - try server.serveStringMessage(.file_system_inputs, file_system_inputs.items); + + if (file_system_inputs.items.len != 0) { + try server.serveStringMessage(.file_system_inputs, file_system_inputs.items); + } + if (output.errors.errorMessageCount() != 0) { try server.serveErrorBundle(output.errors); } else { @@ -4100,6 +4104,7 @@ fn serve( .flags = .{ .cache_hit = output.cache_hit }, }); } + continue; } @@ -4515,48 +4520,75 @@ fn cmdTranslateC( prog_node: std.Progress.Node, ) !void { dev.check(.translate_c_command); - _ = file_system_inputs; - _ = fancy_output; assert(comp.c_source_files.len == 1); const c_source_file = comp.c_source_files[0]; - var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.makeOpenPath("tmp", .{}); - defer zig_cache_tmp_dir.close(); + const translated_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.root_name}); + + var man: Cache.Manifest = comp.obtainCObjectCacheManifest(comp.root_mod); + man.want_shared_lock = false; + defer man.deinit(); - const ext = Compilation.classifyFileExt(c_source_file.src_path); - const out_dep_path: ?[]const u8 = blk: { - if (comp.disable_c_depfile) break :blk null; - const c_src_basename = fs.path.basename(c_source_file.src_path); - const dep_basename = try std.fmt.allocPrint(arena, "{s}.d", .{c_src_basename}); - const out_dep_path = try comp.tmpFilePath(arena, dep_basename); - break :blk out_dep_path; + man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects + man.hash.add(comp.config.c_frontend); + Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| { + fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); }; - var argv = std.array_list.Managed([]const u8).init(arena); - try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path, comp.root_mod); - try argv.append(c_source_file.src_path); - if (comp.verbose_cc) Compilation.dump_argv(argv.items); - - try translateC(comp.gpa, arena, argv.items, prog_node, null); - - if (out_dep_path) |dep_file_path| { - const dep_basename = fs.path.basename(dep_file_path); - // Add the files depended on to the cache system. - //man.addDepFilePost(zig_cache_tmp_dir, dep_basename) catch |err| switch (err) { - // error.FileNotFound => { - // // Clang didn't emit the dep file; nothing to add to the manifest. - // break :add_deps; - // }, - // else => |e| return e, - //}; - // Just to save disk space, we delete the file because it is never needed again. - zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { - warn("failed to delete '{s}': {t}", .{ dep_file_path, err }); + const result: Compilation.CImportResult = if (try man.hit()) .{ + .digest = man.finalBin(), + .cache_hit = true, + .errors = std.zig.ErrorBundle.empty, + } else result: { + const result = try comp.translateC( + arena, + &man, + Compilation.classifyFileExt(c_source_file.src_path), + .{ .path = c_source_file.src_path }, + translated_basename, + comp.root_mod, + prog_node, + ); + + if (result.errors.errorMessageCount() != 0) { + if (fancy_output) |p| { + if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf); + p.* = result; + return; + } else { + const color: Color = .auto; + result.errors.renderToStdErr(color.renderOptions()); + process.exit(1); + } + } + + man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err}); + break :result result; + }; + + if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf); + if (fancy_output) |p| { + p.* = result; + } else { + const hex_digest = Cache.binToHex(result.digest); + const out_zig_path = try fs.path.join(arena, &.{ "o", &hex_digest, translated_basename }); + const zig_file = comp.dirs.local_cache.handle.openFile(out_zig_path, .{}) catch |err| { + const path = comp.dirs.local_cache.path orelse "."; + fatal("unable to open cached translated zig file '{s}{s}{s}': {s}", .{ + path, + fs.path.sep_str, + out_zig_path, + @errorName(err), + }); }; + defer zig_file.close(); + var stdout_writer = fs.File.stdout().writer(&stdout_buffer); + var file_reader = zig_file.reader(&.{}); + _ = try stdout_writer.interface.sendFileAll(&file_reader, .unlimited); + try stdout_writer.interface.flush(); + return cleanExit(); } - - return cleanExit(); } pub fn translateC( diff --git a/tools/incr-check.zig b/tools/incr-check.zig index ed7443046a11..183d59bf887c 100644 --- a/tools/incr-check.zig +++ b/tools/incr-check.zig @@ -259,20 +259,7 @@ const Eval = struct { switch (header.tag) { .error_bundle => { - const EbHdr = std.zig.Server.Message.ErrorBundle; - const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body)); - const extra_bytes = - body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len]; - const string_bytes = - body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len]; - // TODO: use @ptrCast when the compiler supports it - const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes); - const extra_array = try arena.alloc(u32, unaligned_extra.len); - @memcpy(extra_array, unaligned_extra); - const result_error_bundle: std.zig.ErrorBundle = .{ - .string_bytes = try arena.dupe(u8, string_bytes), - .extra = extra_array, - }; + const result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body); if (stderr.bufferedLen() > 0) { const stderr_data = try poller.toOwnedSlice(.stderr); if (eval.allow_stderr) {