Commit b4343074d2

Robin Voetter <robin@voetter.nl>
2024-08-18 14:35:03
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.
1 parent 43f73af
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}));
         }
 
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,
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,
     });
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");
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;
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;
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;
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,
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,
     });
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
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";
 
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,
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);