authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-08-15 14:37:30+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-08-16 21:56:24+01:00
log0096c0806c0f939140ef61216d967b00e571018c
tree15d7b6b8669d537e67a544610680e597893843d3
parent4fcdb08390dc1496b74a3e652498f9a0b27fada5

Compilation: retain ZCU object when emitting unstripped Mach-O binary

On macOS, when using the LLVM backend, the output binary retains a reference to this object file's debug info (as opposed to self-hosted backends which instead emit a dSYM bundle). As such, we need to retain this object file in such cases. This object does unfortunately "leak", in that it won't be reused and will just sit in the cache forever (or until GC'd in the future). But that's no worse than the cache behavior prior to the rework that caused this, and it will become less of a problem over time as the self-hosted backend gains usability for debug builds and eventually becomes the default. Resolves: #24369

1 files changed, 15 insertions(+), 3 deletions(-)

src/Compilation.zig+15-3
...@@ -1549,7 +1549,8 @@ pub const SystemLib = link.SystemLib;...@@ -1549,7 +1549,8 @@ pub const SystemLib = link.SystemLib;
1549pub const CacheMode = enum {1549pub const CacheMode = enum {
1550 /// The results of this compilation are not cached. The compilation is always performed, and the1550 /// The results of this compilation are not cached. The compilation is always performed, and the
1551 /// results are emitted directly to their output locations. Temporary files will be placed in a1551 /// results are emitted directly to their output locations. Temporary files will be placed in a
1552 /// temporary directory in the cache, but deleted after the compilation is done.1552 /// temporary directory in the cache, but deleted after the compilation is done, unless they are
1553 /// needed for the output binary to work correctly.
1553 ///1554 ///
1554 /// This mode is typically used for direct CLI invocations like `zig build-exe`, because such1555 /// This mode is typically used for direct CLI invocations like `zig build-exe`, because such
1555 /// processes are typically low-level usages which would not make efficient use of the cache.1556 /// processes are typically low-level usages which would not make efficient use of the cache.
...@@ -1593,8 +1594,8 @@ const CacheUse = union(CacheMode) {...@@ -1593,8 +1594,8 @@ const CacheUse = union(CacheMode) {
1593 const None = struct {1594 const None = struct {
1594 /// User-requested artifacts are written directly to their output path in this cache mode.1595 /// User-requested artifacts are written directly to their output path in this cache mode.
1595 /// However, if we need to emit any temporary files, they are placed in this directory.1596 /// However, if we need to emit any temporary files, they are placed in this directory.
1596 /// We will recursively delete this directory at the end of this update. This field is1597 /// We will recursively delete this directory at the end of this update if possible. This
1597 /// non-`null` only inside `update`.1598 /// field is non-`null` only inside `update`.
1598 tmp_artifact_directory: ?Cache.Directory,1599 tmp_artifact_directory: ?Cache.Directory,
1599 };1600 };
16001601
...@@ -2807,6 +2808,17 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {...@@ -2807,6 +2808,17 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
2807 // temporary directories; it doesn't have a real cache directory anyway.2808 // temporary directories; it doesn't have a real cache directory anyway.
2808 return;2809 return;
2809 }2810 }
2811 // Usually, we want to delete the temporary directory. However, if we are emitting
2812 // an unstripped Mach-O binary with the LLVM backend, then the temporary directory
2813 // contains the ZCU object file emitted by LLVM, which contains debug symbols not
2814 // replicated in the output binary (the output instead contains a reference to that
2815 // file which debug tooling can look through). So, in that particular case, we need
2816 // to keep this directory around so that the output binary can be debugged.
2817 if (comp.bin_file != null and comp.getTarget().ofmt == .macho and comp.config.debug_format != .strip) {
2818 // We are emitting an unstripped Mach-O binary with the LLVM backend: the ZCU
2819 // object file must remain on-disk for its debug info.
2820 return;
2821 }
2810 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);2822 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2811 comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| {2823 comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| {
2812 log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{2824 log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{