Commit 8f2f12f940
Changed files (2)
src/Compilation.zig
@@ -2702,6 +2702,72 @@ pub fn makeBinFileWritable(self: *Compilation) !void {
return self.bin_file.makeWritable();
}
+const Header = extern struct {
+ intern_pool: extern struct {
+ items_len: u32,
+ extra_len: u32,
+ limbs_len: u32,
+ string_bytes_len: u32,
+ },
+};
+
+/// Note that all state that is included in the cache hash namespace is *not*
+/// saved, such as the target and most CLI flags. A cache hit will only occur
+/// when subsequent compiler invocations use the same set of flags.
+pub fn saveState(comp: *Compilation) !void {
+ var bufs_list: [6]std.os.iovec_const = undefined;
+ var bufs_len: usize = 0;
+
+ const emit = comp.bin_file.options.emit orelse return;
+
+ if (comp.bin_file.options.module) |mod| {
+ const ip = &mod.intern_pool;
+ const header: Header = .{
+ .intern_pool = .{
+ .items_len = @intCast(ip.items.len),
+ .extra_len = @intCast(ip.extra.items.len),
+ .limbs_len = @intCast(ip.limbs.items.len),
+ .string_bytes_len = @intCast(ip.string_bytes.items.len),
+ },
+ };
+ addBuf(&bufs_list, &bufs_len, mem.asBytes(&header));
+ addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.limbs.items));
+ addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.extra.items));
+ addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.items.items(.data)));
+ addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.items.items(.tag)));
+ addBuf(&bufs_list, &bufs_len, ip.string_bytes.items);
+
+ // TODO: compilation errors
+ // TODO: files
+ // TODO: namespaces
+ // TODO: decls
+ // TODO: linker state
+ }
+ var basename_buf: [255]u8 = undefined;
+ const basename = std.fmt.bufPrint(&basename_buf, "{s}.zcs", .{
+ comp.bin_file.options.root_name,
+ }) catch o: {
+ basename_buf[basename_buf.len - 4 ..].* = ".zcs".*;
+ break :o &basename_buf;
+ };
+
+ // Using an atomic file prevents a crash or power failure from corrupting
+ // the previous incremental compilation state.
+ var af = try emit.directory.handle.atomicFile(basename, .{});
+ defer af.deinit();
+ try af.file.pwritevAll(bufs_list[0..bufs_len], 0);
+ try af.finish();
+}
+
+fn addBuf(bufs_list: []std.os.iovec_const, bufs_len: *usize, buf: []const u8) void {
+ const i = bufs_len.*;
+ bufs_len.* = i + 1;
+ bufs_list[i] = .{
+ .iov_base = buf.ptr,
+ .iov_len = buf.len,
+ };
+}
+
/// This function is temporally single-threaded.
pub fn totalErrorCount(self: *Compilation) u32 {
var total: usize = self.failed_c_objects.count() +
src/main.zig
@@ -594,6 +594,7 @@ const usage_build_generic =
\\ --debug-log [scope] Enable printing debug/info log messages for scope
\\ --debug-compile-errors Crash with helpful diagnostics at the first compile error
\\ --debug-link-snapshot Enable dumping of the linker's state in JSON format
+ \\ --debug-incremental Enable experimental feature: incremental compilation
\\
;
@@ -904,6 +905,7 @@ fn buildOutputType(
var minor_subsystem_version: ?u32 = null;
var wasi_exec_model: ?std.builtin.WasiExecModel = null;
var enable_link_snapshots: bool = false;
+ var debug_incremental: bool = false;
var install_name: ?[]const u8 = null;
var hash_style: link.HashStyle = .both;
var entitlements: ?[]const u8 = null;
@@ -1272,6 +1274,8 @@ fn buildOutputType(
} else {
enable_link_snapshots = true;
}
+ } else if (mem.eql(u8, arg, "--debug-incremental")) {
+ debug_incremental = true;
} else if (mem.eql(u8, arg, "--entitlements")) {
entitlements = args_iter.nextOrFatal();
} else if (mem.eql(u8, arg, "-fcompiler-rt")) {
@@ -3591,11 +3595,16 @@ fn buildOutputType(
}
updateModule(comp) catch |err| switch (err) {
- error.SemanticAnalyzeFail => if (listen == .none) process.exit(1),
+ error.SemanticAnalyzeFail => {
+ assert(listen == .none);
+ saveState(comp, debug_incremental);
+ process.exit(1);
+ },
else => |e| return e,
};
if (build_options.only_c) return cleanExit();
try comp.makeBinFileExecutable();
+ saveState(comp, debug_incremental);
if (test_exec_args.items.len == 0 and object_format == .c) default_exec_args: {
// Default to using `zig run` to execute the produced .c code from `zig test`.
@@ -3658,6 +3667,14 @@ fn buildOutputType(
return cleanExit();
}
+fn saveState(comp: *Compilation, debug_incremental: bool) void {
+ if (debug_incremental) {
+ comp.saveState() catch |err| {
+ warn("unable to save incremental compilation state: {s}", .{@errorName(err)});
+ };
+ }
+}
+
fn serve(
comp: *Compilation,
in: fs.File,