aboutsummaryrefslogtreecommitdiff
path: root/src/link/Elf.zig
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2025-06-06 20:16:26 +0100
committermlugg <mlugg@mlugg.co.uk>2025-06-12 13:55:40 +0100
commitb5f73f8a7b90c5144b79692f142b5d91025dbe01 (patch)
tree3228d7f1afc8e7c48c2f686ef9643de8688f8370 /src/link/Elf.zig
parent808c15dd397f995d9bdf43664ee5644b39c9c863 (diff)
downloadzig-b5f73f8a7b90c5144b79692f142b5d91025dbe01.tar.gz
zig-b5f73f8a7b90c5144b79692f142b5d91025dbe01.zip
compiler: rework emit paths and cache modes
Previously, various doc comments heavily disagreed with the implementation on both what lives where on the filesystem at what time, and how that was represented in code. Notably, the combination of emit paths outside the cache and `disable_lld_caching` created a kind of ad-hoc "cache disable" mechanism -- which didn't actually *work* very well, 'most everything still ended up in this cache. There was also a long-standing issue where building using the LLVM backend would put a random object file in your cwd. This commit reworks how emit paths are specified in `Compilation.CreateOptions`, how they are represented internally, and how the cache usage is specified. There are now 3 options for `Compilation.CacheMode`: * `.none`: do not use the cache. The paths we have to emit to are relative to the compiler cwd (they're either user-specified, or defaults inferred from the root name). If we create any temporary files (e.g. the ZCU object when using the LLVM backend) they are emitted to a directory in `local_cache/tmp/`, which is deleted once the update finishes. * `.whole`: cache the compilation based on all inputs, including file contents. All emit paths are computed by the compiler (and will be stored as relative to the local cache directory); it is a CLI error to specify an explicit emit path. Artifacts (including temporary files) are written to a directory under `local_cache/tmp/`, which is later renamed to an appropriate `local_cache/o/`. The caller (who is using `--listen`; e.g. the build system) learns the name of this directory, and can get the artifacts from it. * `.incremental`: similar to `.whole`, but Zig source file contents, and anything else which incremental compilation can handle changes for, is not included in the cache manifest. We don't need to do the dance where the output directory is initially in `tmp/`, because our digest is computed entirely from CLI inputs. To be clear, the difference between `CacheMode.whole` and `CacheMode.incremental` is unchanged. `CacheMode.none` is new (previously it was sort of poorly imitated with `CacheMode.whole`). The defined behavior for temporary/intermediate files is new. `.none` is used for direct CLI invocations like `zig build-exe foo.zig`. The other cache modes are reserved for `--listen`, and the cache mode in use is currently just based on the presence of the `-fincremental` flag. There are two cases in which `CacheMode.whole` is used despite there being no `--listen` flag: `zig test` and `zig run`. Unless an explicit `-femit-bin=xxx` argument is passed on the CLI, these subcommands will use `CacheMode.whole`, so that they can put the output somewhere without polluting the cwd (plus, caching is potentially more useful for direct usage of these subcommands). Users of `--listen` (such as the build system) can now use `std.zig.EmitArtifact.cacheName` to find out what an output will be named. This avoids having to synchronize logic between the compiler and all users of `--listen`.
Diffstat (limited to 'src/link/Elf.zig')
-rw-r--r--src/link/Elf.zig23
1 files changed, 7 insertions, 16 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index 34e04ad557..498bc734c3 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -249,14 +249,6 @@ pub fn createEmpty(
const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic;
const default_sym_version: elf.Versym = if (is_dyn_lib or comp.config.rdynamic) .GLOBAL else .LOCAL;
- // If using LLVM to generate the object file for the zig compilation unit,
- // we need a place to put the object file so that it can be subsequently
- // handled.
- const zcu_object_sub_path = if (!use_llvm)
- null
- else
- try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
-
var rpath_table: std.StringArrayHashMapUnmanaged(void) = .empty;
try rpath_table.entries.resize(arena, options.rpath_list.len);
@memcpy(rpath_table.entries.items(.key), options.rpath_list);
@@ -268,7 +260,10 @@ pub fn createEmpty(
.tag = .elf,
.comp = comp,
.emit = emit,
- .zcu_object_sub_path = zcu_object_sub_path,
+ .zcu_object_basename = if (use_llvm)
+ try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
+ else
+ null,
.gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
.print_gc_sections = options.print_gc_sections,
.stack_size = options.stack_size orelse 16777216,
@@ -770,17 +765,13 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
const gpa = comp.gpa;
const diags = &comp.link_diags;
- const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
- .root_dir = self.base.emit.root_dir,
- .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname|
- try fs.path.join(arena, &.{ dirname, path })
- else
- path,
+ const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: {
+ break :p try comp.resolveEmitPathFlush(arena, .temp, raw);
} else null;
if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid);
- if (module_obj_path) |path| openParseObjectReportingFailure(self, path);
+ if (zcu_obj_path) |path| openParseObjectReportingFailure(self, path);
switch (comp.config.output_mode) {
.Obj => return relocatable.flushObject(self, comp),