From 43f73af3595c3174b8e67e9f2792c3774f2192e9 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 18 Aug 2024 00:43:33 +0200 Subject: [PATCH 1/4] fix various issues related to Path handling in the compiler and std A compilation build step for which the binary is not required could not be compiled previously. There were 2 issues that caused this: - The compiler communicated only the results of the emitted binary and did not properly communicate the result if the binary was not emitted. This is fixed by communicating the final hash of the artifact path (the hash of the corresponding /o/ directory) and communicating this instead of the entire path. This changes the zig build --listen protocol to communicate hashes instead of paths, and emit_bin_path is accordingly renamed to emit_digest. - There was an error related to the default llvm object path when CacheUse.Whole was selected. I'm not really sure why this didn't manifest when the binary is also emitted. This was fixed by improving the path handling related to flush() and emitLlvmObject(). In general, this commit also improves some of the path handling throughout the compiler and standard library. --- lib/compiler/objcopy.zig | 7 +- lib/std/Build.zig | 2 +- lib/std/Build/Cache.zig | 9 ++- lib/std/Build/Fuzz.zig | 18 +++-- lib/std/Build/Fuzz/WebServer.zig | 44 ++++++++---- lib/std/Build/Step.zig | 23 ++++--- lib/std/Build/Step/Compile.zig | 29 ++++---- lib/std/Build/Step/InstallArtifact.zig | 49 ++++++------- lib/std/Build/Step/Run.zig | 5 +- lib/std/Build/Step/TranslateC.zig | 12 ++-- lib/std/zig/Server.zig | 21 +++--- src/Compilation.zig | 95 +++++++++++++------------- src/Sema.zig | 11 +-- src/link.zig | 5 +- src/main.zig | 78 ++++----------------- tools/incr-check.zig | 43 +++++++++--- 16 files changed, 231 insertions(+), 220 deletions(-) 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..5a1c54aa9e8b 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"); @@ -269,6 +271,11 @@ llvm_opt_bisect_limit: c_int, file_system_inputs: ?*std.ArrayListUnmanaged(u8), +/// 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, + +/// TODO(robin): Remove because it is the same as Cache.Path pub const Emit = struct { /// Where the output will go. directory: Directory, @@ -868,8 +875,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 +1677,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(); @@ -2121,9 +2128,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(); @@ -2329,7 +2338,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 +2351,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,7 +2386,8 @@ 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 @@ -2393,9 +2404,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 +2422,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 +2454,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 +2474,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); } } } @@ -2745,7 +2755,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 +2766,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 +2782,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; } @@ -4403,7 +4413,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 +4423,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 +4511,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 +4536,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 +4550,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 +4563,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, }; 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..213b440c0619 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1029,7 +1029,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.directory, + .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/main.zig b/src/main.zig index 3429500bf815..1a080224ee93 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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. @@ -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/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; From b4343074d2d6ee81f8e589395a9c045031b86b83 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 18 Aug 2024 14:35:03 +0200 Subject: [PATCH 2/4] replace Compilation.Emit with std.Build.Cache.Path This type is exactly the same as std.Build.Cache.Path, except for one function which is not used anymore. Therefore we can replace it without consequences. --- src/Compilation.zig | 65 +++++++++++--------------------- src/link.zig | 19 +++++----- src/link/C.zig | 7 ++-- src/link/Coff.zig | 7 ++-- src/link/Coff/lld.zig | 4 +- src/link/Elf.zig | 13 ++++--- src/link/MachO.zig | 17 +++++---- src/link/MachO/load_commands.zig | 4 +- src/link/NvPtx.zig | 5 ++- src/link/Plan9.zig | 7 ++-- src/link/SpirV.zig | 7 ++-- src/link/Wasm.zig | 11 +++--- src/main.zig | 8 ++-- 13 files changed, 80 insertions(+), 94 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 5a1c54aa9e8b..48387e450137 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -72,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, @@ -275,29 +275,6 @@ file_system_inputs: ?*std.ArrayListUnmanaged(u8), /// This digest will be known after update() is called. digest: ?[Cache.bin_digest_len]u8 = null, -/// TODO(robin): Remove because it is the same as Cache.Path -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); - } - } -}; - pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; pub const SemaError = Zcu.SemaError; @@ -1695,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); @@ -1704,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, }; } @@ -2164,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); @@ -2394,7 +2371,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { // 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.?, }; @@ -2543,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, }; } @@ -2552,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, }; } @@ -3045,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(); @@ -4010,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(); @@ -4034,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(); @@ -4233,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(); @@ -4251,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), }); @@ -4803,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/link.zig b/src/link.zig index 213b440c0619..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; @@ -1030,7 +1031,7 @@ pub const File = struct { prog_node: std.Progress.Node, ) !void { return base.comp.emitLlvmObject(arena, .{ - .root_dir = base.emit.directory, + .root_dir = base.emit.root_dir, .sub_path = std.fs.path.dirname(base.emit.sub_path) orelse "", }, .{ .directory = null, 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 1a080224ee93..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, }); @@ -4256,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); @@ -4368,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, }); @@ -4377,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); From 294ca6563ea5efeec3ab30f27a06c05c2a657f80 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 18 Aug 2024 15:02:24 +0200 Subject: [PATCH 3/4] add standalone test for only dependending on the emitted assembly and not the bin --- test/standalone/build.zig.zon | 3 +++ test/standalone/emit_asm_no_bin/build.zig | 19 +++++++++++++++++++ test/standalone/emit_asm_no_bin/main.zig | 1 + 3 files changed, 23 insertions(+) create mode 100644 test/standalone/emit_asm_no_bin/build.zig create mode 100644 test/standalone/emit_asm_no_bin/main.zig diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon index 8e4d727642ec..f45aa43788fe 100644 --- a/test/standalone/build.zig.zon +++ b/test/standalone/build.zig.zon @@ -59,6 +59,9 @@ //.issue_12588 = .{ // .path = "issue_12588", //}, + .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 {} From 80999391d9b0db0303f59942cb52542a6e4da331 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 18 Aug 2024 15:05:52 +0200 Subject: [PATCH 4/4] re-enable emit_asm_and_bin and emit_llvm_no_bin tests These were fixed during the last few commits too. The emit_llvm_no_bin test is renamed from the issue_12588 test. Closes #17484 --- test/standalone/build.zig.zon | 16 +++++++--------- .../{issue_12588 => emit_llvm_no_bin}/build.zig | 0 .../{issue_12588 => emit_llvm_no_bin}/main.zig | 0 3 files changed, 7 insertions(+), 9 deletions(-) rename test/standalone/{issue_12588 => emit_llvm_no_bin}/build.zig (100%) rename test/standalone/{issue_12588 => emit_llvm_no_bin}/main.zig (100%) diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon index f45aa43788fe..30ec07823b75 100644 --- a/test/standalone/build.zig.zon +++ b/test/standalone/build.zig.zon @@ -51,16 +51,14 @@ .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", + .path = "emit_asm_no_bin", }, .child_process = .{ .path = "child_process", 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