diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index f3360c8108ea..b48fb52e821b 100644 --- a/lib/compiler/objcopy.zig +++ b/lib/compiler/objcopy.zig @@ -201,9 +201,10 @@ fn cmdObjCopy( if (seen_update) fatal("zig objcopy only supports 1 update for now", .{}); seen_update = true; - try server.serveEmitBinPath(output, .{ - .flags = .{ .cache_hit = false }, - }); + // The build system already knows what the output is at this point, we + // only need to communicate that the process has finished. + // Use the empty error bundle to indicate that the update is done. + try server.serveErrorBundle(std.zig.ErrorBundle.empty); }, else => fatal("unsupported message: {s}", .{@tagName(hdr.tag)}), } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 03743cf52e3a..82810bb02fd9 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -2373,7 +2373,7 @@ pub const LazyPath = union(enum) { // basis for not traversing up too many directories. var file_path: Cache.Path = .{ - .root_dir = gen.file.step.owner.build_root, + .root_dir = Cache.Directory.cwd(), .sub_path = gen.file.path orelse { std.debug.lockStdErr(); const stderr = std.io.getStdErr(); diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 1dafcf3bb76a..1eabdd54e66a 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -896,8 +896,8 @@ pub const Manifest = struct { } } - /// Returns a hex encoded hash of the inputs. - pub fn final(self: *Manifest) HexDigest { + /// Returns a binary hash of the inputs. + pub fn finalBin(self: *Manifest) BinDigest { assert(self.manifest_file != null); // We don't close the manifest file yet, because we want to @@ -908,7 +908,12 @@ pub const Manifest = struct { var bin_digest: BinDigest = undefined; self.hash.hasher.final(&bin_digest); + return bin_digest; + } + /// Returns a hex encoded hash of the inputs. + pub fn final(self: *Manifest) HexDigest { + const bin_digest = self.finalBin(); return binToHex(bin_digest); } diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index 9857db5a1fe2..23f8a0269283 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -100,6 +100,15 @@ pub fn start( } fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) void { + rebuildTestsWorkerRunFallible(run, ttyconf, parent_prog_node) catch |err| { + const compile = run.producer.?; + log.err("step '{s}': failed to rebuild in fuzz mode: {s}", .{ + compile.step.name, @errorName(err), + }); + }; +} + +fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) !void { const gpa = run.step.owner.allocator; const stderr = std.io.getStdErr(); @@ -121,14 +130,9 @@ fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog const rebuilt_bin_path = result catch |err| switch (err) { error.MakeFailed => return, - else => { - log.err("step '{s}': failed to rebuild in fuzz mode: {s}", .{ - compile.step.name, @errorName(err), - }); - return; - }, + else => |other| return other, }; - run.rebuilt_executable = rebuilt_bin_path; + run.rebuilt_executable = try rebuilt_bin_path.join(gpa, compile.out_filename); } fn fuzzWorkerRun( diff --git a/lib/std/Build/Fuzz/WebServer.zig b/lib/std/Build/Fuzz/WebServer.zig index 8534d95d16cc..fbf6b8dbcee2 100644 --- a/lib/std/Build/Fuzz/WebServer.zig +++ b/lib/std/Build/Fuzz/WebServer.zig @@ -8,6 +8,8 @@ const Coverage = std.debug.Coverage; const abi = std.Build.Fuzz.abi; const log = std.log; const assert = std.debug.assert; +const Cache = std.Build.Cache; +const Path = Cache.Path; const WebServer = @This(); @@ -31,6 +33,10 @@ coverage_mutex: std.Thread.Mutex, /// Signaled when `coverage_files` changes. coverage_condition: std.Thread.Condition, +const fuzzer_bin_name = "fuzzer"; +const fuzzer_arch_os_abi = "wasm32-freestanding"; +const fuzzer_cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext"; + const CoverageMap = struct { mapped_memory: []align(std.mem.page_size) const u8, coverage: Coverage, @@ -181,9 +187,18 @@ fn serveWasm( // Do the compilation every request, so that the user can edit the files // and see the changes without restarting the server. - const wasm_binary_path = try buildWasmBinary(ws, arena, optimize_mode); + const wasm_base_path = try buildWasmBinary(ws, arena, optimize_mode); + const bin_name = try std.zig.binNameAlloc(arena, .{ + .root_name = fuzzer_bin_name, + .target = std.zig.system.resolveTargetQuery(std.Build.parseTargetQuery(.{ + .arch_os_abi = fuzzer_arch_os_abi, + .cpu_features = fuzzer_cpu_features, + }) catch unreachable) catch unreachable, + .output_mode = .Exe, + }); // std.http.Server does not have a sendfile API yet. - const file_contents = try std.fs.cwd().readFileAlloc(gpa, wasm_binary_path, 10 * 1024 * 1024); + const bin_path = try wasm_base_path.join(arena, bin_name); + const file_contents = try bin_path.root_dir.handle.readFileAlloc(gpa, bin_path.sub_path, 10 * 1024 * 1024); defer gpa.free(file_contents); try request.respond(file_contents, .{ .extra_headers = &.{ @@ -197,7 +212,7 @@ fn buildWasmBinary( ws: *WebServer, arena: Allocator, optimize_mode: std.builtin.OptimizeMode, -) ![]const u8 { +) !Path { const gpa = ws.gpa; const main_src_path: Build.Cache.Path = .{ @@ -219,11 +234,11 @@ fn buildWasmBinary( ws.zig_exe_path, "build-exe", // "-fno-entry", // "-O", @tagName(optimize_mode), // - "-target", "wasm32-freestanding", // - "-mcpu", "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext", // + "-target", fuzzer_arch_os_abi, // + "-mcpu", fuzzer_cpu_features, // "--cache-dir", ws.global_cache_directory.path orelse ".", // "--global-cache-dir", ws.global_cache_directory.path orelse ".", // - "--name", "fuzzer", // + "--name", fuzzer_bin_name, // "-rdynamic", // "-fsingle-threaded", // "--dep", "Walk", // @@ -251,7 +266,7 @@ fn buildWasmBinary( try sendMessage(child.stdin.?, .exit); const Header = std.zig.Server.Message.Header; - var result: ?[]const u8 = null; + var result: ?Path = null; var result_error_bundle = std.zig.ErrorBundle.empty; const stdout = poller.fifo(.stdout); @@ -288,13 +303,17 @@ fn buildWasmBinary( .extra = extra_array, }; }, - .emit_bin_path => { - const EbpHdr = std.zig.Server.Message.EmitBinPath; + .emit_digest => { + const EbpHdr = std.zig.Server.Message.EmitDigest; const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); if (!ebp_hdr.flags.cache_hit) { log.info("source changes detected; rebuilt wasm component", .{}); } - result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); + const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; + result = Path{ + .root_dir = ws.global_cache_directory, + .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)), + }; }, else => {}, // ignore other messages } @@ -568,10 +587,7 @@ fn prepareTables( }; errdefer gop.value_ptr.coverage.deinit(gpa); - const rebuilt_exe_path: Build.Cache.Path = .{ - .root_dir = Build.Cache.Directory.cwd(), - .sub_path = run_step.rebuilt_executable.?, - }; + const rebuilt_exe_path = run_step.rebuilt_executable.?; var debug_info = std.debug.Info.load(gpa, rebuilt_exe_path, &gop.value_ptr.coverage) catch |err| { log.err("step '{s}': failed to load debug information for '{}': {s}", .{ run_step.step.name, rebuilt_exe_path, @errorName(err), diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 47a6e49a82c2..346ab2c9b324 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -317,6 +317,8 @@ const Build = std.Build; const Allocator = std.mem.Allocator; const assert = std.debug.assert; const builtin = @import("builtin"); +const Cache = Build.Cache; +const Path = Cache.Path; pub fn evalChildProcess(s: *Step, argv: []const []const u8) ![]u8 { const run_result = try captureChildProcess(s, std.Progress.Node.none, argv); @@ -373,7 +375,7 @@ pub fn evalZigProcess( argv: []const []const u8, prog_node: std.Progress.Node, watch: bool, -) !?[]const u8 { +) !?Path { if (s.getZigProcess()) |zp| update: { assert(watch); if (std.Progress.have_ipc) if (zp.progress_ipc_fd) |fd| prog_node.setIpcFd(fd); @@ -477,7 +479,7 @@ pub fn evalZigProcess( return result; } -fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?[]const u8 { +fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { const b = s.owner; const arena = b.allocator; @@ -487,7 +489,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?[]const u8 { if (!watch) try sendMessage(zp.child.stdin.?, .exit); const Header = std.zig.Server.Message.Header; - var result: ?[]const u8 = null; + var result: ?Path = null; const stdout = zp.poller.fifo(.stdout); @@ -531,16 +533,15 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?[]const u8 { break; } }, - .emit_bin_path => { - const EbpHdr = std.zig.Server.Message.EmitBinPath; + .emit_digest => { + const EbpHdr = std.zig.Server.Message.EmitDigest; const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); s.result_cached = ebp_hdr.flags.cache_hit; - result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); - if (watch) { - // This message indicates the end of the update. - stdout.discard(body.len); - break; - } + const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; + result = Path{ + .root_dir = b.cache_root, + .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)), + }; }, .file_system_inputs => { s.clearWatchInputs(); diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 054dda9e901b..1aeebbb55b81 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -17,6 +17,7 @@ const Module = std.Build.Module; const InstallDir = std.Build.InstallDir; const GeneratedFile = std.Build.GeneratedFile; const Compile = @This(); +const Path = std.Build.Cache.Path; pub const base_id: Step.Id = .compile; @@ -1765,7 +1766,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { const zig_args = try getZigArgs(compile, false); - const maybe_output_bin_path = step.evalZigProcess( + const maybe_output_dir = step.evalZigProcess( zig_args, options.progress_node, (b.graph.incremental == true) and options.watch, @@ -1779,53 +1780,51 @@ fn make(step: *Step, options: Step.MakeOptions) !void { }; // Update generated files - if (maybe_output_bin_path) |output_bin_path| { - const output_dir = fs.path.dirname(output_bin_path).?; - + if (maybe_output_dir) |output_dir| { if (compile.emit_directory) |lp| { - lp.path = output_dir; + lp.path = b.fmt("{}", .{output_dir}); } // -femit-bin[=path] (default) Output machine code if (compile.generated_bin) |bin| { - bin.path = b.pathJoin(&.{ output_dir, compile.out_filename }); + bin.path = output_dir.joinString(b.allocator, compile.out_filename) catch @panic("OOM"); } - const sep = std.fs.path.sep; + const sep = std.fs.path.sep_str; // output PDB if someone requested it if (compile.generated_pdb) |pdb| { - pdb.path = b.fmt("{s}{c}{s}.pdb", .{ output_dir, sep, compile.name }); + pdb.path = b.fmt("{}" ++ sep ++ "{s}.pdb", .{ output_dir, compile.name }); } // -femit-implib[=path] (default) Produce an import .lib when building a Windows DLL if (compile.generated_implib) |implib| { - implib.path = b.fmt("{s}{c}{s}.lib", .{ output_dir, sep, compile.name }); + implib.path = b.fmt("{}" ++ sep ++ "{s}.lib", .{ output_dir, compile.name }); } // -femit-h[=path] Generate a C header file (.h) if (compile.generated_h) |lp| { - lp.path = b.fmt("{s}{c}{s}.h", .{ output_dir, sep, compile.name }); + lp.path = b.fmt("{}" ++ sep ++ "{s}.h", .{ output_dir, compile.name }); } // -femit-docs[=path] Create a docs/ dir with html documentation if (compile.generated_docs) |generated_docs| { - generated_docs.path = b.pathJoin(&.{ output_dir, "docs" }); + generated_docs.path = output_dir.joinString(b.allocator, "docs") catch @panic("OOM"); } // -femit-asm[=path] Output .s (assembly code) if (compile.generated_asm) |lp| { - lp.path = b.fmt("{s}{c}{s}.s", .{ output_dir, sep, compile.name }); + lp.path = b.fmt("{}" ++ sep ++ "{s}.s", .{ output_dir, compile.name }); } // -femit-llvm-ir[=path] Produce a .ll file with optimized LLVM IR (requires LLVM extensions) if (compile.generated_llvm_ir) |lp| { - lp.path = b.fmt("{s}{c}{s}.ll", .{ output_dir, sep, compile.name }); + lp.path = b.fmt("{}" ++ sep ++ "{s}.ll", .{ output_dir, compile.name }); } // -femit-llvm-bc[=path] Produce an optimized LLVM module as a .bc file (requires LLVM extensions) if (compile.generated_llvm_bc) |lp| { - lp.path = b.fmt("{s}{c}{s}.bc", .{ output_dir, sep, compile.name }); + lp.path = b.fmt("{}" ++ sep ++ "{s}.bc", .{ output_dir, compile.name }); } } @@ -1841,7 +1840,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { } } -pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) ![]const u8 { +pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path { const gpa = c.step.owner.allocator; c.step.result_error_msgs.clearRetainingCapacity(); diff --git a/lib/std/Build/Step/InstallArtifact.zig b/lib/std/Build/Step/InstallArtifact.zig index 4e778d897cf8..3d404eb8ca92 100644 --- a/lib/std/Build/Step/InstallArtifact.zig +++ b/lib/std/Build/Step/InstallArtifact.zig @@ -125,10 +125,10 @@ fn make(step: *Step, options: Step.MakeOptions) !void { if (install_artifact.dest_dir) |dest_dir| { const full_dest_path = b.getInstallPath(dest_dir, install_artifact.dest_sub_path); - const full_src_path = install_artifact.emitted_bin.?.getPath2(b, step); - const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { + const src_path = install_artifact.emitted_bin.?.getPath3(b, step); + const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_dest_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_path, full_dest_path, @errorName(err), + src_path.sub_path, full_dest_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; @@ -141,22 +141,22 @@ fn make(step: *Step, options: Step.MakeOptions) !void { } if (install_artifact.implib_dir) |implib_dir| { - const full_src_path = install_artifact.emitted_implib.?.getPath2(b, step); - const full_implib_path = b.getInstallPath(implib_dir, fs.path.basename(full_src_path)); - const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| { + const src_path = install_artifact.emitted_implib.?.getPath3(b, step); + const full_implib_path = b.getInstallPath(implib_dir, fs.path.basename(src_path.sub_path)); + const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_implib_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_path, full_implib_path, @errorName(err), + src_path.sub_path, full_implib_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; } if (install_artifact.pdb_dir) |pdb_dir| { - const full_src_path = install_artifact.emitted_pdb.?.getPath2(b, step); - const full_pdb_path = b.getInstallPath(pdb_dir, fs.path.basename(full_src_path)); - const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| { + const src_path = install_artifact.emitted_pdb.?.getPath3(b, step); + const full_pdb_path = b.getInstallPath(pdb_dir, fs.path.basename(src_path.sub_path)); + const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_pdb_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_path, full_pdb_path, @errorName(err), + src_path.sub_path, full_pdb_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; @@ -164,11 +164,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void { if (install_artifact.h_dir) |h_dir| { if (install_artifact.emitted_h) |emitted_h| { - const full_src_path = emitted_h.getPath2(b, step); - const full_h_path = b.getInstallPath(h_dir, fs.path.basename(full_src_path)); - const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { + const src_path = emitted_h.getPath3(b, step); + const full_h_path = b.getInstallPath(h_dir, fs.path.basename(src_path.sub_path)); + const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_h_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_path, full_h_path, @errorName(err), + src_path.sub_path, full_h_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; @@ -176,22 +176,22 @@ fn make(step: *Step, options: Step.MakeOptions) !void { for (install_artifact.artifact.installed_headers.items) |installation| switch (installation) { .file => |file| { - const full_src_path = file.source.getPath2(b, step); + const src_path = file.source.getPath3(b, step); const full_h_path = b.getInstallPath(h_dir, file.dest_rel_path); - const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { + const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_h_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_path, full_h_path, @errorName(err), + src_path.sub_path, full_h_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; }, .directory => |dir| { - const full_src_dir_path = dir.source.getPath2(b, step); + const src_dir_path = dir.source.getPath3(b, step); const full_h_prefix = b.getInstallPath(h_dir, dir.dest_rel_path); - var src_dir = b.build_root.handle.openDir(full_src_dir_path, .{ .iterate = true }) catch |err| { + var src_dir = src_dir_path.root_dir.handle.openDir(src_dir_path.sub_path, .{ .iterate = true }) catch |err| { return step.fail("unable to open source directory '{s}': {s}", .{ - full_src_dir_path, @errorName(err), + src_dir_path.sub_path, @errorName(err), }); }; defer src_dir.close(); @@ -208,14 +208,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void { continue :next_entry; } } - const full_src_entry_path = b.pathJoin(&.{ full_src_dir_path, entry.path }); + + const src_entry_path = src_dir_path.join(b.allocator, entry.path) catch @panic("OOM"); const full_dest_path = b.pathJoin(&.{ full_h_prefix, entry.path }); switch (entry.kind) { .directory => try cwd.makePath(full_dest_path), .file => { - const p = fs.Dir.updateFile(cwd, full_src_entry_path, cwd, full_dest_path, .{}) catch |err| { + const p = fs.Dir.updateFile(src_entry_path.root_dir.handle, src_entry_path.sub_path, cwd, full_dest_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_entry_path, full_dest_path, @errorName(err), + src_entry_path.sub_path, full_dest_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 5d9ebce9aa44..0c011e25eddd 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -7,6 +7,7 @@ const mem = std.mem; const process = std.process; const EnvMap = process.EnvMap; const assert = std.debug.assert; +const Path = Build.Cache.Path; const Run = @This(); @@ -93,7 +94,7 @@ cached_test_metadata: ?CachedTestMetadata = null, /// Populated during the fuzz phase if this run step corresponds to a unit test /// executable that contains fuzz tests. -rebuilt_executable: ?[]const u8, +rebuilt_executable: ?Path, /// If this Run step was produced by a Compile step, it is tracked here. producer: ?*Step.Compile, @@ -872,7 +873,7 @@ pub fn rerunInFuzzMode( .artifact => |pa| { const artifact = pa.artifact; const file_path = if (artifact == run.producer.?) - run.rebuilt_executable.? + b.fmt("{}", .{run.rebuilt_executable.?}) else (artifact.installed_path orelse artifact.generated_bin.?.path.?); try argv_list.append(arena, b.fmt("{s}{s}", .{ pa.prefix, file_path })); diff --git a/lib/std/Build/Step/TranslateC.zig b/lib/std/Build/Step/TranslateC.zig index ac4729abd0cc..77fd3e39ad79 100644 --- a/lib/std/Build/Step/TranslateC.zig +++ b/lib/std/Build/Step/TranslateC.zig @@ -153,12 +153,12 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try argv_list.append(c_macro); } - try argv_list.append(translate_c.source.getPath2(b, step)); + const c_source_path = translate_c.source.getPath2(b, step); + try argv_list.append(c_source_path); - const output_path = try step.evalZigProcess(argv_list.items, prog_node, false); + const output_dir = try step.evalZigProcess(argv_list.items, prog_node, false); - translate_c.out_basename = fs.path.basename(output_path.?); - const output_dir = fs.path.dirname(output_path.?).?; - - translate_c.output_file.path = b.pathJoin(&.{ output_dir, translate_c.out_basename }); + const basename = std.fs.path.stem(std.fs.path.basename(c_source_path)); + translate_c.out_basename = b.fmt("{s}.zig", .{basename}); + translate_c.output_file.path = output_dir.?.joinString(b.allocator, translate_c.out_basename) catch @panic("OOM"); } diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig index 7ce017045d96..0ed9cfcd0b12 100644 --- a/lib/std/zig/Server.zig +++ b/lib/std/zig/Server.zig @@ -14,8 +14,8 @@ pub const Message = struct { zig_version, /// Body is an ErrorBundle. error_bundle, - /// Body is a EmitBinPath. - emit_bin_path, + /// Body is a EmitDigest. + emit_digest, /// Body is a TestMetadata test_metadata, /// Body is a TestResults @@ -82,8 +82,8 @@ pub const Message = struct { }; /// Trailing: - /// * file system path where the emitted binary can be found - pub const EmitBinPath = extern struct { + /// * the hex digest of the cache directory within the /o/ subdirectory. + pub const EmitDigest = extern struct { flags: Flags, pub const Flags = packed struct(u8) { @@ -196,17 +196,17 @@ pub fn serveU64Message(s: *Server, tag: OutMessage.Tag, int: u64) !void { }, &.{std.mem.asBytes(&msg_le)}); } -pub fn serveEmitBinPath( +pub fn serveEmitDigest( s: *Server, - fs_path: []const u8, - header: OutMessage.EmitBinPath, + digest: *const [Cache.bin_digest_len]u8, + header: OutMessage.EmitDigest, ) !void { try s.serveMessage(.{ - .tag = .emit_bin_path, - .bytes_len = @intCast(fs_path.len + @sizeOf(OutMessage.EmitBinPath)), + .tag = .emit_digest, + .bytes_len = @intCast(digest.len + @sizeOf(OutMessage.EmitDigest)), }, &.{ std.mem.asBytes(&header), - fs_path, + digest, }); } @@ -328,3 +328,4 @@ const Allocator = std.mem.Allocator; const assert = std.debug.assert; const native_endian = builtin.target.cpu.arch.endian(); const need_bswap = native_endian != .little; +const Cache = std.Build.Cache; diff --git a/src/Compilation.zig b/src/Compilation.zig index fab0496b225b..48387e450137 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -39,6 +39,8 @@ const Air = @import("Air.zig"); const Builtin = @import("Builtin.zig"); const LlvmObject = @import("codegen/llvm.zig").Object; const dev = @import("dev.zig"); +pub const Directory = Cache.Directory; +const Path = Cache.Path; pub const Config = @import("Compilation/Config.zig"); @@ -70,9 +72,9 @@ bin_file: ?*link.File, /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin) sysroot: ?[]const u8, /// This is `null` when not building a Windows DLL, or when `-fno-emit-implib` is used. -implib_emit: ?Emit, +implib_emit: ?Path, /// This is non-null when `-femit-docs` is provided. -docs_emit: ?Emit, +docs_emit: ?Path, root_name: [:0]const u8, include_compiler_rt: bool, objects: []Compilation.LinkObject, @@ -269,27 +271,9 @@ llvm_opt_bisect_limit: c_int, file_system_inputs: ?*std.ArrayListUnmanaged(u8), -pub const Emit = struct { - /// Where the output will go. - directory: Directory, - /// Path to the output file, relative to `directory`. - sub_path: []const u8, - - /// Returns the full path to `basename` if it were in the same directory as the - /// `Emit` sub_path. - pub fn basenamePath(emit: Emit, arena: Allocator, basename: []const u8) ![:0]const u8 { - const full_path = if (emit.directory.path) |p| - try std.fs.path.join(arena, &[_][]const u8{ p, emit.sub_path }) - else - emit.sub_path; - - if (std.fs.path.dirname(full_path)) |dirname| { - return try std.fs.path.joinZ(arena, &.{ dirname, basename }); - } else { - return try arena.dupeZ(u8, basename); - } - } -}; +/// This is the digest of the cache for the current compilation. +/// This digest will be known after update() is called. +digest: ?[Cache.bin_digest_len]u8 = null, pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; pub const SemaError = Zcu.SemaError; @@ -868,8 +852,6 @@ pub const LldError = struct { } }; -pub const Directory = Cache.Directory; - pub const EmitLoc = struct { /// If this is `null` it means the file will be output to the cache directory. /// When provided, both the open file handle and the path name must outlive the `Compilation`. @@ -1672,7 +1654,9 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // In the case of incremental cache mode, this `artifact_directory` // is computed based on a hash of non-linker inputs, and it is where all // build artifacts are stored (even while in-progress). + comp.digest = hash.peekBin(); const digest = hash.final(); + const artifact_sub_dir = "o" ++ std.fs.path.sep_str ++ digest; var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); errdefer artifact_dir.close(); @@ -1688,8 +1672,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil comp.cache_use = .{ .incremental = incremental }; if (options.emit_bin) |emit_bin| { - const emit: Emit = .{ - .directory = emit_bin.directory orelse artifact_directory, + const emit: Path = .{ + .root_dir = emit_bin.directory orelse artifact_directory, .sub_path = emit_bin.basename, }; comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts); @@ -1697,14 +1681,14 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil if (options.emit_implib) |emit_implib| { comp.implib_emit = .{ - .directory = emit_implib.directory orelse artifact_directory, + .root_dir = emit_implib.directory orelse artifact_directory, .sub_path = emit_implib.basename, }; } if (options.emit_docs) |emit_docs| { comp.docs_emit = .{ - .directory = emit_docs.directory orelse artifact_directory, + .root_dir = emit_docs.directory orelse artifact_directory, .sub_path = emit_docs.basename, }; } @@ -2121,9 +2105,11 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { comp.last_update_was_cache_hit = true; log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name}); - const digest = man.final(); + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); - comp.wholeCacheModeSetBinFilePath(whole, &digest); + comp.digest = bin_digest; + comp.wholeCacheModeSetBinFilePath(whole, &hex_digest); assert(whole.lock == null); whole.lock = man.toOwnedLock(); @@ -2155,21 +2141,21 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { if (whole.implib_sub_path) |sub_path| { comp.implib_emit = .{ - .directory = tmp_artifact_directory, + .root_dir = tmp_artifact_directory, .sub_path = std.fs.path.basename(sub_path), }; } if (whole.docs_sub_path) |sub_path| { comp.docs_emit = .{ - .directory = tmp_artifact_directory, + .root_dir = tmp_artifact_directory, .sub_path = std.fs.path.basename(sub_path), }; } if (whole.bin_sub_path) |sub_path| { - const emit: Emit = .{ - .directory = tmp_artifact_directory, + const emit: Path = .{ + .root_dir = tmp_artifact_directory, .sub_path = std.fs.path.basename(sub_path), }; comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts); @@ -2329,7 +2315,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { try man.populateOtherManifest(pwc.manifest, pwc.prefix_map); } - const digest = man.final(); + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); // Rename the temporary directory into place. // Close tmp dir and link.File to avoid open handle during rename. @@ -2341,7 +2328,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { const s = std.fs.path.sep_str; const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int); - const o_sub_path = "o" ++ s ++ digest; + const o_sub_path = "o" ++ s ++ hex_digest; // Work around windows `AccessDenied` if any files within this // directory are open by closing and reopening the file handles. @@ -2376,14 +2363,15 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { }, ); }; - comp.wholeCacheModeSetBinFilePath(whole, &digest); + comp.digest = bin_digest; + comp.wholeCacheModeSetBinFilePath(whole, &hex_digest); // The linker flush functions need to know the final output path // for debug info purposes because executable debug info contains // references object file paths. if (comp.bin_file) |lf| { lf.emit = .{ - .directory = comp.local_cache_directory, + .root_dir = comp.local_cache_directory, .sub_path = whole.bin_sub_path.?, }; @@ -2393,9 +2381,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { } } - try flush(comp, arena, .main, main_progress_node); - - if (try comp.totalErrorCount() != 0) return; + try flush(comp, arena, .{ + .root_dir = comp.local_cache_directory, + .sub_path = o_sub_path, + }, .main, main_progress_node); // Failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { @@ -2410,8 +2399,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { assert(whole.lock == null); whole.lock = man.toOwnedLock(); }, - .incremental => { - try flush(comp, arena, .main, main_progress_node); + .incremental => |incremental| { + try flush(comp, arena, .{ + .root_dir = incremental.artifact_directory, + }, .main, main_progress_node); }, } } @@ -2440,7 +2431,13 @@ pub fn appendFileSystemInput( std.debug.panic("missing prefix directory: {}, {s}", .{ root, sub_file_path }); } -fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { +fn flush( + comp: *Compilation, + arena: Allocator, + default_artifact_directory: Path, + tid: Zcu.PerThread.Id, + prog_node: std.Progress.Node, +) !void { if (comp.bin_file) |lf| { // This is needed before reading the error flags. lf.flush(arena, tid, prog_node) catch |err| switch (err) { @@ -2454,17 +2451,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: try link.File.C.flushEmitH(zcu); if (zcu.llvm_object) |llvm_object| { - const default_emit = switch (comp.cache_use) { - .whole => |whole| .{ - .directory = whole.tmp_artifact_directory.?, - .sub_path = "dummy", - }, - .incremental => |incremental| .{ - .directory = incremental.artifact_directory, - .sub_path = "dummy", - }, - }; - try emitLlvmObject(comp, arena, default_emit, null, llvm_object, prog_node); + try emitLlvmObject(comp, arena, default_artifact_directory, null, llvm_object, prog_node); } } } @@ -2533,7 +2520,7 @@ fn wholeCacheModeSetBinFilePath( @memcpy(sub_path[digest_start..][0..digest.len], digest); comp.implib_emit = .{ - .directory = comp.local_cache_directory, + .root_dir = comp.local_cache_directory, .sub_path = sub_path, }; } @@ -2542,7 +2529,7 @@ fn wholeCacheModeSetBinFilePath( @memcpy(sub_path[digest_start..][0..digest.len], digest); comp.docs_emit = .{ - .directory = comp.local_cache_directory, + .root_dir = comp.local_cache_directory, .sub_path = sub_path, }; } @@ -2745,7 +2732,7 @@ fn emitOthers(comp: *Compilation) void { pub fn emitLlvmObject( comp: *Compilation, arena: Allocator, - default_emit: Emit, + default_artifact_directory: Path, bin_emit_loc: ?EmitLoc, llvm_object: LlvmObject.Ptr, prog_node: std.Progress.Node, @@ -2756,10 +2743,10 @@ pub fn emitLlvmObject( try llvm_object.emit(.{ .pre_ir_path = comp.verbose_llvm_ir, .pre_bc_path = comp.verbose_llvm_bc, - .bin_path = try resolveEmitLoc(arena, default_emit, bin_emit_loc), - .asm_path = try resolveEmitLoc(arena, default_emit, comp.emit_asm), - .post_ir_path = try resolveEmitLoc(arena, default_emit, comp.emit_llvm_ir), - .post_bc_path = try resolveEmitLoc(arena, default_emit, comp.emit_llvm_bc), + .bin_path = try resolveEmitLoc(arena, default_artifact_directory, bin_emit_loc), + .asm_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_asm), + .post_ir_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_ir), + .post_bc_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_bc), .is_debug = comp.root_mod.optimize_mode == .Debug, .is_small = comp.root_mod.optimize_mode == .ReleaseSmall, @@ -2772,14 +2759,14 @@ pub fn emitLlvmObject( fn resolveEmitLoc( arena: Allocator, - default_emit: Emit, + default_artifact_directory: Path, opt_loc: ?EmitLoc, ) Allocator.Error!?[*:0]const u8 { const loc = opt_loc orelse return null; const slice = if (loc.directory) |directory| try directory.joinZ(arena, &.{loc.basename}) else - try default_emit.basenamePath(arena, loc.basename); + try default_artifact_directory.joinStringZ(arena, loc.basename); return slice.ptr; } @@ -3035,7 +3022,7 @@ pub fn saveState(comp: *Compilation) !void { // Using an atomic file prevents a crash or power failure from corrupting // the previous incremental compilation state. - var af = try lf.emit.directory.handle.atomicFile(basename, .{}); + var af = try lf.emit.root_dir.handle.atomicFile(basename, .{}); defer af.deinit(); try af.file.pwritevAll(bufs.items, 0); try af.finish(); @@ -4000,11 +3987,11 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{}); const emit = comp.docs_emit.?; - var out_dir = emit.directory.handle.makeOpenPath(emit.sub_path, .{}) catch |err| { + var out_dir = emit.root_dir.handle.makeOpenPath(emit.sub_path, .{}) catch |err| { return comp.lockAndSetMiscFailure( .docs_copy, "unable to create output directory '{}{s}': {s}", - .{ emit.directory, emit.sub_path, @errorName(err) }, + .{ emit.root_dir, emit.sub_path, @errorName(err) }, ); }; defer out_dir.close(); @@ -4024,7 +4011,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { return comp.lockAndSetMiscFailure( .docs_copy, "unable to create '{}{s}/sources.tar': {s}", - .{ emit.directory, emit.sub_path, @errorName(err) }, + .{ emit.root_dir, emit.sub_path, @errorName(err) }, ); }; defer tar_file.close(); @@ -4223,11 +4210,11 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye try comp.updateSubCompilation(sub_compilation, .docs_wasm, prog_node); const emit = comp.docs_emit.?; - var out_dir = emit.directory.handle.makeOpenPath(emit.sub_path, .{}) catch |err| { + var out_dir = emit.root_dir.handle.makeOpenPath(emit.sub_path, .{}) catch |err| { return comp.lockAndSetMiscFailure( .docs_copy, "unable to create output directory '{}{s}': {s}", - .{ emit.directory, emit.sub_path, @errorName(err) }, + .{ emit.root_dir, emit.sub_path, @errorName(err) }, ); }; defer out_dir.close(); @@ -4241,7 +4228,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}{s}' to '{}{s}': {s}", .{ sub_compilation.local_cache_directory, sub_compilation.cache_use.whole.bin_sub_path.?, - emit.directory, + emit.root_dir, emit.sub_path, @errorName(err), }); @@ -4403,7 +4390,7 @@ pub fn obtainWin32ResourceCacheManifest(comp: *const Compilation) Cache.Manifest } pub const CImportResult = struct { - out_zig_path: []u8, + digest: [Cache.bin_digest_len]u8, cache_hit: bool, errors: std.zig.ErrorBundle, @@ -4413,8 +4400,6 @@ pub const CImportResult = struct { }; /// Caller owns returned memory. -/// This API is currently coupled pretty tightly to stage1's needs; it will need to be reworked -/// a bit when we want to start using it from self-hosted. pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module) !CImportResult { dev.check(.translate_c_command); @@ -4503,7 +4488,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module error.OutOfMemory => return error.OutOfMemory, error.SemanticAnalyzeFail => { return CImportResult{ - .out_zig_path = "", + .digest = undefined, .cache_hit = actual_hit, .errors = errors, }; @@ -4528,8 +4513,9 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module .incremental => {}, } - const digest = man.final(); - const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); + const o_sub_path = "o" ++ std.fs.path.sep_str ++ hex_digest; var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); defer o_dir.close(); @@ -4541,8 +4527,8 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module try out_zig_file.writeAll(formatted); - break :digest digest; - } else man.final(); + break :digest bin_digest; + } else man.finalBin(); if (man.have_exclusive_lock) { // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is @@ -4554,14 +4540,8 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module }; } - const out_zig_path = try comp.local_cache_directory.join(comp.arena, &.{ - "o", &digest, cimport_zig_basename, - }); - if (comp.verbose_cimport) { - log.info("C import output: {s}", .{out_zig_path}); - } return CImportResult{ - .out_zig_path = out_zig_path, + .digest = digest, .cache_hit = actual_hit, .errors = std.zig.ErrorBundle.empty, }; @@ -4800,7 +4780,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr try argv.appendSlice(c_object.src.cache_exempt_flags); const out_obj_path = if (comp.bin_file) |lf| - try lf.emit.directory.join(arena, &.{lf.emit.sub_path}) + try lf.emit.root_dir.join(arena, &.{lf.emit.sub_path}) else "/dev/null"; diff --git a/src/Sema.zig b/src/Sema.zig index c8e2f2ae154c..b511fead3339 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -183,6 +183,7 @@ const InternPool = @import("InternPool.zig"); const Alignment = InternPool.Alignment; const AnalUnit = InternPool.AnalUnit; const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; +const Cache = std.Build.Cache; pub const default_branch_quota = 1000; pub const default_reference_trace_len = 2; @@ -5871,16 +5872,18 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr return sema.failWithOwnedErrorMsg(&child_block, msg); } const parent_mod = parent_block.ownerModule(); + const digest = Cache.binToHex(c_import_res.digest); + const c_import_zig_path = try comp.arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ digest); const c_import_mod = Package.Module.create(comp.arena, .{ .global_cache_directory = comp.global_cache_directory, .paths = .{ .root = .{ - .root_dir = Compilation.Directory.cwd(), - .sub_path = std.fs.path.dirname(c_import_res.out_zig_path) orelse "", + .root_dir = comp.local_cache_directory, + .sub_path = c_import_zig_path, }, - .root_src_path = std.fs.path.basename(c_import_res.out_zig_path), + .root_src_path = "cimport.zig", }, - .fully_qualified_name = c_import_res.out_zig_path, + .fully_qualified_name = c_import_zig_path, .cc_argv = parent_mod.cc_argv, .inherited = .{}, .global = comp.config, diff --git a/src/link.zig b/src/link.zig index b3e0aaa4a88a..894074cddaad 100644 --- a/src/link.zig +++ b/src/link.zig @@ -11,6 +11,7 @@ const wasi_libc = @import("wasi_libc.zig"); const Air = @import("Air.zig"); const Allocator = std.mem.Allocator; const Cache = std.Build.Cache; +const Path = Cache.Path; const Compilation = @import("Compilation.zig"); const LibCInstallation = std.zig.LibCInstallation; const Liveness = @import("Liveness.zig"); @@ -56,7 +57,7 @@ pub const File = struct { /// The owner of this output File. comp: *Compilation, - emit: Compilation.Emit, + emit: Path, file: ?fs.File, /// When linking with LLD, this linker code will output an object file only at @@ -189,7 +190,7 @@ pub const File = struct { pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: OpenOptions, ) !*File { switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { @@ -204,7 +205,7 @@ pub const File = struct { pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: OpenOptions, ) !*File { switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { @@ -243,8 +244,8 @@ pub const File = struct { emit.sub_path, std.crypto.random.int(u32), }); defer gpa.free(tmp_sub_path); - try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{}); - try emit.directory.handle.rename(tmp_sub_path, emit.sub_path); + try emit.root_dir.handle.copyFile(emit.sub_path, emit.root_dir.handle, tmp_sub_path, .{}); + try emit.root_dir.handle.rename(tmp_sub_path, emit.sub_path); switch (builtin.os.tag) { .linux => std.posix.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| { log.warn("ptrace failure: {s}", .{@errorName(err)}); @@ -260,7 +261,7 @@ pub const File = struct { const use_lld = build_options.have_llvm and comp.config.use_lld; const output_mode = comp.config.output_mode; const link_mode = comp.config.link_mode; - base.file = try emit.directory.handle.createFile(emit.sub_path, .{ + base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .truncate = false, .read = true, .mode = determineMode(use_lld, output_mode, link_mode), @@ -603,7 +604,7 @@ pub const File = struct { // Until then, we do `lld -r -o output.o input.o` even though the output is the same // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file // to the final location. See also the corresponding TODO in Coff linking. - const full_out_path = try emit.directory.join(gpa, &[_][]const u8{emit.sub_path}); + const full_out_path = try emit.root_dir.join(gpa, &[_][]const u8{emit.sub_path}); defer gpa.free(full_out_path); assert(comp.c_object_table.count() == 1); const the_key = comp.c_object_table.keys()[0]; @@ -751,7 +752,7 @@ pub const File = struct { const comp = base.comp; const gpa = comp.gpa; - const directory = base.emit.directory; // Just an alias to make it shorter to type. + const directory = base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); const full_out_path_z = try arena.dupeZ(u8, full_out_path); const opt_zcu = comp.module; @@ -1029,7 +1030,10 @@ pub const File = struct { llvm_object: LlvmObject.Ptr, prog_node: std.Progress.Node, ) !void { - return base.comp.emitLlvmObject(arena, base.emit, .{ + return base.comp.emitLlvmObject(arena, .{ + .root_dir = base.emit.root_dir, + .sub_path = std.fs.path.dirname(base.emit.sub_path) orelse "", + }, .{ .directory = null, .basename = base.zcu_object_sub_path.?, }, llvm_object, prog_node); diff --git a/src/link/C.zig b/src/link/C.zig index e7c8f6a7b009..a70417586531 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -3,6 +3,7 @@ const mem = std.mem; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const fs = std.fs; +const Path = std.Build.Cache.Path; const C = @This(); const build_options = @import("build_options"); @@ -104,7 +105,7 @@ pub fn addString(this: *C, s: []const u8) Allocator.Error!String { pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*C { return createEmpty(arena, comp, emit, options); @@ -113,7 +114,7 @@ pub fn open( pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*C { const target = comp.root_mod.resolved_target.result; @@ -127,7 +128,7 @@ pub fn createEmpty( assert(!use_lld); assert(!use_llvm); - const file = try emit.directory.handle.createFile(emit.sub_path, .{ + const file = try emit.root_dir.handle.createFile(emit.sub_path, .{ // Truncation is done on `flush`. .truncate = false, }); diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 7bdcc8d41144..f2359d503fde 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -219,7 +219,7 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size); pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Coff { const target = comp.root_mod.resolved_target.result; @@ -315,7 +315,7 @@ pub fn createEmpty( // If using LLD to link, this code should produce an object file so that it // can be passed to LLD. const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; - self.base.file = try emit.directory.handle.createFile(sub_path, .{ + self.base.file = try emit.root_dir.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.File.determineMode(use_lld, output_mode, link_mode), @@ -416,7 +416,7 @@ pub fn createEmpty( pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Coff { // TODO: restore saved linker state, don't truncate the file, and @@ -2714,6 +2714,7 @@ const math = std.math; const mem = std.mem; const Allocator = std.mem.Allocator; +const Path = std.Build.Cache.Path; const codegen = @import("../codegen.zig"); const link = @import("../link.zig"); diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index ae56183e77ab..7273aa39b617 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -27,7 +27,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no const comp = self.base.comp; const gpa = comp.gpa; - const directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it @@ -248,7 +248,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); if (comp.implib_emit) |emit| { - const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path}); + const implib_out_path = try emit.root_dir.join(arena, &[_][]const u8{emit.sub_path}); try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path})); } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 5a35e67b02e6..4afc59200284 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -204,7 +204,7 @@ pub const SortSection = enum { name, alignment }; pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Elf { const target = comp.root_mod.resolved_target.result; @@ -321,7 +321,7 @@ pub fn createEmpty( // If using LLD to link, this code should produce an object file so that it // can be passed to LLD. const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; - self.base.file = try emit.directory.handle.createFile(sub_path, .{ + self.base.file = try emit.root_dir.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.File.determineMode(use_lld, output_mode, link_mode), @@ -401,7 +401,7 @@ pub fn createEmpty( pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Elf { // TODO: restore saved linker state, don't truncate the file, and @@ -999,7 +999,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod const target = comp.root_mod.resolved_target.result; const link_mode = comp.config.link_mode; - const directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { @@ -1356,7 +1356,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { const target = self.base.comp.root_mod.resolved_target.result; const link_mode = self.base.comp.config.link_mode; - const directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { @@ -2054,7 +2054,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s const comp = self.base.comp; const gpa = comp.gpa; - const directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it @@ -6016,6 +6016,7 @@ const Allocator = std.mem.Allocator; const Archive = @import("Elf/Archive.zig"); pub const Atom = @import("Elf/Atom.zig"); const Cache = std.Build.Cache; +const Path = Cache.Path; const Compilation = @import("../Compilation.zig"); const ComdatGroupSection = synthetic_sections.ComdatGroupSection; const CopyRelSection = synthetic_sections.CopyRelSection; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index f3a6ffc58de3..65f31ae5dcc1 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -156,7 +156,7 @@ pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void { pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*MachO { const target = comp.root_mod.resolved_target.result; @@ -221,7 +221,7 @@ pub fn createEmpty( } errdefer self.base.destroy(); - self.base.file = try emit.directory.handle.createFile(emit.sub_path, .{ + self.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .truncate = true, .read = true, .mode = link.File.determineMode(false, output_mode, link_mode), @@ -260,7 +260,7 @@ pub fn createEmpty( pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*MachO { // TODO: restore saved linker state, don't truncate the file, and @@ -353,7 +353,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n const sub_prog_node = prog_node.start("MachO Flush", 0); defer sub_prog_node.end(); - const directory = self.base.emit.directory; + const directory = self.base.emit.root_dir; const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { @@ -586,7 +586,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n if (codesig) |*csig| { try self.writeCodeSignature(csig); // code signing always comes last const emit = self.base.emit; - try invalidateKernelCache(emit.directory.handle, emit.sub_path); + try invalidateKernelCache(emit.root_dir.handle, emit.sub_path); } } @@ -597,7 +597,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const directory = self.base.emit.directory; + const directory = self.base.emit.root_dir; const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { @@ -3199,7 +3199,7 @@ fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64 } const InitMetadataOptions = struct { - emit: Compilation.Emit, + emit: Path, zo: *ZigObject, symbol_count_hint: u64, program_code_size_hint: u64, @@ -3271,7 +3271,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { ); defer gpa.free(d_sym_path); - var d_sym_bundle = try options.emit.directory.handle.makeOpenPath(d_sym_path, .{}); + var d_sym_bundle = try options.emit.root_dir.handle.makeOpenPath(d_sym_path, .{}); defer d_sym_bundle.close(); const d_sym_file = try d_sym_bundle.createFile(options.emit.sub_path, .{ @@ -4603,6 +4603,7 @@ pub const Atom = @import("MachO/Atom.zig"); const AtomicBool = std.atomic.Value(bool); const Bind = bind.Bind; const Cache = std.Build.Cache; +const Path = Cache.Path; const CodeSignature = @import("MachO/CodeSignature.zig"); const Compilation = @import("../Compilation.zig"); const DataInCode = synthetic.DataInCode; diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index 74d0c58cbd28..a891bedbd6a2 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -53,7 +53,7 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) !u32 if (macho_file.base.isDynLib()) { const emit = macho_file.base.emit; const install_name = macho_file.install_name orelse - try emit.directory.join(gpa, &.{emit.sub_path}); + try emit.root_dir.join(gpa, &.{emit.sub_path}); defer if (macho_file.install_name == null) gpa.free(install_name); sizeofcmds += calcInstallNameLen( @sizeOf(macho.dylib_command), @@ -237,7 +237,7 @@ pub fn writeDylibIdLC(macho_file: *MachO, writer: anytype) !void { assert(comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); const emit = macho_file.base.emit; const install_name = macho_file.install_name orelse - try emit.directory.join(gpa, &.{emit.sub_path}); + try emit.root_dir.join(gpa, &.{emit.sub_path}); defer if (macho_file.install_name == null) gpa.free(install_name); const curr = comp.version orelse std.SemanticVersion{ .major = 1, diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig index cb95779d8e96..1488f65ff957 100644 --- a/src/link/NvPtx.zig +++ b/src/link/NvPtx.zig @@ -11,6 +11,7 @@ const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const log = std.log.scoped(.link); +const Path = std.Build.Cache.Path; const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); @@ -28,7 +29,7 @@ llvm_object: LlvmObject.Ptr, pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*NvPtx { const target = comp.root_mod.resolved_target.result; @@ -70,7 +71,7 @@ pub fn createEmpty( pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*NvPtx { const target = comp.root_mod.resolved_target.result; diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index d22c3b447c4d..eeba9ad61ba1 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -23,6 +23,7 @@ const mem = std.mem; const Allocator = std.mem.Allocator; const log = std.log.scoped(.link); const assert = std.debug.assert; +const Path = std.Build.Cache.Path; base: link.File, sixtyfour_bit: bool, @@ -275,7 +276,7 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Plan9 { const target = comp.root_mod.resolved_target.result; @@ -1199,7 +1200,7 @@ pub fn deinit(self: *Plan9) void { pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Plan9 { const target = comp.root_mod.resolved_target.result; @@ -1213,7 +1214,7 @@ pub fn open( const self = try createEmpty(arena, comp, emit, options); errdefer self.base.destroy(); - const file = try emit.directory.handle.createFile(emit.sub_path, .{ + const file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .read = true, .mode = link.File.determineMode( use_lld, diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index f76ceec2f5ff..9964c09df07b 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -26,6 +26,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const log = std.log.scoped(.link); +const Path = std.Build.Cache.Path; const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); @@ -54,7 +55,7 @@ object: codegen.Object, pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*SpirV { const gpa = comp.gpa; @@ -95,7 +96,7 @@ pub fn createEmpty( pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*SpirV { const target = comp.root_mod.resolved_target.result; @@ -110,7 +111,7 @@ pub fn open( errdefer spirv.base.destroy(); // TODO: read the file and keep valid parts instead of truncating - const file = try emit.directory.handle.createFile(emit.sub_path, .{ + const file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .truncate = true, .read = true, }); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 87dd8c13f957..2c5fc3fe7a28 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -22,6 +22,7 @@ const Air = @import("../Air.zig"); const Allocator = std.mem.Allocator; const Archive = @import("Wasm/Archive.zig"); const Cache = std.Build.Cache; +const Path = Cache.Path; const CodeGen = @import("../arch/wasm/CodeGen.zig"); const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); @@ -346,7 +347,7 @@ pub const StringTable = struct { pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Wasm { // TODO: restore saved linker state, don't truncate the file, and @@ -357,7 +358,7 @@ pub fn open( pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Wasm { const gpa = comp.gpa; @@ -430,7 +431,7 @@ pub fn createEmpty( // can be passed to LLD. const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; - wasm.base.file = try emit.directory.handle.createFile(sub_path, .{ + wasm.base.file = try emit.root_dir.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = if (fs.has_executable_bit) @@ -2496,7 +2497,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no const sub_prog_node = prog_node.start("Wasm Flush", 0); defer sub_prog_node.end(); - const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type. + const directory = wasm.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (wasm.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { @@ -3346,7 +3347,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: const gpa = comp.gpa; - const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type. + const directory = wasm.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it diff --git a/src/main.zig b/src/main.zig index 3429500bf815..1267baf9a913 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3519,7 +3519,7 @@ fn buildOutputType( if (test_exec_args.items.len == 0 and target.ofmt == .c) default_exec_args: { // Default to using `zig run` to execute the produced .c code from `zig test`. const c_code_loc = emit_bin_loc orelse break :default_exec_args; - const c_code_directory = c_code_loc.directory orelse comp.bin_file.?.emit.directory; + const c_code_directory = c_code_loc.directory orelse comp.bin_file.?.emit.root_dir; const c_code_path = try fs.path.join(arena, &[_][]const u8{ c_code_directory.path orelse ".", c_code_loc.basename, }); @@ -4142,7 +4142,7 @@ fn serve( if (output.errors.errorMessageCount() != 0) { try server.serveErrorBundle(output.errors); } else { - try server.serveEmitBinPath(output.out_zig_path, .{ + try server.serveEmitDigest(&output.digest, .{ .flags = .{ .cache_hit = output.cache_hit }, }); } @@ -4229,62 +4229,10 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void { return; } - // This logic is counter-intuitive because the protocol accounts for each - // emitted artifact possibly being in a different location, which correctly - // matches the behavior of the compiler, however, the build system - // currently always passes flags that makes all build artifacts output to - // the same local cache directory, and relies on them all being in the same - // directory. - // - // So, until the build system and protocol are changed to reflect this, - // this logic must ensure that emit_bin_path is emitted for at least one - // thing, if there are any artifacts. - - switch (comp.cache_use) { - .incremental => if (comp.bin_file) |lf| { - const full_path = try lf.emit.directory.join(gpa, &.{lf.emit.sub_path}); - defer gpa.free(full_path); - try s.serveEmitBinPath(full_path, .{ - .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, - }); - return; - }, - .whole => |whole| if (whole.bin_sub_path) |sub_path| { - const full_path = try comp.local_cache_directory.join(gpa, &.{sub_path}); - defer gpa.free(full_path); - try s.serveEmitBinPath(full_path, .{ - .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, - }); - return; - }, - } - - for ([_]?Compilation.Emit{ - comp.docs_emit, - comp.implib_emit, - }) |opt_emit| { - const emit = opt_emit orelse continue; - const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); - defer gpa.free(full_path); - try s.serveEmitBinPath(full_path, .{ + if (comp.digest) |digest| { + try s.serveEmitDigest(&digest, .{ .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, }); - return; - } - - for ([_]?Compilation.EmitLoc{ - comp.emit_asm, - comp.emit_llvm_ir, - comp.emit_llvm_bc, - }) |opt_emit_loc| { - const emit_loc = opt_emit_loc orelse continue; - const directory = emit_loc.directory orelse continue; - const full_path = try directory.join(gpa, &.{emit_loc.basename}); - defer gpa.free(full_path); - try s.serveEmitBinPath(full_path, .{ - .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, - }); - return; } // Serve empty error bundle to indicate the update is done. @@ -4308,7 +4256,7 @@ fn runOrTest( // A naive `directory.join` here will indeed get the correct path to the binary, // however, in the case of cwd, we actually want `./foo` so that the path can be executed. const exe_path = try fs.path.join(arena, &[_][]const u8{ - lf.emit.directory.path orelse ".", lf.emit.sub_path, + lf.emit.root_dir.path orelse ".", lf.emit.sub_path, }); var argv = std.ArrayList([]const u8).init(gpa); @@ -4420,7 +4368,7 @@ fn runOrTestHotSwap( // tmp zig-cache and use it to spawn the child process. This way we are free to update // the binary with each requested hot update. .windows => blk: { - try lf.emit.directory.handle.copyFile(lf.emit.sub_path, comp.local_cache_directory.handle, lf.emit.sub_path, .{}); + try lf.emit.root_dir.handle.copyFile(lf.emit.sub_path, comp.local_cache_directory.handle, lf.emit.sub_path, .{}); break :blk try fs.path.join(gpa, &[_][]const u8{ comp.local_cache_directory.path orelse ".", lf.emit.sub_path, }); @@ -4429,7 +4377,7 @@ fn runOrTestHotSwap( // A naive `directory.join` here will indeed get the correct path to the binary, // however, in the case of cwd, we actually want `./foo` so that the path can be executed. else => try fs.path.join(gpa, &[_][]const u8{ - lf.emit.directory.path orelse ".", lf.emit.sub_path, + lf.emit.root_dir.path orelse ".", lf.emit.sub_path, }), }; defer gpa.free(exe_path); @@ -4539,9 +4487,11 @@ fn cmdTranslateC( }; if (fancy_output) |p| p.cache_hit = true; - const digest = if (try man.hit()) digest: { + const bin_digest, const hex_digest = if (try man.hit()) digest: { if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf); - break :digest man.final(); + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); + break :digest .{ bin_digest, hex_digest }; } else digest: { if (fancy_output) |p| p.cache_hit = false; var argv = std.ArrayList([]const u8).init(arena); @@ -4639,8 +4589,10 @@ fn cmdTranslateC( }; } - const digest = man.final(); - const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest }); + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); + + const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &hex_digest }); var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); defer o_dir.close(); @@ -4656,16 +4608,14 @@ fn cmdTranslateC( if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf); - break :digest digest; + break :digest .{ bin_digest, hex_digest }; }; if (fancy_output) |p| { - p.out_zig_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ - "o", &digest, translated_zig_basename, - }); + p.digest = bin_digest; p.errors = std.zig.ErrorBundle.empty; } else { - const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); + const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &hex_digest, translated_zig_basename }); const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| { const path = comp.local_cache_directory.path orelse "."; fatal("unable to open cached translated zig file '{s}{s}{s}': {s}", .{ path, fs.path.sep_str, out_zig_path, @errorName(err) }); diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon index 8e4d727642ec..30ec07823b75 100644 --- a/test/standalone/build.zig.zon +++ b/test/standalone/build.zig.zon @@ -51,14 +51,15 @@ .install_raw_hex = .{ .path = "install_raw_hex", }, - // https://github.com/ziglang/zig/issues/17484 - //.emit_asm_and_bin = .{ - // .path = "emit_asm_and_bin", - //}, - // https://github.com/ziglang/zig/issues/17484 - //.issue_12588 = .{ - // .path = "issue_12588", - //}, + .emit_asm_and_bin = .{ + .path = "emit_asm_and_bin", + }, + .emit_llvm_no_bin = .{ + .path = "emit_llvm_no_bin", + }, + .emit_asm_no_bin = .{ + .path = "emit_asm_no_bin", + }, .child_process = .{ .path = "child_process", }, diff --git a/test/standalone/emit_asm_no_bin/build.zig b/test/standalone/emit_asm_no_bin/build.zig new file mode 100644 index 000000000000..f2f2391bc179 --- /dev/null +++ b/test/standalone/emit_asm_no_bin/build.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + + const obj = b.addObject(.{ + .name = "main", + .root_source_file = b.path("main.zig"), + .optimize = optimize, + .target = b.graph.host, + }); + _ = obj.getEmittedAsm(); + b.default_step.dependOn(&obj.step); + + test_step.dependOn(&obj.step); +} diff --git a/test/standalone/emit_asm_no_bin/main.zig b/test/standalone/emit_asm_no_bin/main.zig new file mode 100644 index 000000000000..902b554db075 --- /dev/null +++ b/test/standalone/emit_asm_no_bin/main.zig @@ -0,0 +1 @@ +pub fn main() void {} diff --git a/test/standalone/issue_12588/build.zig b/test/standalone/emit_llvm_no_bin/build.zig similarity index 100% rename from test/standalone/issue_12588/build.zig rename to test/standalone/emit_llvm_no_bin/build.zig diff --git a/test/standalone/issue_12588/main.zig b/test/standalone/emit_llvm_no_bin/main.zig similarity index 100% rename from test/standalone/issue_12588/main.zig rename to test/standalone/emit_llvm_no_bin/main.zig diff --git a/tools/incr-check.zig b/tools/incr-check.zig index 0386f1a12daa..a5c0afc62a06 100644 --- a/tools/incr-check.zig +++ b/tools/incr-check.zig @@ -1,6 +1,7 @@ const std = @import("std"); const fatal = std.process.fatal; const Allocator = std.mem.Allocator; +const Cache = std.Build.Cache; const usage = "usage: incr-check [--zig-lib-dir lib] [--debug-zcu] [--emit none|bin|c] [--zig-cc-binary /path/to/zig]"; @@ -233,30 +234,52 @@ const Eval = struct { fatal("error_bundle included unexpected stderr:\n{s}", .{stderr_data}); } } - if (result_error_bundle.errorMessageCount() == 0) { - // Empty bundle indicates successful update in a `-fno-emit-bin` build. - try eval.checkSuccessOutcome(update, null, prog_node); - } else { + if (result_error_bundle.errorMessageCount() != 0) { try eval.checkErrorOutcome(update, result_error_bundle); } // This message indicates the end of the update. stdout.discard(body.len); return; }, - .emit_bin_path => { - const EbpHdr = std.zig.Server.Message.EmitBinPath; + .emit_digest => { + const EbpHdr = std.zig.Server.Message.EmitDigest; const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); _ = ebp_hdr; - const result_binary = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); if (stderr.readableLength() > 0) { const stderr_data = try stderr.toOwnedSlice(); if (eval.allow_stderr) { - std.log.info("emit_bin_path included stderr:\n{s}", .{stderr_data}); + std.log.info("emit_digest included stderr:\n{s}", .{stderr_data}); } else { - fatal("emit_bin_path included unexpected stderr:\n{s}", .{stderr_data}); + fatal("emit_digest included unexpected stderr:\n{s}", .{stderr_data}); } } - try eval.checkSuccessOutcome(update, result_binary, prog_node); + + if (eval.emit == .none) { + try eval.checkSuccessOutcome(update, null, prog_node); + // This message indicates the end of the update. + stdout.discard(body.len); + return; + } + + const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; + const result_dir = ".local-cache" ++ std.fs.path.sep_str ++ "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*); + + const name = std.fs.path.stem(std.fs.path.basename(eval.case.root_source_file)); + const bin_name = try std.zig.binNameAlloc(arena, .{ + .root_name = name, + .target = try std.zig.system.resolveTargetQuery(try std.Build.parseTargetQuery(.{ + .arch_os_abi = eval.case.target_query, + .object_format = switch (eval.emit) { + .none => unreachable, + .bin => null, + .c => "c", + }, + })), + .output_mode = .Exe, + }); + const bin_path = try std.fs.path.join(arena, &.{ result_dir, bin_name }); + + try eval.checkSuccessOutcome(update, bin_path, prog_node); // This message indicates the end of the update. stdout.discard(body.len); return;