authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-06-06 20:16:26+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-06-12 13:55:40+01:00
logb5f73f8a7b90c5144b79692f142b5d91025dbe01
tree3228d7f1afc8e7c48c2f686ef9643de8688f8370
parent808c15dd397f995d9bdf43664ee5644b39c9c863
signaturelock-open Commit is signed but in an unrecognized format.

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`.

21 files changed, 624 insertions(+), 841 deletions(-)

lib/std/Build/Step/Compile.zig+25-41
......@@ -1834,47 +1834,16 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
18341834 lp.path = b.fmt("{}", .{output_dir});
18351835 }
18361836
1837 // -femit-bin[=path] (default) Output machine code
1838 if (compile.generated_bin) |bin| {
1839 bin.path = output_dir.joinString(b.allocator, compile.out_filename) catch @panic("OOM");
1840 }
1841
1842 const sep = std.fs.path.sep_str;
1843
1844 // output PDB if someone requested it
1845 if (compile.generated_pdb) |pdb| {
1846 pdb.path = b.fmt("{}" ++ sep ++ "{s}.pdb", .{ output_dir, compile.name });
1847 }
1848
1849 // -femit-implib[=path] (default) Produce an import .lib when building a Windows DLL
1850 if (compile.generated_implib) |implib| {
1851 implib.path = b.fmt("{}" ++ sep ++ "{s}.lib", .{ output_dir, compile.name });
1852 }
1853
1854 // -femit-h[=path] Generate a C header file (.h)
1855 if (compile.generated_h) |lp| {
1856 lp.path = b.fmt("{}" ++ sep ++ "{s}.h", .{ output_dir, compile.name });
1857 }
1858
1859 // -femit-docs[=path] Create a docs/ dir with html documentation
1860 if (compile.generated_docs) |generated_docs| {
1861 generated_docs.path = output_dir.joinString(b.allocator, "docs") catch @panic("OOM");
1862 }
1863
1864 // -femit-asm[=path] Output .s (assembly code)
1865 if (compile.generated_asm) |lp| {
1866 lp.path = b.fmt("{}" ++ sep ++ "{s}.s", .{ output_dir, compile.name });
1867 }
1868
1869 // -femit-llvm-ir[=path] Produce a .ll file with optimized LLVM IR (requires LLVM extensions)
1870 if (compile.generated_llvm_ir) |lp| {
1871 lp.path = b.fmt("{}" ++ sep ++ "{s}.ll", .{ output_dir, compile.name });
1872 }
1873
1874 // -femit-llvm-bc[=path] Produce an optimized LLVM module as a .bc file (requires LLVM extensions)
1875 if (compile.generated_llvm_bc) |lp| {
1876 lp.path = b.fmt("{}" ++ sep ++ "{s}.bc", .{ output_dir, compile.name });
1877 }
1837 // zig fmt: off
1838 if (compile.generated_bin) |lp| lp.path = compile.outputPath(output_dir, .bin);
1839 if (compile.generated_pdb) |lp| lp.path = compile.outputPath(output_dir, .pdb);
1840 if (compile.generated_implib) |lp| lp.path = compile.outputPath(output_dir, .implib);
1841 if (compile.generated_h) |lp| lp.path = compile.outputPath(output_dir, .h);
1842 if (compile.generated_docs) |lp| lp.path = compile.outputPath(output_dir, .docs);
1843 if (compile.generated_asm) |lp| lp.path = compile.outputPath(output_dir, .@"asm");
1844 if (compile.generated_llvm_ir) |lp| lp.path = compile.outputPath(output_dir, .llvm_ir);
1845 if (compile.generated_llvm_bc) |lp| lp.path = compile.outputPath(output_dir, .llvm_bc);
1846 // zig fmt: on
18781847 }
18791848
18801849 if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic and
......@@ -1888,6 +1857,21 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
18881857 );
18891858 }
18901859}
1860fn outputPath(c: *Compile, out_dir: std.Build.Cache.Path, ea: std.zig.EmitArtifact) []const u8 {
1861 const arena = c.step.owner.graph.arena;
1862 const name = ea.cacheName(arena, .{
1863 .root_name = c.name,
1864 .target = c.root_module.resolved_target.?.result,
1865 .output_mode = switch (c.kind) {
1866 .lib => .Lib,
1867 .obj, .test_obj => .Obj,
1868 .exe, .@"test" => .Exe,
1869 },
1870 .link_mode = c.linkage,
1871 .version = c.version,
1872 }) catch @panic("OOM");
1873 return out_dir.joinString(arena, name) catch @panic("OOM");
1874}
18911875
18921876pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path {
18931877 const gpa = c.step.owner.allocator;
lib/std/zig.zig+29
......@@ -884,6 +884,35 @@ pub const SimpleComptimeReason = enum(u32) {
884884 }
885885};
886886
887/// Every kind of artifact which the compiler can emit.
888pub const EmitArtifact = enum {
889 bin,
890 @"asm",
891 implib,
892 llvm_ir,
893 llvm_bc,
894 docs,
895 pdb,
896 h,
897
898 /// If using `Server` to communicate with the compiler, it will place requested artifacts in
899 /// paths under the output directory, where those paths are named according to this function.
900 /// Returned string is allocated with `gpa` and owned by the caller.
901 pub fn cacheName(ea: EmitArtifact, gpa: Allocator, opts: BinNameOptions) Allocator.Error![]const u8 {
902 const suffix: []const u8 = switch (ea) {
903 .bin => return binNameAlloc(gpa, opts),
904 .@"asm" => ".s",
905 .implib => ".lib",
906 .llvm_ir => ".ll",
907 .llvm_bc => ".bc",
908 .docs => "-docs",
909 .pdb => ".pdb",
910 .h => ".h",
911 };
912 return std.fmt.allocPrint(gpa, "{s}{s}", .{ opts.root_name, suffix });
913 }
914};
915
887916test {
888917 _ = Ast;
889918 _ = AstRlAnnotate;
src/Compilation.zig+366-352
......@@ -55,8 +55,7 @@ gpa: Allocator,
5555arena: Allocator,
5656/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
5757zcu: ?*Zcu,
58/// Contains different state depending on whether the Compilation uses
59/// incremental or whole cache mode.
58/// Contains different state depending on the `CacheMode` used by this `Compilation`.
6059cache_use: CacheUse,
6160/// All compilations have a root module because this is where some important
6261/// settings are stored, such as target and optimization mode. This module
......@@ -67,17 +66,13 @@ root_mod: *Package.Module,
6766config: Config,
6867
6968/// The main output file.
70/// In whole cache mode, this is null except for during the body of the update
71/// function. In incremental cache mode, this is a long-lived object.
72/// In both cases, this is `null` when `-fno-emit-bin` is used.
69/// In `CacheMode.whole`, this is null except for during the body of `update`.
70/// In `CacheMode.none` and `CacheMode.incremental`, this is long-lived.
71/// Regardless of cache mode, this is `null` when `-fno-emit-bin` is used.
7372bin_file: ?*link.File,
7473
7574/// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin)
7675sysroot: ?[]const u8,
77/// This is `null` when not building a Windows DLL, or when `-fno-emit-implib` is used.
78implib_emit: ?Cache.Path,
79/// This is non-null when `-femit-docs` is provided.
80docs_emit: ?Cache.Path,
8176root_name: [:0]const u8,
8277compiler_rt_strat: RtStrat,
8378ubsan_rt_strat: RtStrat,
......@@ -259,10 +254,6 @@ mutex: if (builtin.single_threaded) struct {
259254test_filters: []const []const u8,
260255test_name_prefix: ?[]const u8,
261256
262emit_asm: ?EmitLoc,
263emit_llvm_ir: ?EmitLoc,
264emit_llvm_bc: ?EmitLoc,
265
266257link_task_wait_group: WaitGroup = .{},
267258work_queue_progress_node: std.Progress.Node = .none,
268259
......@@ -274,6 +265,31 @@ file_system_inputs: ?*std.ArrayListUnmanaged(u8),
274265/// This digest will be known after update() is called.
275266digest: ?[Cache.bin_digest_len]u8 = null,
276267
268/// Non-`null` iff we are emitting a binary.
269/// Does not change for the lifetime of this `Compilation`.
270/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
271emit_bin: ?[]const u8,
272/// Non-`null` iff we are emitting assembly.
273/// Does not change for the lifetime of this `Compilation`.
274/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
275emit_asm: ?[]const u8,
276/// Non-`null` iff we are emitting an implib.
277/// Does not change for the lifetime of this `Compilation`.
278/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
279emit_implib: ?[]const u8,
280/// Non-`null` iff we are emitting LLVM IR.
281/// Does not change for the lifetime of this `Compilation`.
282/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
283emit_llvm_ir: ?[]const u8,
284/// Non-`null` iff we are emitting LLVM bitcode.
285/// Does not change for the lifetime of this `Compilation`.
286/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
287emit_llvm_bc: ?[]const u8,
288/// Non-`null` iff we are emitting documentation.
289/// Does not change for the lifetime of this `Compilation`.
290/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
291emit_docs: ?[]const u8,
292
277293const QueuedJobs = struct {
278294 compiler_rt_lib: bool = false,
279295 compiler_rt_obj: bool = false,
......@@ -774,13 +790,6 @@ pub const CrtFile = struct {
774790 lock: Cache.Lock,
775791 full_object_path: Cache.Path,
776792
777 pub fn isObject(cf: CrtFile) bool {
778 return switch (classifyFileExt(cf.full_object_path.sub_path)) {
779 .object => true,
780 else => false,
781 };
782 }
783
784793 pub fn deinit(self: *CrtFile, gpa: Allocator) void {
785794 self.lock.release();
786795 gpa.free(self.full_object_path.sub_path);
......@@ -1321,14 +1330,6 @@ pub const MiscError = struct {
13211330 }
13221331};
13231332
1324pub const EmitLoc = struct {
1325 /// If this is `null` it means the file will be output to the cache directory.
1326 /// When provided, both the open file handle and the path name must outlive the `Compilation`.
1327 directory: ?Cache.Directory,
1328 /// This may not have sub-directories in it.
1329 basename: []const u8,
1330};
1331
13321333pub const cache_helpers = struct {
13331334 pub fn addModule(hh: *Cache.HashHelper, mod: *const Package.Module) void {
13341335 addResolvedTarget(hh, mod.resolved_target);
......@@ -1368,15 +1369,6 @@ pub const cache_helpers = struct {
13681369 hh.add(resolved_target.is_explicit_dynamic_linker);
13691370 }
13701371
1371 pub fn addEmitLoc(hh: *Cache.HashHelper, emit_loc: EmitLoc) void {
1372 hh.addBytes(emit_loc.basename);
1373 }
1374
1375 pub fn addOptionalEmitLoc(hh: *Cache.HashHelper, optional_emit_loc: ?EmitLoc) void {
1376 hh.add(optional_emit_loc != null);
1377 addEmitLoc(hh, optional_emit_loc orelse return);
1378 }
1379
13801372 pub fn addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?Config.DebugFormat) void {
13811373 hh.add(x != null);
13821374 addDebugFormat(hh, x orelse return);
......@@ -1423,7 +1415,38 @@ pub const ClangPreprocessorMode = enum {
14231415pub const Framework = link.File.MachO.Framework;
14241416pub const SystemLib = link.SystemLib;
14251417
1426pub const CacheMode = enum { incremental, whole };
1418pub const CacheMode = enum {
1419 /// The results of this compilation are not cached. The compilation is always performed, and the
1420 /// results are emitted directly to their output locations. Temporary files will be placed in a
1421 /// temporary directory in the cache, but deleted after the compilation is done.
1422 ///
1423 /// This mode is typically used for direct CLI invocations like `zig build-exe`, because such
1424 /// processes are typically low-level usages which would not make efficient use of the cache.
1425 none,
1426 /// The compilation is cached based only on the options given when creating the `Compilation`.
1427 /// In particular, Zig source file contents are not included in the cache manifest. This mode
1428 /// allows incremental compilation, because the old cached compilation state can be restored
1429 /// and the old binary patched up with the changes. All files, including temporary files, are
1430 /// stored in the cache directory like '<cache>/o/<hash>/'. Temporary files are not deleted.
1431 ///
1432 /// At the time of writing, incremental compilation is only supported with the `-fincremental`
1433 /// command line flag, so this mode is rarely used. However, it is required in order to use
1434 /// incremental compilation.
1435 incremental,
1436 /// The compilation is cached based on the `Compilation` options and every input, including Zig
1437 /// source files, linker inputs, and `@embedFile` targets. If any of them change, we will see a
1438 /// cache miss, and the entire compilation will be re-run. On a cache miss, we initially write
1439 /// all output files to a directory under '<cache>/tmp/', because we don't know the final
1440 /// manifest digest until the update is almost done. Once we can compute the final digest, this
1441 /// directory is moved to '<cache>/o/<hash>/'. Temporary files are not deleted.
1442 ///
1443 /// At the time of writing, this is the most commonly used cache mode: it is used by the build
1444 /// system (and any other parent using `--listen`) unless incremental compilation is enabled.
1445 /// Once incremental compilation is more mature, it will be replaced by `incremental` in many
1446 /// cases, but still has use cases, such as for release binaries, particularly globally cached
1447 /// artifacts like compiler_rt.
1448 whole,
1449};
14271450
14281451pub const ParentWholeCache = struct {
14291452 manifest: *Cache.Manifest,
......@@ -1432,22 +1455,33 @@ pub const ParentWholeCache = struct {
14321455};
14331456
14341457const CacheUse = union(CacheMode) {
1458 none: *None,
14351459 incremental: *Incremental,
14361460 whole: *Whole,
14371461
1462 const None = struct {
1463 /// User-requested artifacts are written directly to their output path in this cache mode.
1464 /// However, if we need to emit any temporary files, they are placed in this directory.
1465 /// We will recursively delete this directory at the end of this update. This field is
1466 /// non-`null` only inside `update`.
1467 tmp_artifact_directory: ?Cache.Directory,
1468 };
1469
1470 const Incremental = struct {
1471 /// All output files, including artifacts and incremental compilation metadata, are placed
1472 /// in this directory, which is some 'o/<hash>' in a cache directory.
1473 artifact_directory: Cache.Directory,
1474 };
1475
14381476 const Whole = struct {
1439 /// This is a pointer to a local variable inside `update()`.
1440 cache_manifest: ?*Cache.Manifest = null,
1441 cache_manifest_mutex: std.Thread.Mutex = .{},
1442 /// null means -fno-emit-bin.
1443 /// This is mutable memory allocated into the Compilation-lifetime arena (`arena`)
1444 /// of exactly the correct size for "o/[digest]/[basename]".
1445 /// The basename is of the outputted binary file in case we don't know the directory yet.
1446 bin_sub_path: ?[]u8,
1447 /// Same as `bin_sub_path` but for implibs.
1448 implib_sub_path: ?[]u8,
1449 docs_sub_path: ?[]u8,
1477 /// Since we don't open the output file until `update`, we must save these options for then.
14501478 lf_open_opts: link.File.OpenOptions,
1479 /// This is a pointer to a local variable inside `update`.
1480 cache_manifest: ?*Cache.Manifest,
1481 cache_manifest_mutex: std.Thread.Mutex,
1482 /// This is non-`null` for most of the body of `update`. It is the temporary directory which
1483 /// we initially emit our artifacts to. After the main part of the update is done, it will
1484 /// be closed and moved to its final location, and this field set to `null`.
14511485 tmp_artifact_directory: ?Cache.Directory,
14521486 /// Prevents other processes from clobbering files in the output directory.
14531487 lock: ?Cache.Lock,
......@@ -1466,17 +1500,16 @@ const CacheUse = union(CacheMode) {
14661500 }
14671501 };
14681502
1469 const Incremental = struct {
1470 /// Where build artifacts and incremental compilation metadata serialization go.
1471 artifact_directory: Cache.Directory,
1472 };
1473
14741503 fn deinit(cu: CacheUse) void {
14751504 switch (cu) {
1505 .none => |none| {
1506 assert(none.tmp_artifact_directory == null);
1507 },
14761508 .incremental => |incremental| {
14771509 incremental.artifact_directory.handle.close();
14781510 },
14791511 .whole => |whole| {
1512 assert(whole.tmp_artifact_directory == null);
14801513 whole.releaseLock();
14811514 },
14821515 }
......@@ -1503,28 +1536,14 @@ pub const CreateOptions = struct {
15031536 std_mod: ?*Package.Module = null,
15041537 root_name: []const u8,
15051538 sysroot: ?[]const u8 = null,
1506 /// `null` means to not emit a binary file.
1507 emit_bin: ?EmitLoc,
1508 /// `null` means to not emit a C header file.
1509 emit_h: ?EmitLoc = null,
1510 /// `null` means to not emit assembly.
1511 emit_asm: ?EmitLoc = null,
1512 /// `null` means to not emit LLVM IR.
1513 emit_llvm_ir: ?EmitLoc = null,
1514 /// `null` means to not emit LLVM module bitcode.
1515 emit_llvm_bc: ?EmitLoc = null,
1516 /// `null` means to not emit docs.
1517 emit_docs: ?EmitLoc = null,
1518 /// `null` means to not emit an import lib.
1519 emit_implib: ?EmitLoc = null,
1520 /// Normally when using LLD to link, Zig uses a file named "lld.id" in the
1521 /// same directory as the output binary which contains the hash of the link
1522 /// operation, allowing Zig to skip linking when the hash would be unchanged.
1523 /// In the case that the output binary is being emitted into a directory which
1524 /// is externally modified - essentially anything other than zig-cache - then
1525 /// this flag would be set to disable this machinery to avoid false positives.
1526 disable_lld_caching: bool = false,
1527 cache_mode: CacheMode = .incremental,
1539 cache_mode: CacheMode,
1540 emit_h: Emit = .no,
1541 emit_bin: Emit,
1542 emit_asm: Emit = .no,
1543 emit_implib: Emit = .no,
1544 emit_llvm_ir: Emit = .no,
1545 emit_llvm_bc: Emit = .no,
1546 emit_docs: Emit = .no,
15281547 /// This field is intended to be removed.
15291548 /// The ELF implementation no longer uses this data, however the MachO and COFF
15301549 /// implementations still do.
......@@ -1662,6 +1681,38 @@ pub const CreateOptions = struct {
16621681 parent_whole_cache: ?ParentWholeCache = null,
16631682
16641683 pub const Entry = link.File.OpenOptions.Entry;
1684
1685 /// Which fields are valid depends on the `cache_mode` given.
1686 pub const Emit = union(enum) {
1687 /// Do not emit this file. Always valid.
1688 no,
1689 /// Emit this file into its default name in the cache directory.
1690 /// Requires `cache_mode` to not be `.none`.
1691 yes_cache,
1692 /// Emit this file to the given path (absolute or cwd-relative).
1693 /// Requires `cache_mode` to be `.none`.
1694 yes_path: []const u8,
1695
1696 fn resolve(emit: Emit, arena: Allocator, opts: *const CreateOptions, ea: std.zig.EmitArtifact) Allocator.Error!?[]const u8 {
1697 switch (emit) {
1698 .no => return null,
1699 .yes_cache => {
1700 assert(opts.cache_mode != .none);
1701 return try ea.cacheName(arena, .{
1702 .root_name = opts.root_name,
1703 .target = opts.root_mod.resolved_target.result,
1704 .output_mode = opts.config.output_mode,
1705 .link_mode = opts.config.link_mode,
1706 .version = opts.version,
1707 });
1708 },
1709 .yes_path => |path| {
1710 assert(opts.cache_mode == .none);
1711 return try arena.dupe(u8, path);
1712 },
1713 }
1714 }
1715 };
16651716};
16661717
16671718fn addModuleTableToCacheHash(
......@@ -1869,13 +1920,18 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
18691920 cache.hash.add(options.config.link_libunwind);
18701921 cache.hash.add(output_mode);
18711922 cache_helpers.addDebugFormat(&cache.hash, options.config.debug_format);
1872 cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin);
1873 cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib);
1874 cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_docs);
18751923 cache.hash.addBytes(options.root_name);
18761924 cache.hash.add(options.config.wasi_exec_model);
18771925 cache.hash.add(options.config.san_cov_trace_pc_guard);
18781926 cache.hash.add(options.debug_compiler_runtime_libs);
1927 // The actual emit paths don't matter. They're only user-specified if we aren't using the
1928 // cache! However, it does matter whether the files are emitted at all.
1929 cache.hash.add(options.emit_bin != .no);
1930 cache.hash.add(options.emit_asm != .no);
1931 cache.hash.add(options.emit_implib != .no);
1932 cache.hash.add(options.emit_llvm_ir != .no);
1933 cache.hash.add(options.emit_llvm_bc != .no);
1934 cache.hash.add(options.emit_docs != .no);
18791935 // TODO audit this and make sure everything is in it
18801936
18811937 const main_mod = options.main_mod orelse options.root_mod;
......@@ -1925,7 +1981,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
19251981 try zcu.init(options.thread_pool.getIdCount());
19261982 break :blk zcu;
19271983 } else blk: {
1928 if (options.emit_h != null) return error.NoZigModuleForCHeader;
1984 if (options.emit_h != .no) return error.NoZigModuleForCHeader;
19291985 break :blk null;
19301986 };
19311987 errdefer if (opt_zcu) |zcu| zcu.deinit();
......@@ -1938,18 +1994,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
19381994 .arena = arena,
19391995 .zcu = opt_zcu,
19401996 .cache_use = undefined, // populated below
1941 .bin_file = null, // populated below
1942 .implib_emit = null, // handled below
1943 .docs_emit = null, // handled below
1997 .bin_file = null, // populated below if necessary
19441998 .root_mod = options.root_mod,
19451999 .config = options.config,
19462000 .dirs = options.dirs,
1947 .emit_asm = options.emit_asm,
1948 .emit_llvm_ir = options.emit_llvm_ir,
1949 .emit_llvm_bc = options.emit_llvm_bc,
19502001 .work_queues = @splat(.init(gpa)),
1951 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),
1952 .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{},
2002 .c_object_work_queue = .init(gpa),
2003 .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) .init(gpa) else .{},
19532004 .c_source_files = options.c_source_files,
19542005 .rc_source_files = options.rc_source_files,
19552006 .cache_parent = cache,
......@@ -2002,6 +2053,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
20022053 .file_system_inputs = options.file_system_inputs,
20032054 .parent_whole_cache = options.parent_whole_cache,
20042055 .link_diags = .init(gpa),
2056 .emit_bin = try options.emit_bin.resolve(arena, &options, .bin),
2057 .emit_asm = try options.emit_asm.resolve(arena, &options, .@"asm"),
2058 .emit_implib = try options.emit_implib.resolve(arena, &options, .implib),
2059 .emit_llvm_ir = try options.emit_llvm_ir.resolve(arena, &options, .llvm_ir),
2060 .emit_llvm_bc = try options.emit_llvm_bc.resolve(arena, &options, .llvm_bc),
2061 .emit_docs = try options.emit_docs.resolve(arena, &options, .docs),
20052062 };
20062063
20072064 // Prevent some footguns by making the "any" fields of config reflect
......@@ -2068,7 +2125,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
20682125 .soname = options.soname,
20692126 .compatibility_version = options.compatibility_version,
20702127 .build_id = build_id,
2071 .disable_lld_caching = options.disable_lld_caching or options.cache_mode == .whole,
20722128 .subsystem = options.subsystem,
20732129 .hash_style = options.hash_style,
20742130 .enable_link_snapshots = options.enable_link_snapshots,
......@@ -2087,6 +2143,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
20872143 };
20882144
20892145 switch (options.cache_mode) {
2146 .none => {
2147 const none = try arena.create(CacheUse.None);
2148 none.* = .{ .tmp_artifact_directory = null };
2149 comp.cache_use = .{ .none = none };
2150 if (comp.emit_bin) |path| {
2151 comp.bin_file = try link.File.open(arena, comp, .{
2152 .root_dir = .cwd(),
2153 .sub_path = path,
2154 }, lf_open_opts);
2155 }
2156 },
20902157 .incremental => {
20912158 // Options that are specific to zig source files, that cannot be
20922159 // modified between incremental updates.
......@@ -2100,7 +2167,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
21002167 hash.addListOfBytes(options.test_filters);
21012168 hash.addOptionalBytes(options.test_name_prefix);
21022169 hash.add(options.skip_linker_dependencies);
2103 hash.add(options.emit_h != null);
2170 hash.add(options.emit_h != .no);
21042171 hash.add(error_limit);
21052172
21062173 // Here we put the root source file path name, but *not* with addFile.
......@@ -2135,49 +2202,26 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
21352202 };
21362203 comp.cache_use = .{ .incremental = incremental };
21372204
2138 if (options.emit_bin) |emit_bin| {
2205 if (comp.emit_bin) |cache_rel_path| {
21392206 const emit: Cache.Path = .{
2140 .root_dir = emit_bin.directory orelse artifact_directory,
2141 .sub_path = emit_bin.basename,
2207 .root_dir = artifact_directory,
2208 .sub_path = cache_rel_path,
21422209 };
21432210 comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts);
21442211 }
2145
2146 if (options.emit_implib) |emit_implib| {
2147 comp.implib_emit = .{
2148 .root_dir = emit_implib.directory orelse artifact_directory,
2149 .sub_path = emit_implib.basename,
2150 };
2151 }
2152
2153 if (options.emit_docs) |emit_docs| {
2154 comp.docs_emit = .{
2155 .root_dir = emit_docs.directory orelse artifact_directory,
2156 .sub_path = emit_docs.basename,
2157 };
2158 }
21592212 },
21602213 .whole => {
2161 // For whole cache mode, we don't know where to put outputs from
2162 // the linker until the final cache hash, which is available after
2163 // the compilation is complete.
2214 // For whole cache mode, we don't know where to put outputs from the linker until
2215 // the final cache hash, which is available after the compilation is complete.
21642216 //
2165 // Therefore, bin_file is left null until the beginning of update(),
2166 // where it may find a cache hit, or use a temporary directory to
2167 // hold output artifacts.
2217 // Therefore, `comp.bin_file` is left `null` (already done) until `update`, where
2218 // it may find a cache hit, or else will use a temporary directory to hold output
2219 // artifacts.
21682220 const whole = try arena.create(CacheUse.Whole);
21692221 whole.* = .{
2170 // This is kept here so that link.File.open can be called later.
21712222 .lf_open_opts = lf_open_opts,
2172 // This is so that when doing `CacheMode.whole`, the mechanism in update()
2173 // can use it for communicating the result directory via `bin_file.emit`.
2174 // This is used to distinguish between -fno-emit-bin and -femit-bin
2175 // for `CacheMode.whole`.
2176 // This memory will be overwritten with the real digest in update() but
2177 // the basename will be preserved.
2178 .bin_sub_path = try prepareWholeEmitSubPath(arena, options.emit_bin),
2179 .implib_sub_path = try prepareWholeEmitSubPath(arena, options.emit_implib),
2180 .docs_sub_path = try prepareWholeEmitSubPath(arena, options.emit_docs),
2223 .cache_manifest = null,
2224 .cache_manifest_mutex = .{},
21812225 .tmp_artifact_directory = null,
21822226 .lock = null,
21832227 };
......@@ -2245,12 +2289,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
22452289 }
22462290 }
22472291
2248 const have_bin_emit = switch (comp.cache_use) {
2249 .whole => |whole| whole.bin_sub_path != null,
2250 .incremental => comp.bin_file != null,
2251 };
2252
2253 if (have_bin_emit and target.ofmt != .c) {
2292 if (comp.emit_bin != null and target.ofmt != .c) {
22542293 if (!comp.skip_linker_dependencies) {
22552294 // If we need to build libc for the target, add work items for it.
22562295 // We go through the work queue so that building can be done in parallel.
......@@ -2544,8 +2583,23 @@ pub fn hotCodeSwap(
25442583 try lf.makeExecutable();
25452584}
25462585
2547fn cleanupAfterUpdate(comp: *Compilation) void {
2586fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
25482587 switch (comp.cache_use) {
2588 .none => |none| {
2589 if (none.tmp_artifact_directory) |*tmp_dir| {
2590 tmp_dir.handle.close();
2591 none.tmp_artifact_directory = null;
2592 const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2593 comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| {
2594 log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{
2595 comp.dirs.local_cache.path orelse ".",
2596 std.fs.path.sep,
2597 tmp_dir_sub_path,
2598 @errorName(err),
2599 });
2600 };
2601 }
2602 },
25492603 .incremental => return,
25502604 .whole => |whole| {
25512605 if (whole.cache_manifest) |man| {
......@@ -2556,10 +2610,18 @@ fn cleanupAfterUpdate(comp: *Compilation) void {
25562610 lf.destroy();
25572611 comp.bin_file = null;
25582612 }
2559 if (whole.tmp_artifact_directory) |*directory| {
2560 directory.handle.close();
2561 if (directory.path) |p| comp.gpa.free(p);
2613 if (whole.tmp_artifact_directory) |*tmp_dir| {
2614 tmp_dir.handle.close();
25622615 whole.tmp_artifact_directory = null;
2616 const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2617 comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| {
2618 log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{
2619 comp.dirs.local_cache.path orelse ".",
2620 std.fs.path.sep,
2621 tmp_dir_sub_path,
2622 @errorName(err),
2623 });
2624 };
25632625 }
25642626 },
25652627 }
......@@ -2579,14 +2641,27 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
25792641 comp.clearMiscFailures();
25802642 comp.last_update_was_cache_hit = false;
25812643
2582 var man: Cache.Manifest = undefined;
2583 defer cleanupAfterUpdate(comp);
2584
25852644 var tmp_dir_rand_int: u64 = undefined;
2645 var man: Cache.Manifest = undefined;
2646 defer cleanupAfterUpdate(comp, tmp_dir_rand_int);
25862647
25872648 // If using the whole caching strategy, we check for *everything* up front, including
25882649 // C source files.
2650 log.debug("Compilation.update for {s}, CacheMode.{s}", .{ comp.root_name, @tagName(comp.cache_use) });
25892651 switch (comp.cache_use) {
2652 .none => |none| {
2653 assert(none.tmp_artifact_directory == null);
2654 none.tmp_artifact_directory = d: {
2655 tmp_dir_rand_int = std.crypto.random.int(u64);
2656 const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2657 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
2658 break :d .{
2659 .path = path,
2660 .handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}),
2661 };
2662 };
2663 },
2664 .incremental => {},
25902665 .whole => |whole| {
25912666 assert(comp.bin_file == null);
25922667 // We are about to obtain this lock, so here we give other processes a chance first.
......@@ -2633,10 +2708,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
26332708 comp.last_update_was_cache_hit = true;
26342709 log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name});
26352710 const bin_digest = man.finalBin();
2636 const hex_digest = Cache.binToHex(bin_digest);
26372711
26382712 comp.digest = bin_digest;
2639 comp.wholeCacheModeSetBinFilePath(whole, &hex_digest);
26402713
26412714 assert(whole.lock == null);
26422715 whole.lock = man.toOwnedLock();
......@@ -2645,52 +2718,23 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
26452718 log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name});
26462719
26472720 // Compile the artifacts to a temporary directory.
2648 const tmp_artifact_directory: Cache.Directory = d: {
2649 const s = std.fs.path.sep_str;
2721 whole.tmp_artifact_directory = d: {
26502722 tmp_dir_rand_int = std.crypto.random.int(u64);
2651 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int);
2652
2653 const path = try comp.dirs.local_cache.join(gpa, &.{tmp_dir_sub_path});
2654 errdefer gpa.free(path);
2655
2656 const handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{});
2657 errdefer handle.close();
2658
2723 const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2724 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
26592725 break :d .{
26602726 .path = path,
2661 .handle = handle,
2727 .handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}),
26622728 };
26632729 };
2664 whole.tmp_artifact_directory = tmp_artifact_directory;
2665
2666 // Now that the directory is known, it is time to create the Emit
2667 // objects and call link.File.open.
2668
2669 if (whole.implib_sub_path) |sub_path| {
2670 comp.implib_emit = .{
2671 .root_dir = tmp_artifact_directory,
2672 .sub_path = std.fs.path.basename(sub_path),
2673 };
2674 }
2675
2676 if (whole.docs_sub_path) |sub_path| {
2677 comp.docs_emit = .{
2678 .root_dir = tmp_artifact_directory,
2679 .sub_path = std.fs.path.basename(sub_path),
2680 };
2681 }
2682
2683 if (whole.bin_sub_path) |sub_path| {
2730 if (comp.emit_bin) |sub_path| {
26842731 const emit: Cache.Path = .{
2685 .root_dir = tmp_artifact_directory,
2686 .sub_path = std.fs.path.basename(sub_path),
2732 .root_dir = whole.tmp_artifact_directory.?,
2733 .sub_path = sub_path,
26872734 };
26882735 comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts);
26892736 }
26902737 },
2691 .incremental => {
2692 log.debug("Compilation.update for {s}, CacheMode.incremental", .{comp.root_name});
2693 },
26942738 }
26952739
26962740 // From this point we add a preliminary set of file system inputs that
......@@ -2789,11 +2833,18 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
27892833 return;
27902834 }
27912835
2792 // Flush below handles -femit-bin but there is still -femit-llvm-ir,
2793 // -femit-llvm-bc, and -femit-asm, in the case of C objects.
2794 comp.emitOthers();
2836 if (comp.zcu == null and comp.config.output_mode == .Obj and comp.c_object_table.count() == 1) {
2837 // This is `zig build-obj foo.c`. We can emit asm and LLVM IR/bitcode.
2838 const c_obj_path = comp.c_object_table.keys()[0].status.success.object_path;
2839 if (comp.emit_asm) |path| try comp.emitFromCObject(arena, c_obj_path, ".s", path);
2840 if (comp.emit_llvm_ir) |path| try comp.emitFromCObject(arena, c_obj_path, ".ll", path);
2841 if (comp.emit_llvm_bc) |path| try comp.emitFromCObject(arena, c_obj_path, ".bc", path);
2842 }
27952843
27962844 switch (comp.cache_use) {
2845 .none, .incremental => {
2846 try flush(comp, arena, .main, main_progress_node);
2847 },
27972848 .whole => |whole| {
27982849 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
27992850 if (comp.parent_whole_cache) |pwc| {
......@@ -2805,18 +2856,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
28052856 const bin_digest = man.finalBin();
28062857 const hex_digest = Cache.binToHex(bin_digest);
28072858
2808 // Rename the temporary directory into place.
2809 // Close tmp dir and link.File to avoid open handle during rename.
2810 if (whole.tmp_artifact_directory) |*tmp_directory| {
2811 tmp_directory.handle.close();
2812 if (tmp_directory.path) |p| gpa.free(p);
2813 whole.tmp_artifact_directory = null;
2814 } else unreachable;
2815
2816 const s = std.fs.path.sep_str;
2817 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int);
2818 const o_sub_path = "o" ++ s ++ hex_digest;
2819
28202859 // Work around windows `AccessDenied` if any files within this
28212860 // directory are open by closing and reopening the file handles.
28222861 const need_writable_dance: enum { no, lf_only, lf_and_debug } = w: {
......@@ -2841,6 +2880,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
28412880 break :w .no;
28422881 };
28432882
2883 // Rename the temporary directory into place.
2884 // Close tmp dir and link.File to avoid open handle during rename.
2885 whole.tmp_artifact_directory.?.handle.close();
2886 whole.tmp_artifact_directory = null;
2887 const s = std.fs.path.sep_str;
2888 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int);
2889 const o_sub_path = "o" ++ s ++ hex_digest;
28442890 renameTmpIntoCache(comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| {
28452891 return comp.setMiscFailure(
28462892 .rename_results,
......@@ -2853,7 +2899,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
28532899 );
28542900 };
28552901 comp.digest = bin_digest;
2856 comp.wholeCacheModeSetBinFilePath(whole, &hex_digest);
28572902
28582903 // The linker flush functions need to know the final output path
28592904 // for debug info purposes because executable debug info contains
......@@ -2861,10 +2906,9 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
28612906 if (comp.bin_file) |lf| {
28622907 lf.emit = .{
28632908 .root_dir = comp.dirs.local_cache,
2864 .sub_path = whole.bin_sub_path.?,
2909 .sub_path = try std.fs.path.join(arena, &.{ o_sub_path, comp.emit_bin.? }),
28652910 };
28662911
2867 // Has to be after the `wholeCacheModeSetBinFilePath` above.
28682912 switch (need_writable_dance) {
28692913 .no => {},
28702914 .lf_only => try lf.makeWritable(),
......@@ -2875,10 +2919,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
28752919 }
28762920 }
28772921
2878 try flush(comp, arena, .{
2879 .root_dir = comp.dirs.local_cache,
2880 .sub_path = o_sub_path,
2881 }, .main, main_progress_node);
2922 try flush(comp, arena, .main, main_progress_node);
28822923
28832924 // Calling `flush` may have produced errors, in which case the
28842925 // cache manifest must not be written.
......@@ -2897,11 +2938,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
28972938 assert(whole.lock == null);
28982939 whole.lock = man.toOwnedLock();
28992940 },
2900 .incremental => |incremental| {
2901 try flush(comp, arena, .{
2902 .root_dir = incremental.artifact_directory,
2903 }, .main, main_progress_node);
2904 },
29052941 }
29062942}
29072943
......@@ -2931,10 +2967,47 @@ pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocat
29312967 fsi.appendSliceAssumeCapacity(path.sub_path);
29322968}
29332969
2970fn resolveEmitPath(comp: *Compilation, path: []const u8) Cache.Path {
2971 return .{
2972 .root_dir = switch (comp.cache_use) {
2973 .none => .cwd(),
2974 .incremental => |i| i.artifact_directory,
2975 .whole => |w| w.tmp_artifact_directory.?,
2976 },
2977 .sub_path = path,
2978 };
2979}
2980/// Like `resolveEmitPath`, but for calling during `flush`. The returned `Cache.Path` may reference
2981/// memory from `arena`, and may reference `path` itself.
2982/// If `kind == .temp`, then the returned path will be in a temporary or cache directory. This is
2983/// useful for intermediate files, such as the ZCU object file emitted by the LLVM backend.
2984pub fn resolveEmitPathFlush(
2985 comp: *Compilation,
2986 arena: Allocator,
2987 kind: enum { temp, artifact },
2988 path: []const u8,
2989) Allocator.Error!Cache.Path {
2990 switch (comp.cache_use) {
2991 .none => |none| return .{
2992 .root_dir = switch (kind) {
2993 .temp => none.tmp_artifact_directory.?,
2994 .artifact => .cwd(),
2995 },
2996 .sub_path = path,
2997 },
2998 .incremental, .whole => return .{
2999 .root_dir = comp.dirs.local_cache,
3000 .sub_path = try fs.path.join(arena, &.{
3001 "o",
3002 &Cache.binToHex(comp.digest.?),
3003 path,
3004 }),
3005 },
3006 }
3007}
29343008fn flush(
29353009 comp: *Compilation,
29363010 arena: Allocator,
2937 default_artifact_directory: Cache.Path,
29383011 tid: Zcu.PerThread.Id,
29393012 prog_node: std.Progress.Node,
29403013) !void {
......@@ -2942,19 +3015,32 @@ fn flush(
29423015 if (zcu.llvm_object) |llvm_object| {
29433016 // Emit the ZCU object from LLVM now; it's required to flush the output file.
29443017 // If there's an output file, it wants to decide where the LLVM object goes!
2945 const zcu_obj_emit_loc: ?EmitLoc = if (comp.bin_file) |lf| .{
2946 .directory = null,
2947 .basename = lf.zcu_object_sub_path.?,
2948 } else null;
29493018 const sub_prog_node = prog_node.start("LLVM Emit Object", 0);
29503019 defer sub_prog_node.end();
29513020 try llvm_object.emit(.{
29523021 .pre_ir_path = comp.verbose_llvm_ir,
29533022 .pre_bc_path = comp.verbose_llvm_bc,
2954 .bin_path = try resolveEmitLoc(arena, default_artifact_directory, zcu_obj_emit_loc),
2955 .asm_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_asm),
2956 .post_ir_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_ir),
2957 .post_bc_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_bc),
3023
3024 .bin_path = p: {
3025 const lf = comp.bin_file orelse break :p null;
3026 const p = try comp.resolveEmitPathFlush(arena, .temp, lf.zcu_object_basename.?);
3027 break :p try p.toStringZ(arena);
3028 },
3029 .asm_path = p: {
3030 const raw = comp.emit_asm orelse break :p null;
3031 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
3032 break :p try p.toStringZ(arena);
3033 },
3034 .post_ir_path = p: {
3035 const raw = comp.emit_llvm_ir orelse break :p null;
3036 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
3037 break :p try p.toStringZ(arena);
3038 },
3039 .post_bc_path = p: {
3040 const raw = comp.emit_llvm_bc orelse break :p null;
3041 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
3042 break :p try p.toStringZ(arena);
3043 },
29583044
29593045 .is_debug = comp.root_mod.optimize_mode == .Debug,
29603046 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,
......@@ -3025,45 +3111,6 @@ fn renameTmpIntoCache(
30253111 }
30263112}
30273113
3028/// Communicate the output binary location to parent Compilations.
3029fn wholeCacheModeSetBinFilePath(
3030 comp: *Compilation,
3031 whole: *CacheUse.Whole,
3032 digest: *const [Cache.hex_digest_len]u8,
3033) void {
3034 const digest_start = 2; // "o/[digest]/[basename]"
3035
3036 if (whole.bin_sub_path) |sub_path| {
3037 @memcpy(sub_path[digest_start..][0..digest.len], digest);
3038 }
3039
3040 if (whole.implib_sub_path) |sub_path| {
3041 @memcpy(sub_path[digest_start..][0..digest.len], digest);
3042
3043 comp.implib_emit = .{
3044 .root_dir = comp.dirs.local_cache,
3045 .sub_path = sub_path,
3046 };
3047 }
3048
3049 if (whole.docs_sub_path) |sub_path| {
3050 @memcpy(sub_path[digest_start..][0..digest.len], digest);
3051
3052 comp.docs_emit = .{
3053 .root_dir = comp.dirs.local_cache,
3054 .sub_path = sub_path,
3055 };
3056 }
3057}
3058
3059fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemory}!?[]u8 {
3060 const emit = opt_emit orelse return null;
3061 if (emit.directory != null) return null;
3062 const s = std.fs.path.sep_str;
3063 const format = "o" ++ s ++ ("x" ** Cache.hex_digest_len) ++ s ++ "{s}";
3064 return try std.fmt.allocPrint(arena, format, .{emit.basename});
3065}
3066
30673114/// This is only observed at compile-time and used to emit a compile error
30683115/// to remind the programmer to update multiple related pieces of code that
30693116/// are in different locations. Bump this number when adding or deleting
......@@ -3084,7 +3131,7 @@ fn addNonIncrementalStuffToCacheManifest(
30843131 man.hash.addListOfBytes(comp.test_filters);
30853132 man.hash.addOptionalBytes(comp.test_name_prefix);
30863133 man.hash.add(comp.skip_linker_dependencies);
3087 //man.hash.add(zcu.emit_h != null);
3134 //man.hash.add(zcu.emit_h != .no);
30883135 man.hash.add(zcu.error_limit);
30893136 } else {
30903137 cache_helpers.addModule(&man.hash, comp.root_mod);
......@@ -3130,10 +3177,6 @@ fn addNonIncrementalStuffToCacheManifest(
31303177 man.hash.addListOfBytes(comp.framework_dirs);
31313178 man.hash.addListOfBytes(comp.windows_libs.keys());
31323179
3133 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
3134 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
3135 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);
3136
31373180 man.hash.addListOfBytes(comp.global_cc_argv);
31383181
31393182 const opts = comp.cache_use.whole.lf_open_opts;
......@@ -3211,54 +3254,39 @@ fn addNonIncrementalStuffToCacheManifest(
32113254 man.hash.addOptional(opts.minor_subsystem_version);
32123255}
32133256
3214fn emitOthers(comp: *Compilation) void {
3215 if (comp.config.output_mode != .Obj or comp.zcu != null or
3216 comp.c_object_table.count() == 0)
3217 {
3218 return;
3219 }
3220 const obj_path = comp.c_object_table.keys()[0].status.success.object_path;
3221 const ext = std.fs.path.extension(obj_path.sub_path);
3222 const dirname = obj_path.sub_path[0 .. obj_path.sub_path.len - ext.len];
3223 // This obj path always ends with the object file extension, but if we change the
3224 // extension to .ll, .bc, or .s, then it will be the path to those things.
3225 const outs = [_]struct {
3226 emit: ?EmitLoc,
3227 ext: []const u8,
3228 }{
3229 .{ .emit = comp.emit_asm, .ext = ".s" },
3230 .{ .emit = comp.emit_llvm_ir, .ext = ".ll" },
3231 .{ .emit = comp.emit_llvm_bc, .ext = ".bc" },
3257fn emitFromCObject(
3258 comp: *Compilation,
3259 arena: Allocator,
3260 c_obj_path: Cache.Path,
3261 new_ext: []const u8,
3262 unresolved_emit_path: []const u8,
3263) Allocator.Error!void {
3264 // The dirname and stem (i.e. everything but the extension), of the sub path of the C object.
3265 // We'll append `new_ext` to it to get the path to the right thing (asm, LLVM IR, etc).
3266 const c_obj_dir_and_stem: []const u8 = p: {
3267 const p = c_obj_path.sub_path;
3268 const ext_len = fs.path.extension(p).len;
3269 break :p p[0 .. p.len - ext_len];
32323270 };
3233 for (outs) |out| {
3234 if (out.emit) |loc| {
3235 if (loc.directory) |directory| {
3236 const src_path = std.fmt.allocPrint(comp.gpa, "{s}{s}", .{
3237 dirname, out.ext,
3238 }) catch |err| {
3239 log.err("unable to copy {s}{s}: {s}", .{ dirname, out.ext, @errorName(err) });
3240 continue;
3241 };
3242 defer comp.gpa.free(src_path);
3243 obj_path.root_dir.handle.copyFile(src_path, directory.handle, loc.basename, .{}) catch |err| {
3244 log.err("unable to copy {s}: {s}", .{ src_path, @errorName(err) });
3245 };
3246 }
3247 }
3248 }
3249}
3271 const src_path: Cache.Path = .{
3272 .root_dir = c_obj_path.root_dir,
3273 .sub_path = try std.fmt.allocPrint(arena, "{s}{s}", .{
3274 c_obj_dir_and_stem,
3275 new_ext,
3276 }),
3277 };
3278 const emit_path = comp.resolveEmitPath(unresolved_emit_path);
32503279
3251fn resolveEmitLoc(
3252 arena: Allocator,
3253 default_artifact_directory: Cache.Path,
3254 opt_loc: ?EmitLoc,
3255) Allocator.Error!?[*:0]const u8 {
3256 const loc = opt_loc orelse return null;
3257 const slice = if (loc.directory) |directory|
3258 try directory.joinZ(arena, &.{loc.basename})
3259 else
3260 try default_artifact_directory.joinStringZ(arena, loc.basename);
3261 return slice.ptr;
3280 src_path.root_dir.handle.copyFile(
3281 src_path.sub_path,
3282 emit_path.root_dir.handle,
3283 emit_path.sub_path,
3284 .{},
3285 ) catch |err| log.err("unable to copy '{}' to '{}': {s}", .{
3286 src_path,
3287 emit_path,
3288 @errorName(err),
3289 });
32623290}
32633291
32643292/// Having the file open for writing is problematic as far as executing the
......@@ -4179,7 +4207,7 @@ fn performAllTheWorkInner(
41794207
41804208 comp.link_task_queue.start(comp);
41814209
4182 if (comp.docs_emit != null) {
4210 if (comp.emit_docs != null) {
41834211 dev.check(.docs_emit);
41844212 comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp});
41854213 work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });
......@@ -4457,7 +4485,7 @@ fn performAllTheWorkInner(
44574485 };
44584486 }
44594487 },
4460 .incremental => {},
4488 .none, .incremental => {},
44614489 }
44624490
44634491 if (any_fatal_files or
......@@ -4721,12 +4749,12 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void {
47214749 const zcu = comp.zcu orelse
47224750 return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{});
47234751
4724 const emit = comp.docs_emit.?;
4725 var out_dir = emit.root_dir.handle.makeOpenPath(emit.sub_path, .{}) catch |err| {
4752 const docs_path = comp.resolveEmitPath(comp.emit_docs.?);
4753 var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| {
47264754 return comp.lockAndSetMiscFailure(
47274755 .docs_copy,
4728 "unable to create output directory '{}{s}': {s}",
4729 .{ emit.root_dir, emit.sub_path, @errorName(err) },
4756 "unable to create output directory '{}': {s}",
4757 .{ docs_path, @errorName(err) },
47304758 );
47314759 };
47324760 defer out_dir.close();
......@@ -4745,8 +4773,8 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void {
47454773 var tar_file = out_dir.createFile("sources.tar", .{}) catch |err| {
47464774 return comp.lockAndSetMiscFailure(
47474775 .docs_copy,
4748 "unable to create '{}{s}/sources.tar': {s}",
4749 .{ emit.root_dir, emit.sub_path, @errorName(err) },
4776 "unable to create '{}/sources.tar': {s}",
4777 .{ docs_path, @errorName(err) },
47504778 );
47514779 };
47524780 defer tar_file.close();
......@@ -4896,11 +4924,6 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
48964924 .parent = root_mod,
48974925 });
48984926 try root_mod.deps.put(arena, "Walk", walk_mod);
4899 const bin_basename = try std.zig.binNameAlloc(arena, .{
4900 .root_name = root_name,
4901 .target = resolved_target.result,
4902 .output_mode = output_mode,
4903 });
49044927
49054928 const sub_compilation = try Compilation.create(gpa, arena, .{
49064929 .dirs = dirs,
......@@ -4912,10 +4935,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
49124935 .root_name = root_name,
49134936 .thread_pool = comp.thread_pool,
49144937 .libc_installation = comp.libc_installation,
4915 .emit_bin = .{
4916 .directory = null, // Put it in the cache directory.
4917 .basename = bin_basename,
4918 },
4938 .emit_bin = .yes_cache,
49194939 .verbose_cc = comp.verbose_cc,
49204940 .verbose_link = comp.verbose_link,
49214941 .verbose_air = comp.verbose_air,
......@@ -4930,27 +4950,31 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
49304950
49314951 try comp.updateSubCompilation(sub_compilation, .docs_wasm, prog_node);
49324952
4933 const emit = comp.docs_emit.?;
4934 var out_dir = emit.root_dir.handle.makeOpenPath(emit.sub_path, .{}) catch |err| {
4953 var crt_file = try sub_compilation.toCrtFile();
4954 defer crt_file.deinit(gpa);
4955
4956 const docs_bin_file = crt_file.full_object_path;
4957 assert(docs_bin_file.sub_path.len > 0); // emitted binary is not a directory
4958
4959 const docs_path = comp.resolveEmitPath(comp.emit_docs.?);
4960 var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| {
49354961 return comp.lockAndSetMiscFailure(
49364962 .docs_copy,
4937 "unable to create output directory '{}{s}': {s}",
4938 .{ emit.root_dir, emit.sub_path, @errorName(err) },
4963 "unable to create output directory '{}': {s}",
4964 .{ docs_path, @errorName(err) },
49394965 );
49404966 };
49414967 defer out_dir.close();
49424968
4943 sub_compilation.dirs.local_cache.handle.copyFile(
4944 sub_compilation.cache_use.whole.bin_sub_path.?,
4969 crt_file.full_object_path.root_dir.handle.copyFile(
4970 crt_file.full_object_path.sub_path,
49454971 out_dir,
49464972 "main.wasm",
49474973 .{},
49484974 ) catch |err| {
4949 return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}{s}' to '{}{s}': {s}", .{
4950 sub_compilation.dirs.local_cache,
4951 sub_compilation.cache_use.whole.bin_sub_path.?,
4952 emit.root_dir,
4953 emit.sub_path,
4975 return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}' to '{}': {s}", .{
4976 crt_file.full_object_path,
4977 docs_path,
49544978 @errorName(err),
49554979 });
49564980 };
......@@ -5212,7 +5236,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module
52125236 defer whole.cache_manifest_mutex.unlock();
52135237 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
52145238 },
5215 .incremental => {},
5239 .incremental, .none => {},
52165240 }
52175241
52185242 const bin_digest = man.finalBin();
......@@ -5557,9 +5581,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
55575581 defer man.deinit();
55585582
55595583 man.hash.add(comp.clang_preprocessor_mode);
5560 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
5561 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
5562 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);
5584 man.hash.addOptionalBytes(comp.emit_asm);
5585 man.hash.addOptionalBytes(comp.emit_llvm_ir);
5586 man.hash.addOptionalBytes(comp.emit_llvm_bc);
55635587
55645588 try cache_helpers.hashCSource(&man, c_object.src);
55655589
......@@ -5793,7 +5817,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
57935817 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
57945818 }
57955819 },
5796 .incremental => {},
5820 .incremental, .none => {},
57975821 }
57985822 }
57995823
......@@ -6037,7 +6061,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
60376061 defer whole.cache_manifest_mutex.unlock();
60386062 try whole_cache_manifest.addFilePost(dep_file_path);
60396063 },
6040 .incremental => {},
6064 .incremental, .none => {},
60416065 }
60426066 }
60436067 }
......@@ -7209,12 +7233,6 @@ fn buildOutputFromZig(
72097233 .cc_argv = &.{},
72107234 .parent = null,
72117235 });
7212 const target = comp.getTarget();
7213 const bin_basename = try std.zig.binNameAlloc(arena, .{
7214 .root_name = root_name,
7215 .target = target,
7216 .output_mode = output_mode,
7217 });
72187236
72197237 const parent_whole_cache: ?ParentWholeCache = switch (comp.cache_use) {
72207238 .whole => |whole| .{
......@@ -7227,7 +7245,7 @@ fn buildOutputFromZig(
72277245 3, // global cache is the same
72287246 },
72297247 },
7230 .incremental => null,
7248 .incremental, .none => null,
72317249 };
72327250
72337251 const sub_compilation = try Compilation.create(gpa, arena, .{
......@@ -7240,13 +7258,9 @@ fn buildOutputFromZig(
72407258 .root_name = root_name,
72417259 .thread_pool = comp.thread_pool,
72427260 .libc_installation = comp.libc_installation,
7243 .emit_bin = .{
7244 .directory = null, // Put it in the cache directory.
7245 .basename = bin_basename,
7246 },
7261 .emit_bin = .yes_cache,
72477262 .function_sections = true,
72487263 .data_sections = true,
7249 .emit_h = null,
72507264 .verbose_cc = comp.verbose_cc,
72517265 .verbose_link = comp.verbose_link,
72527266 .verbose_air = comp.verbose_air,
......@@ -7366,13 +7380,9 @@ pub fn build_crt_file(
73667380 .root_name = root_name,
73677381 .thread_pool = comp.thread_pool,
73687382 .libc_installation = comp.libc_installation,
7369 .emit_bin = .{
7370 .directory = null, // Put it in the cache directory.
7371 .basename = basename,
7372 },
7383 .emit_bin = .yes_cache,
73737384 .function_sections = options.function_sections orelse false,
73747385 .data_sections = options.data_sections orelse false,
7375 .emit_h = null,
73767386 .c_source_files = c_source_files,
73777387 .verbose_cc = comp.verbose_cc,
73787388 .verbose_link = comp.verbose_link,
......@@ -7444,7 +7454,11 @@ pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {
74447454 return .{
74457455 .full_object_path = .{
74467456 .root_dir = comp.dirs.local_cache,
7447 .sub_path = try comp.gpa.dupe(u8, comp.cache_use.whole.bin_sub_path.?),
7457 .sub_path = try std.fs.path.join(comp.gpa, &.{
7458 "o",
7459 &Cache.binToHex(comp.digest.?),
7460 comp.emit_bin.?,
7461 }),
74487462 },
74497463 .lock = comp.cache_use.whole.moveLock(),
74507464 };
src/Zcu/PerThread.zig+2-2
......@@ -2493,7 +2493,7 @@ fn newEmbedFile(
24932493 cache: {
24942494 const whole = switch (zcu.comp.cache_use) {
24952495 .whole => |whole| whole,
2496 .incremental => break :cache,
2496 .incremental, .none => break :cache,
24972497 };
24982498 const man = whole.cache_manifest orelse break :cache;
24992499 const ip_str = opt_ip_str orelse break :cache; // this will be a compile error
......@@ -3377,7 +3377,7 @@ pub fn populateTestFunctions(
33773377 }
33783378
33793379 // The linker thread is not running, so we actually need to dispatch this task directly.
3380 @import("../link.zig").doZcuTask(zcu.comp, @intFromEnum(pt.tid), .{ .link_nav = nav_index });
3380 @import("../link.zig").linkTestFunctionsNav(pt, nav_index);
33813381 }
33823382}
33833383
src/libs/freebsd.zig+1-6
......@@ -1019,10 +1019,6 @@ fn buildSharedLib(
10191019 defer tracy.end();
10201020
10211021 const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });
1022 const emit_bin = Compilation.EmitLoc{
1023 .directory = bin_directory,
1024 .basename = basename,
1025 };
10261022 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
10271023 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
10281024 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
......@@ -1082,8 +1078,7 @@ fn buildSharedLib(
10821078 .root_mod = root_mod,
10831079 .root_name = lib.name,
10841080 .libc_installation = comp.libc_installation,
1085 .emit_bin = emit_bin,
1086 .emit_h = null,
1081 .emit_bin = .yes_cache,
10871082 .verbose_cc = comp.verbose_cc,
10881083 .verbose_link = comp.verbose_link,
10891084 .verbose_air = comp.verbose_air,
src/libs/glibc.zig+1-6
......@@ -1185,10 +1185,6 @@ fn buildSharedLib(
11851185 defer tracy.end();
11861186
11871187 const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });
1188 const emit_bin = Compilation.EmitLoc{
1189 .directory = bin_directory,
1190 .basename = basename,
1191 };
11921188 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
11931189 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
11941190 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
......@@ -1248,8 +1244,7 @@ fn buildSharedLib(
12481244 .root_mod = root_mod,
12491245 .root_name = lib.name,
12501246 .libc_installation = comp.libc_installation,
1251 .emit_bin = emit_bin,
1252 .emit_h = null,
1247 .emit_bin = .yes_cache,
12531248 .verbose_cc = comp.verbose_cc,
12541249 .verbose_link = comp.verbose_link,
12551250 .verbose_air = comp.verbose_air,
src/libs/libcxx.zig+2-26
......@@ -122,17 +122,6 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!
122122 const output_mode = .Lib;
123123 const link_mode = .static;
124124 const target = comp.root_mod.resolved_target.result;
125 const basename = try std.zig.binNameAlloc(arena, .{
126 .root_name = root_name,
127 .target = target,
128 .output_mode = output_mode,
129 .link_mode = link_mode,
130 });
131
132 const emit_bin = Compilation.EmitLoc{
133 .directory = null, // Put it in the cache directory.
134 .basename = basename,
135 };
136125
137126 const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" });
138127 const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" });
......@@ -271,8 +260,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!
271260 .root_name = root_name,
272261 .thread_pool = comp.thread_pool,
273262 .libc_installation = comp.libc_installation,
274 .emit_bin = emit_bin,
275 .emit_h = null,
263 .emit_bin = .yes_cache,
276264 .c_source_files = c_source_files.items,
277265 .verbose_cc = comp.verbose_cc,
278266 .verbose_link = comp.verbose_link,
......@@ -327,17 +315,6 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
327315 const output_mode = .Lib;
328316 const link_mode = .static;
329317 const target = comp.root_mod.resolved_target.result;
330 const basename = try std.zig.binNameAlloc(arena, .{
331 .root_name = root_name,
332 .target = target,
333 .output_mode = output_mode,
334 .link_mode = link_mode,
335 });
336
337 const emit_bin = Compilation.EmitLoc{
338 .directory = null, // Put it in the cache directory.
339 .basename = basename,
340 };
341318
342319 const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" });
343320 const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" });
......@@ -467,8 +444,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
467444 .root_name = root_name,
468445 .thread_pool = comp.thread_pool,
469446 .libc_installation = comp.libc_installation,
470 .emit_bin = emit_bin,
471 .emit_h = null,
447 .emit_bin = .yes_cache,
472448 .c_source_files = c_source_files.items,
473449 .verbose_cc = comp.verbose_cc,
474450 .verbose_link = comp.verbose_link,
src/libs/libtsan.zig+1-7
......@@ -45,11 +45,6 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
4545 .link_mode = link_mode,
4646 });
4747
48 const emit_bin = Compilation.EmitLoc{
49 .directory = null, // Put it in the cache directory.
50 .basename = basename,
51 };
52
5348 const optimize_mode = comp.compilerRtOptMode();
5449 const strip = comp.compilerRtStrip();
5550 const unwind_tables: std.builtin.UnwindTables =
......@@ -287,8 +282,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
287282 .root_mod = root_mod,
288283 .root_name = root_name,
289284 .libc_installation = comp.libc_installation,
290 .emit_bin = emit_bin,
291 .emit_h = null,
285 .emit_bin = .yes_cache,
292286 .c_source_files = c_source_files.items,
293287 .verbose_cc = comp.verbose_cc,
294288 .verbose_link = comp.verbose_link,
src/libs/libunwind.zig+2-13
......@@ -31,7 +31,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
3131 const unwind_tables: std.builtin.UnwindTables =
3232 if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .@"async";
3333 const config = Compilation.Config.resolve(.{
34 .output_mode = .Lib,
34 .output_mode = output_mode,
3535 .resolved_target = comp.root_mod.resolved_target,
3636 .is_test = false,
3737 .have_zcu = false,
......@@ -85,17 +85,6 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
8585 };
8686
8787 const root_name = "unwind";
88 const link_mode = .static;
89 const basename = try std.zig.binNameAlloc(arena, .{
90 .root_name = root_name,
91 .target = target,
92 .output_mode = output_mode,
93 .link_mode = link_mode,
94 });
95 const emit_bin = Compilation.EmitLoc{
96 .directory = null, // Put it in the cache directory.
97 .basename = basename,
98 };
9988 var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined;
10089 for (unwind_src_list, 0..) |unwind_src, i| {
10190 var cflags = std.ArrayList([]const u8).init(arena);
......@@ -160,7 +149,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
160149 .main_mod = null,
161150 .thread_pool = comp.thread_pool,
162151 .libc_installation = comp.libc_installation,
163 .emit_bin = emit_bin,
152 .emit_bin = .yes_cache,
164153 .function_sections = comp.function_sections,
165154 .c_source_files = &c_source_files,
166155 .verbose_cc = comp.verbose_cc,
src/libs/musl.zig+1-2
......@@ -252,8 +252,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro
252252 .thread_pool = comp.thread_pool,
253253 .root_name = "c",
254254 .libc_installation = comp.libc_installation,
255 .emit_bin = .{ .directory = null, .basename = "libc.so" },
256 .emit_h = null,
255 .emit_bin = .yes_cache,
257256 .verbose_cc = comp.verbose_cc,
258257 .verbose_link = comp.verbose_link,
259258 .verbose_air = comp.verbose_air,
src/libs/netbsd.zig+1-6
......@@ -684,10 +684,6 @@ fn buildSharedLib(
684684 defer tracy.end();
685685
686686 const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });
687 const emit_bin = Compilation.EmitLoc{
688 .directory = bin_directory,
689 .basename = basename,
690 };
691687 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
692688 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
693689 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
......@@ -746,8 +742,7 @@ fn buildSharedLib(
746742 .root_mod = root_mod,
747743 .root_name = lib.name,
748744 .libc_installation = comp.libc_installation,
749 .emit_bin = emit_bin,
750 .emit_h = null,
745 .emit_bin = .yes_cache,
751746 .verbose_cc = comp.verbose_cc,
752747 .verbose_link = comp.verbose_link,
753748 .verbose_air = comp.verbose_air,
src/link.zig+31-5
......@@ -384,9 +384,11 @@ pub const File = struct {
384384 emit: Path,
385385
386386 file: ?fs.File,
387 /// When linking with LLD, this linker code will output an object file only at
388 /// this location, and then this path can be placed on the LLD linker line.
389 zcu_object_sub_path: ?[]const u8 = null,
387 /// When using the LLVM backend, the emitted object is written to a file with this name. This
388 /// object file then becomes a normal link input to LLD or a self-hosted linker.
389 ///
390 /// To convert this to an actual path, see `Compilation.resolveEmitPath` (with `kind == .temp`).
391 zcu_object_basename: ?[]const u8 = null,
390392 gc_sections: bool,
391393 print_gc_sections: bool,
392394 build_id: std.zig.BuildId,
......@@ -433,7 +435,6 @@ pub const File = struct {
433435 export_symbol_names: []const []const u8,
434436 global_base: ?u64,
435437 build_id: std.zig.BuildId,
436 disable_lld_caching: bool,
437438 hash_style: Lld.Elf.HashStyle,
438439 sort_section: ?Lld.Elf.SortSection,
439440 major_subsystem_version: ?u16,
......@@ -1083,7 +1084,7 @@ pub const File = struct {
10831084 // In this case, an object file is created by the LLVM backend, so
10841085 // there is no prelink phase. The Zig code is linked as a standard
10851086 // object along with the others.
1086 if (base.zcu_object_sub_path != null) return;
1087 if (base.zcu_object_basename != null) return;
10871088
10881089 switch (base.tag) {
10891090 inline .wasm => |tag| {
......@@ -1496,6 +1497,31 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
14961497 },
14971498 }
14981499}
1500/// After the main pipeline is done, but before flush, the compilation may need to link one final
1501/// `Nav` into the binary: the `builtin.test_functions` value. Since the link thread isn't running
1502/// by then, we expose this function which can be called directly.
1503pub fn linkTestFunctionsNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) void {
1504 const zcu = pt.zcu;
1505 const comp = zcu.comp;
1506 const diags = &comp.link_diags;
1507 if (zcu.llvm_object) |llvm_object| {
1508 llvm_object.updateNav(pt, nav_index) catch |err| switch (err) {
1509 error.OutOfMemory => diags.setAllocFailure(),
1510 };
1511 } else if (comp.bin_file) |lf| {
1512 lf.updateNav(pt, nav_index) catch |err| switch (err) {
1513 error.OutOfMemory => diags.setAllocFailure(),
1514 error.CodegenFail => zcu.assertCodegenFailed(nav_index),
1515 error.Overflow, error.RelocationNotByteAligned => {
1516 switch (zcu.codegenFail(nav_index, "unable to codegen: {s}", .{@errorName(err)})) {
1517 error.CodegenFail => return,
1518 error.OutOfMemory => return diags.setAllocFailure(),
1519 }
1520 // Not a retryable failure.
1521 },
1522 };
1523 }
1524}
14991525
15001526/// Provided by the CLI, processed into `LinkInput` instances at the start of
15011527/// the compilation pipeline.
src/link/Coff.zig+4-9
......@@ -224,21 +224,16 @@ pub fn createEmpty(
224224 else => 0x1000,
225225 };
226226
227 // If using LLVM to generate the object file for the zig compilation unit,
228 // we need a place to put the object file so that it can be subsequently
229 // handled.
230 const zcu_object_sub_path = if (!use_llvm)
231 null
232 else
233 try allocPrint(arena, "{s}.obj", .{emit.sub_path});
234
235227 const coff = try arena.create(Coff);
236228 coff.* = .{
237229 .base = .{
238230 .tag = .coff,
239231 .comp = comp,
240232 .emit = emit,
241 .zcu_object_sub_path = zcu_object_sub_path,
233 .zcu_object_basename = if (use_llvm)
234 try std.fmt.allocPrint(arena, "{s}_zcu.obj", .{fs.path.stem(emit.sub_path)})
235 else
236 null,
242237 .stack_size = options.stack_size orelse 16777216,
243238 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),
244239 .print_gc_sections = options.print_gc_sections,
src/link/Elf.zig+7-16
......@@ -249,14 +249,6 @@ pub fn createEmpty(
249249 const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic;
250250 const default_sym_version: elf.Versym = if (is_dyn_lib or comp.config.rdynamic) .GLOBAL else .LOCAL;
251251
252 // If using LLVM to generate the object file for the zig compilation unit,
253 // we need a place to put the object file so that it can be subsequently
254 // handled.
255 const zcu_object_sub_path = if (!use_llvm)
256 null
257 else
258 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
259
260252 var rpath_table: std.StringArrayHashMapUnmanaged(void) = .empty;
261253 try rpath_table.entries.resize(arena, options.rpath_list.len);
262254 @memcpy(rpath_table.entries.items(.key), options.rpath_list);
......@@ -268,7 +260,10 @@ pub fn createEmpty(
268260 .tag = .elf,
269261 .comp = comp,
270262 .emit = emit,
271 .zcu_object_sub_path = zcu_object_sub_path,
263 .zcu_object_basename = if (use_llvm)
264 try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
265 else
266 null,
272267 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
273268 .print_gc_sections = options.print_gc_sections,
274269 .stack_size = options.stack_size orelse 16777216,
......@@ -770,17 +765,13 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
770765 const gpa = comp.gpa;
771766 const diags = &comp.link_diags;
772767
773 const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
774 .root_dir = self.base.emit.root_dir,
775 .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname|
776 try fs.path.join(arena, &.{ dirname, path })
777 else
778 path,
768 const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: {
769 break :p try comp.resolveEmitPathFlush(arena, .temp, raw);
779770 } else null;
780771
781772 if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid);
782773
783 if (module_obj_path) |path| openParseObjectReportingFailure(self, path);
774 if (zcu_obj_path) |path| openParseObjectReportingFailure(self, path);
784775
785776 switch (comp.config.output_mode) {
786777 .Obj => return relocatable.flushObject(self, comp),
src/link/Goff.zig+1-1
......@@ -41,7 +41,7 @@ pub fn createEmpty(
4141 .tag = .goff,
4242 .comp = comp,
4343 .emit = emit,
44 .zcu_object_sub_path = emit.sub_path,
44 .zcu_object_basename = emit.sub_path,
4545 .gc_sections = options.gc_sections orelse false,
4646 .print_gc_sections = options.print_gc_sections,
4747 .stack_size = options.stack_size orelse 0,
src/link/Lld.zig+26-49
......@@ -1,5 +1,4 @@
11base: link.File,
2disable_caching: bool,
32ofmt: union(enum) {
43 elf: Elf,
54 coff: Coff,
......@@ -231,7 +230,7 @@ pub fn createEmpty(
231230 .tag = .lld,
232231 .comp = comp,
233232 .emit = emit,
234 .zcu_object_sub_path = try allocPrint(arena, "{s}.{s}", .{ emit.sub_path, obj_file_ext }),
233 .zcu_object_basename = try allocPrint(arena, "{s}_zcu.{s}", .{ fs.path.stem(emit.sub_path), obj_file_ext }),
235234 .gc_sections = gc_sections,
236235 .print_gc_sections = options.print_gc_sections,
237236 .stack_size = stack_size,
......@@ -239,7 +238,6 @@ pub fn createEmpty(
239238 .file = null,
240239 .build_id = options.build_id,
241240 },
242 .disable_caching = options.disable_lld_caching,
243241 .ofmt = switch (target.ofmt) {
244242 .coff => .{ .coff = try .init(comp, options) },
245243 .elf => .{ .elf = try .init(comp, options) },
......@@ -289,14 +287,11 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {
289287 const full_out_path_z = try arena.dupeZ(u8, full_out_path);
290288 const opt_zcu = comp.zcu;
291289
292 // If there is no Zig code to compile, then we should skip flushing the output file
293 // because it will not be part of the linker line anyway.
294 const zcu_obj_path: ?[]const u8 = if (opt_zcu != null) blk: {
295 const dirname = fs.path.dirname(full_out_path_z) orelse ".";
296 break :blk try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? });
290 const zcu_obj_path: ?Cache.Path = if (opt_zcu != null) p: {
291 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
297292 } else null;
298293
299 log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"});
294 log.debug("zcu_obj_path={?}", .{zcu_obj_path});
300295
301296 const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj)
302297 comp.compiler_rt_obj.?.full_object_path
......@@ -330,7 +325,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {
330325 for (comp.win32_resource_table.keys()) |key| {
331326 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));
332327 }
333 if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
328 if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
334329 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
335330 if (ubsan_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
336331
......@@ -368,14 +363,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
368363 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
369364 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
370365
371 // If there is no Zig code to compile, then we should skip flushing the output file because it
372 // will not be part of the linker line anyway.
373 const module_obj_path: ?[]const u8 = if (comp.zcu != null) p: {
374 if (fs.path.dirname(full_out_path)) |dirname| {
375 break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? });
376 } else {
377 break :p base.zcu_object_sub_path.?;
378 }
366 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
367 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
379368 } else null;
380369
381370 const is_lib = comp.config.output_mode == .Lib;
......@@ -402,8 +391,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
402391 if (comp.c_object_table.count() != 0)
403392 break :blk comp.c_object_table.keys()[0].status.success.object_path;
404393
405 if (module_obj_path) |p|
406 break :blk Cache.Path.initCwd(p);
394 if (zcu_obj_path) |p|
395 break :blk p;
407396
408397 // TODO I think this is unreachable. Audit this situation when solving the above TODO
409398 // regarding eliding redundant object -> object transformations.
......@@ -513,9 +502,9 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
513502
514503 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
515504
516 if (comp.implib_emit) |emit| {
517 const implib_out_path = try emit.root_dir.join(arena, &[_][]const u8{emit.sub_path});
518 try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
505 if (comp.emit_implib) |raw_emit_path| {
506 const path = try comp.resolveEmitPathFlush(arena, .temp, raw_emit_path);
507 try argv.append(try allocPrint(arena, "-IMPLIB:{}", .{path}));
519508 }
520509
521510 if (comp.config.link_libc) {
......@@ -556,8 +545,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
556545 try argv.append(key.status.success.res_path);
557546 }
558547
559 if (module_obj_path) |p| {
560 try argv.append(p);
548 if (zcu_obj_path) |p| {
549 try argv.append(try p.toString(arena));
561550 }
562551
563552 if (coff.module_definition_file) |def| {
......@@ -808,14 +797,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
808797 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
809798 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
810799
811 // If there is no Zig code to compile, then we should skip flushing the output file because it
812 // will not be part of the linker line anyway.
813 const module_obj_path: ?[]const u8 = if (comp.zcu != null) p: {
814 if (fs.path.dirname(full_out_path)) |dirname| {
815 break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? });
816 } else {
817 break :p base.zcu_object_sub_path.?;
818 }
800 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
801 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
819802 } else null;
820803
821804 const output_mode = comp.config.output_mode;
......@@ -862,8 +845,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
862845 if (comp.c_object_table.count() != 0)
863846 break :blk comp.c_object_table.keys()[0].status.success.object_path;
864847
865 if (module_obj_path) |p|
866 break :blk Cache.Path.initCwd(p);
848 if (zcu_obj_path) |p|
849 break :blk p;
867850
868851 // TODO I think this is unreachable. Audit this situation when solving the above TODO
869852 // regarding eliding redundant object -> object transformations.
......@@ -1151,8 +1134,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
11511134 try argv.append(try key.status.success.object_path.toString(arena));
11521135 }
11531136
1154 if (module_obj_path) |p| {
1155 try argv.append(p);
1137 if (zcu_obj_path) |p| {
1138 try argv.append(try p.toString(arena));
11561139 }
11571140
11581141 if (comp.tsan_lib) |lib| {
......@@ -1387,14 +1370,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
13871370 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
13881371 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
13891372
1390 // If there is no Zig code to compile, then we should skip flushing the output file because it
1391 // will not be part of the linker line anyway.
1392 const module_obj_path: ?[]const u8 = if (comp.zcu != null) p: {
1393 if (fs.path.dirname(full_out_path)) |dirname| {
1394 break :p try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? });
1395 } else {
1396 break :p base.zcu_object_sub_path.?;
1397 }
1373 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
1374 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
13981375 } else null;
13991376
14001377 const is_obj = comp.config.output_mode == .Obj;
......@@ -1419,8 +1396,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
14191396 if (comp.c_object_table.count() != 0)
14201397 break :blk comp.c_object_table.keys()[0].status.success.object_path;
14211398
1422 if (module_obj_path) |p|
1423 break :blk Cache.Path.initCwd(p);
1399 if (zcu_obj_path) |p|
1400 break :blk p;
14241401
14251402 // TODO I think this is unreachable. Audit this situation when solving the above TODO
14261403 // regarding eliding redundant object -> object transformations.
......@@ -1610,8 +1587,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
16101587 for (comp.c_object_table.keys()) |key| {
16111588 try argv.append(try key.status.success.object_path.toString(arena));
16121589 }
1613 if (module_obj_path) |p| {
1614 try argv.append(p);
1590 if (zcu_obj_path) |p| {
1591 try argv.append(try p.toString(arena));
16151592 }
16161593
16171594 if (compiler_rt_path) |p| {
src/link/MachO.zig+14-26
......@@ -173,13 +173,6 @@ pub fn createEmpty(
173173 const output_mode = comp.config.output_mode;
174174 const link_mode = comp.config.link_mode;
175175
176 // If using LLVM to generate the object file for the zig compilation unit,
177 // we need a place to put the object file so that it can be subsequently
178 // handled.
179 const zcu_object_sub_path = if (!use_llvm)
180 null
181 else
182 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
183176 const allow_shlib_undefined = options.allow_shlib_undefined orelse false;
184177
185178 const self = try arena.create(MachO);
......@@ -188,7 +181,10 @@ pub fn createEmpty(
188181 .tag = .macho,
189182 .comp = comp,
190183 .emit = emit,
191 .zcu_object_sub_path = zcu_object_sub_path,
184 .zcu_object_basename = if (use_llvm)
185 try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
186 else
187 null,
192188 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),
193189 .print_gc_sections = options.print_gc_sections,
194190 .stack_size = options.stack_size orelse 16777216,
......@@ -351,21 +347,16 @@ pub fn flush(
351347 const sub_prog_node = prog_node.start("MachO Flush", 0);
352348 defer sub_prog_node.end();
353349
354 const directory = self.base.emit.root_dir;
355 const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
356 .root_dir = directory,
357 .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname|
358 try fs.path.join(arena, &.{ dirname, path })
359 else
360 path,
350 const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: {
351 break :p try comp.resolveEmitPathFlush(arena, .temp, raw);
361352 } else null;
362353
363354 // --verbose-link
364355 if (comp.verbose_link) try self.dumpArgv(comp);
365356
366357 if (self.getZigObject()) |zo| try zo.flush(self, tid);
367 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
368 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
358 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, zcu_obj_path);
359 if (self.base.isObject()) return relocatable.flushObject(self, comp, zcu_obj_path);
369360
370361 var positionals = std.ArrayList(link.Input).init(gpa);
371362 defer positionals.deinit();
......@@ -387,7 +378,7 @@ pub fn flush(
387378 positionals.appendAssumeCapacity(try link.openObjectInput(diags, key.status.success.object_path));
388379 }
389380
390 if (module_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path));
381 if (zcu_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path));
391382
392383 if (comp.config.any_sanitize_thread) {
393384 try positionals.append(try link.openObjectInput(diags, comp.tsan_lib.?.full_object_path));
......@@ -636,12 +627,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
636627
637628 const directory = self.base.emit.root_dir;
638629 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
639 const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
640 if (fs.path.dirname(full_out_path)) |dirname| {
641 break :blk try fs.path.join(arena, &.{ dirname, path });
642 } else {
643 break :blk path;
644 }
630 const zcu_obj_path: ?[]const u8 = if (self.base.zcu_object_basename) |raw| p: {
631 const p = try comp.resolveEmitPathFlush(arena, .temp, raw);
632 break :p try p.toString(arena);
645633 } else null;
646634
647635 var argv = std.ArrayList([]const u8).init(arena);
......@@ -670,7 +658,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
670658 try argv.append(try key.status.success.object_path.toString(arena));
671659 }
672660
673 if (module_obj_path) |p| {
661 if (zcu_obj_path) |p| {
674662 try argv.append(p);
675663 }
676664 } else {
......@@ -762,7 +750,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
762750 try argv.append(try key.status.success.object_path.toString(arena));
763751 }
764752
765 if (module_obj_path) |p| {
753 if (zcu_obj_path) |p| {
766754 try argv.append(p);
767755 }
768756
src/link/Wasm.zig+7-18
......@@ -2951,21 +2951,16 @@ pub fn createEmpty(
29512951 const output_mode = comp.config.output_mode;
29522952 const wasi_exec_model = comp.config.wasi_exec_model;
29532953
2954 // If using LLVM to generate the object file for the zig compilation unit,
2955 // we need a place to put the object file so that it can be subsequently
2956 // handled.
2957 const zcu_object_sub_path = if (!use_llvm)
2958 null
2959 else
2960 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
2961
29622954 const wasm = try arena.create(Wasm);
29632955 wasm.* = .{
29642956 .base = .{
29652957 .tag = .wasm,
29662958 .comp = comp,
29672959 .emit = emit,
2968 .zcu_object_sub_path = zcu_object_sub_path,
2960 .zcu_object_basename = if (use_llvm)
2961 try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
2962 else
2963 null,
29692964 // Garbage collection is so crucial to WebAssembly that we design
29702965 // the linker around the assumption that it will be on in the vast
29712966 // majority of cases, and therefore express "no garbage collection"
......@@ -3834,15 +3829,9 @@ pub fn flush(
38343829
38353830 if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items);
38363831
3837 if (wasm.base.zcu_object_sub_path) |path| {
3838 const module_obj_path: Path = .{
3839 .root_dir = wasm.base.emit.root_dir,
3840 .sub_path = if (fs.path.dirname(wasm.base.emit.sub_path)) |dirname|
3841 try fs.path.join(arena, &.{ dirname, path })
3842 else
3843 path,
3844 };
3845 openParseObjectReportingFailure(wasm, module_obj_path);
3832 if (wasm.base.zcu_object_basename) |raw| {
3833 const zcu_obj_path: Path = try comp.resolveEmitPathFlush(arena, .temp, raw);
3834 openParseObjectReportingFailure(wasm, zcu_obj_path);
38463835 try prelink(wasm, prog_node);
38473836 }
38483837
src/link/Xcoff.zig+1-1
......@@ -41,7 +41,7 @@ pub fn createEmpty(
4141 .tag = .xcoff,
4242 .comp = comp,
4343 .emit = emit,
44 .zcu_object_sub_path = emit.sub_path,
44 .zcu_object_basename = emit.sub_path,
4545 .gc_sections = options.gc_sections orelse false,
4646 .print_gc_sections = options.print_gc_sections,
4747 .stack_size = options.stack_size orelse 0,
src/main.zig+101-254
......@@ -699,55 +699,21 @@ const Emit = union(enum) {
699699 yes_default_path,
700700 yes: []const u8,
701701
702 const Resolved = struct {
703 data: ?Compilation.EmitLoc,
704 dir: ?fs.Dir,
705
706 fn deinit(self: *Resolved) void {
707 if (self.dir) |*dir| {
708 dir.close();
709 }
710 }
711 };
712
713 fn resolve(emit: Emit, default_basename: []const u8, output_to_cache: bool) !Resolved {
714 var resolved: Resolved = .{ .data = null, .dir = null };
715 errdefer resolved.deinit();
716
717 switch (emit) {
718 .no => {},
719 .yes_default_path => {
720 resolved.data = Compilation.EmitLoc{
721 .directory = if (output_to_cache) null else .{
722 .path = null,
723 .handle = fs.cwd(),
724 },
725 .basename = default_basename,
726 };
727 },
728 .yes => |full_path| {
729 const basename = fs.path.basename(full_path);
730 if (fs.path.dirname(full_path)) |dirname| {
731 const handle = try fs.cwd().openDir(dirname, .{});
732 resolved = .{
733 .dir = handle,
734 .data = Compilation.EmitLoc{
735 .basename = basename,
736 .directory = .{
737 .path = dirname,
738 .handle = handle,
739 },
740 },
741 };
742 } else {
743 resolved.data = Compilation.EmitLoc{
744 .basename = basename,
745 .directory = .{ .path = null, .handle = fs.cwd() },
746 };
702 const OutputToCacheReason = enum { listen, @"zig run", @"zig test" };
703 fn resolve(emit: Emit, default_basename: []const u8, output_to_cache: ?OutputToCacheReason) Compilation.CreateOptions.Emit {
704 return switch (emit) {
705 .no => .no,
706 .yes_default_path => if (output_to_cache != null) .yes_cache else .{ .yes_path = default_basename },
707 .yes => |path| if (output_to_cache) |reason| {
708 switch (reason) {
709 .listen => fatal("--listen incompatible with explicit output path '{s}'", .{path}),
710 .@"zig run", .@"zig test" => fatal(
711 "'{s}' with explicit output path '{s}' requires explicit '-femit-bin=path' or '-fno-emit-bin'",
712 .{ @tagName(reason), path },
713 ),
747714 }
748 },
749 }
750 return resolved;
715 } else .{ .yes_path = path },
716 };
751717 }
752718};
753719
......@@ -2830,7 +2796,7 @@ fn buildOutputType(
28302796 .link => {
28312797 create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe;
28322798 if (emit_bin != .no) {
2833 emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
2799 emit_bin = if (out_path) |p| .{ .yes = p } else .yes_a_out;
28342800 }
28352801 if (emit_llvm) {
28362802 fatal("-emit-llvm cannot be used when linking", .{});
......@@ -3208,7 +3174,17 @@ fn buildOutputType(
32083174 var cleanup_emit_bin_dir: ?fs.Dir = null;
32093175 defer if (cleanup_emit_bin_dir) |*dir| dir.close();
32103176
3211 const output_to_cache = listen != .none;
3177 // For `zig run` and `zig test`, we don't want to put the binary in the cwd by default. So, if
3178 // the binary is requested with no explicit path (as is the default), we emit to the cache.
3179 const output_to_cache: ?Emit.OutputToCacheReason = switch (listen) {
3180 .stdio, .ip4 => .listen,
3181 .none => if (arg_mode == .run and emit_bin == .yes_default_path)
3182 .@"zig run"
3183 else if (arg_mode == .zig_test and emit_bin == .yes_default_path)
3184 .@"zig test"
3185 else
3186 null,
3187 };
32123188 const optional_version = if (have_version) version else null;
32133189
32143190 const root_name = if (provided_name) |n| n else main_mod.fully_qualified_name;
......@@ -3225,150 +3201,48 @@ fn buildOutputType(
32253201 },
32263202 };
32273203
3228 const a_out_basename = switch (target.ofmt) {
3229 .coff => "a.exe",
3230 else => "a.out",
3231 };
3232
3233 const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) {
3234 .no => null,
3235 .yes_default_path => Compilation.EmitLoc{
3236 .directory = blk: {
3237 switch (arg_mode) {
3238 .run, .zig_test => break :blk null,
3239 .build, .cc, .cpp, .translate_c, .zig_test_obj => {
3240 if (output_to_cache) {
3241 break :blk null;
3242 } else {
3243 break :blk .{ .path = null, .handle = fs.cwd() };
3244 }
3245 },
3246 }
3247 },
3248 .basename = if (clang_preprocessor_mode == .pch)
3249 try std.fmt.allocPrint(arena, "{s}.pch", .{root_name})
3250 else
3251 try std.zig.binNameAlloc(arena, .{
3204 const emit_bin_resolved: Compilation.CreateOptions.Emit = switch (emit_bin) {
3205 .no => .no,
3206 .yes_default_path => emit: {
3207 if (output_to_cache != null) break :emit .yes_cache;
3208 const name = switch (clang_preprocessor_mode) {
3209 .pch => try std.fmt.allocPrint(arena, "{s}.pch", .{root_name}),
3210 else => try std.zig.binNameAlloc(arena, .{
32523211 .root_name = root_name,
32533212 .target = target,
32543213 .output_mode = create_module.resolved_options.output_mode,
32553214 .link_mode = create_module.resolved_options.link_mode,
32563215 .version = optional_version,
32573216 }),
3217 };
3218 break :emit .{ .yes_path = name };
32583219 },
3259 .yes => |full_path| b: {
3260 const basename = fs.path.basename(full_path);
3261 if (fs.path.dirname(full_path)) |dirname| {
3262 const handle = fs.cwd().openDir(dirname, .{}) catch |err| {
3263 fatal("unable to open output directory '{s}': {s}", .{ dirname, @errorName(err) });
3264 };
3265 cleanup_emit_bin_dir = handle;
3266 break :b Compilation.EmitLoc{
3267 .basename = basename,
3268 .directory = .{
3269 .path = dirname,
3270 .handle = handle,
3271 },
3272 };
3273 } else {
3274 break :b Compilation.EmitLoc{
3275 .basename = basename,
3276 .directory = .{ .path = null, .handle = fs.cwd() },
3277 };
3278 }
3279 },
3280 .yes_a_out => Compilation.EmitLoc{
3281 .directory = .{ .path = null, .handle = fs.cwd() },
3282 .basename = a_out_basename,
3220 .yes => |path| if (output_to_cache != null) {
3221 assert(output_to_cache == .listen); // there was an explicit bin path
3222 fatal("--listen incompatible with explicit output path '{s}'", .{path});
3223 } else .{ .yes_path = path },
3224 .yes_a_out => emit: {
3225 assert(output_to_cache == null);
3226 break :emit .{ .yes_path = switch (target.ofmt) {
3227 .coff => "a.exe",
3228 else => "a.out",
3229 } };
32833230 },
32843231 };
32853232
32863233 const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name});
3287 var emit_h_resolved = emit_h.resolve(default_h_basename, output_to_cache) catch |err| {
3288 switch (emit_h) {
3289 .yes => |p| {
3290 fatal("unable to open directory from argument '-femit-h', '{s}': {s}", .{
3291 p, @errorName(err),
3292 });
3293 },
3294 .yes_default_path => {
3295 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
3296 default_h_basename, @errorName(err),
3297 });
3298 },
3299 .no => unreachable,
3300 }
3301 };
3302 defer emit_h_resolved.deinit();
3234 const emit_h_resolved = emit_h.resolve(default_h_basename, output_to_cache);
33033235
33043236 const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name});
3305 var emit_asm_resolved = emit_asm.resolve(default_asm_basename, output_to_cache) catch |err| {
3306 switch (emit_asm) {
3307 .yes => |p| {
3308 fatal("unable to open directory from argument '-femit-asm', '{s}': {s}", .{
3309 p, @errorName(err),
3310 });
3311 },
3312 .yes_default_path => {
3313 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
3314 default_asm_basename, @errorName(err),
3315 });
3316 },
3317 .no => unreachable,
3318 }
3319 };
3320 defer emit_asm_resolved.deinit();
3237 const emit_asm_resolved = emit_asm.resolve(default_asm_basename, output_to_cache);
33213238
33223239 const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name});
3323 var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename, output_to_cache) catch |err| {
3324 switch (emit_llvm_ir) {
3325 .yes => |p| {
3326 fatal("unable to open directory from argument '-femit-llvm-ir', '{s}': {s}", .{
3327 p, @errorName(err),
3328 });
3329 },
3330 .yes_default_path => {
3331 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
3332 default_llvm_ir_basename, @errorName(err),
3333 });
3334 },
3335 .no => unreachable,
3336 }
3337 };
3338 defer emit_llvm_ir_resolved.deinit();
3240 const emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename, output_to_cache);
33393241
33403242 const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name});
3341 var emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename, output_to_cache) catch |err| {
3342 switch (emit_llvm_bc) {
3343 .yes => |p| {
3344 fatal("unable to open directory from argument '-femit-llvm-bc', '{s}': {s}", .{
3345 p, @errorName(err),
3346 });
3347 },
3348 .yes_default_path => {
3349 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
3350 default_llvm_bc_basename, @errorName(err),
3351 });
3352 },
3353 .no => unreachable,
3354 }
3355 };
3356 defer emit_llvm_bc_resolved.deinit();
3243 const emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename, output_to_cache);
33573244
3358 var emit_docs_resolved = emit_docs.resolve("docs", output_to_cache) catch |err| {
3359 switch (emit_docs) {
3360 .yes => |p| {
3361 fatal("unable to open directory from argument '-femit-docs', '{s}': {s}", .{
3362 p, @errorName(err),
3363 });
3364 },
3365 .yes_default_path => {
3366 fatal("unable to open directory 'docs': {s}", .{@errorName(err)});
3367 },
3368 .no => unreachable,
3369 }
3370 };
3371 defer emit_docs_resolved.deinit();
3245 const emit_docs_resolved = emit_docs.resolve("docs", output_to_cache);
33723246
33733247 const is_exe_or_dyn_lib = switch (create_module.resolved_options.output_mode) {
33743248 .Obj => false,
......@@ -3378,7 +3252,7 @@ fn buildOutputType(
33783252 // Note that cmake when targeting Windows will try to execute
33793253 // zig cc to make an executable and output an implib too.
33803254 const implib_eligible = is_exe_or_dyn_lib and
3381 emit_bin_loc != null and target.os.tag == .windows;
3255 emit_bin_resolved != .no and target.os.tag == .windows;
33823256 if (!implib_eligible) {
33833257 if (!emit_implib_arg_provided) {
33843258 emit_implib = .no;
......@@ -3387,22 +3261,18 @@ fn buildOutputType(
33873261 }
33883262 }
33893263 const default_implib_basename = try std.fmt.allocPrint(arena, "{s}.lib", .{root_name});
3390 var emit_implib_resolved = switch (emit_implib) {
3391 .no => Emit.Resolved{ .data = null, .dir = null },
3392 .yes => |p| emit_implib.resolve(default_implib_basename, output_to_cache) catch |err| {
3393 fatal("unable to open directory from argument '-femit-implib', '{s}': {s}", .{
3394 p, @errorName(err),
3264 const emit_implib_resolved: Compilation.CreateOptions.Emit = switch (emit_implib) {
3265 .no => .no,
3266 .yes => emit_implib.resolve(default_implib_basename, output_to_cache),
3267 .yes_default_path => emit: {
3268 if (output_to_cache != null) break :emit .yes_cache;
3269 const p = try fs.path.join(arena, &.{
3270 fs.path.dirname(emit_bin_resolved.yes_path) orelse ".",
3271 default_implib_basename,
33953272 });
3396 },
3397 .yes_default_path => Emit.Resolved{
3398 .data = Compilation.EmitLoc{
3399 .directory = emit_bin_loc.?.directory,
3400 .basename = default_implib_basename,
3401 },
3402 .dir = null,
3273 break :emit .{ .yes_path = p };
34033274 },
34043275 };
3405 defer emit_implib_resolved.deinit();
34063276
34073277 var thread_pool: ThreadPool = undefined;
34083278 try thread_pool.init(.{
......@@ -3456,7 +3326,7 @@ fn buildOutputType(
34563326 src.src_path = try dirs.local_cache.join(arena, &.{sub_path});
34573327 }
34583328
3459 if (build_options.have_llvm and emit_asm != .no) {
3329 if (build_options.have_llvm and emit_asm_resolved != .no) {
34603330 // LLVM has no way to set this non-globally.
34613331 const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" };
34623332 @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv);
......@@ -3472,23 +3342,11 @@ fn buildOutputType(
34723342 fatal("--debug-incremental requires -fincremental", .{});
34733343 }
34743344
3475 const disable_lld_caching = !output_to_cache;
3476
34773345 const cache_mode: Compilation.CacheMode = b: {
3346 // Once incremental compilation is the default, we'll want some smarter logic here,
3347 // considering things like the backend in use and whether there's a ZCU.
3348 if (output_to_cache == null) break :b .none;
34783349 if (incremental) break :b .incremental;
3479 if (disable_lld_caching) break :b .incremental;
3480 if (!create_module.resolved_options.have_zcu) break :b .whole;
3481
3482 // TODO: once we support incremental compilation for the LLVM backend
3483 // via saving the LLVM module into a bitcode file and restoring it,
3484 // along with compiler state, this clause can be removed so that
3485 // incremental cache mode is used for LLVM backend too.
3486 if (create_module.resolved_options.use_llvm) break :b .whole;
3487
3488 // Eventually, this default should be `.incremental`. However, since incremental
3489 // compilation is currently an opt-in feature, it makes a strictly worse default cache mode
3490 // than `.whole`.
3491 // https://github.com/ziglang/zig/issues/21165
34923350 break :b .whole;
34933351 };
34943352
......@@ -3510,13 +3368,13 @@ fn buildOutputType(
35103368 .main_mod = main_mod,
35113369 .root_mod = root_mod,
35123370 .std_mod = std_mod,
3513 .emit_bin = emit_bin_loc,
3514 .emit_h = emit_h_resolved.data,
3515 .emit_asm = emit_asm_resolved.data,
3516 .emit_llvm_ir = emit_llvm_ir_resolved.data,
3517 .emit_llvm_bc = emit_llvm_bc_resolved.data,
3518 .emit_docs = emit_docs_resolved.data,
3519 .emit_implib = emit_implib_resolved.data,
3371 .emit_bin = emit_bin_resolved,
3372 .emit_h = emit_h_resolved,
3373 .emit_asm = emit_asm_resolved,
3374 .emit_llvm_ir = emit_llvm_ir_resolved,
3375 .emit_llvm_bc = emit_llvm_bc_resolved,
3376 .emit_docs = emit_docs_resolved,
3377 .emit_implib = emit_implib_resolved,
35203378 .lib_directories = create_module.lib_directories.items,
35213379 .rpath_list = create_module.rpath_list.items,
35223380 .symbol_wrap_set = symbol_wrap_set,
......@@ -3599,7 +3457,6 @@ fn buildOutputType(
35993457 .test_filters = test_filters.items,
36003458 .test_name_prefix = test_name_prefix,
36013459 .test_runner_path = test_runner_path,
3602 .disable_lld_caching = disable_lld_caching,
36033460 .cache_mode = cache_mode,
36043461 .subsystem = subsystem,
36053462 .debug_compile_errors = debug_compile_errors,
......@@ -3744,13 +3601,8 @@ fn buildOutputType(
37443601 }) {
37453602 dev.checkAny(&.{ .run_command, .test_command });
37463603
3747 if (test_exec_args.items.len == 0 and target.ofmt == .c) default_exec_args: {
3604 if (test_exec_args.items.len == 0 and target.ofmt == .c and emit_bin_resolved != .no) {
37483605 // Default to using `zig run` to execute the produced .c code from `zig test`.
3749 const c_code_loc = emit_bin_loc orelse break :default_exec_args;
3750 const c_code_directory = c_code_loc.directory orelse comp.bin_file.?.emit.root_dir;
3751 const c_code_path = try fs.path.join(arena, &[_][]const u8{
3752 c_code_directory.path orelse ".", c_code_loc.basename,
3753 });
37543606 try test_exec_args.appendSlice(arena, &.{ self_exe_path, "run" });
37553607 if (dirs.zig_lib.path) |p| {
37563608 try test_exec_args.appendSlice(arena, &.{ "-I", p });
......@@ -3775,7 +3627,7 @@ fn buildOutputType(
37753627 if (create_module.dynamic_linker) |dl| {
37763628 try test_exec_args.appendSlice(arena, &.{ "--dynamic-linker", dl });
37773629 }
3778 try test_exec_args.append(arena, c_code_path);
3630 try test_exec_args.append(arena, null); // placeholder for the path of the emitted C source file
37793631 }
37803632
37813633 try runOrTest(
......@@ -4354,12 +4206,22 @@ fn runOrTest(
43544206 runtime_args_start: ?usize,
43554207 link_libc: bool,
43564208) !void {
4357 const lf = comp.bin_file orelse return;
4358 // A naive `directory.join` here will indeed get the correct path to the binary,
4359 // however, in the case of cwd, we actually want `./foo` so that the path can be executed.
4360 const exe_path = try fs.path.join(arena, &[_][]const u8{
4361 lf.emit.root_dir.path orelse ".", lf.emit.sub_path,
4362 });
4209 const raw_emit_bin = comp.emit_bin orelse return;
4210 const exe_path = switch (comp.cache_use) {
4211 .none => p: {
4212 if (fs.path.isAbsolute(raw_emit_bin)) break :p raw_emit_bin;
4213 // Use `fs.path.join` to make a file in the cwd is still executed properly.
4214 break :p try fs.path.join(arena, &.{
4215 ".",
4216 raw_emit_bin,
4217 });
4218 },
4219 .whole, .incremental => try comp.dirs.local_cache.join(arena, &.{
4220 "o",
4221 &Cache.binToHex(comp.digest.?),
4222 raw_emit_bin,
4223 }),
4224 };
43634225
43644226 var argv = std.ArrayList([]const u8).init(gpa);
43654227 defer argv.deinit();
......@@ -5087,16 +4949,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
50874949 };
50884950 };
50894951
5090 const exe_basename = try std.zig.binNameAlloc(arena, .{
5091 .root_name = "build",
5092 .target = resolved_target.result,
5093 .output_mode = .Exe,
5094 });
5095 const emit_bin: Compilation.EmitLoc = .{
5096 .directory = null, // Use the local zig-cache.
5097 .basename = exe_basename,
5098 };
5099
51004952 process.raiseFileDescriptorLimit();
51014953
51024954 const cwd_path = try introspect.getResolvedCwd(arena);
......@@ -5357,8 +5209,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
53575209 .config = config,
53585210 .root_mod = root_mod,
53595211 .main_mod = build_mod,
5360 .emit_bin = emit_bin,
5361 .emit_h = null,
5212 .emit_bin = .yes_cache,
53625213 .self_exe_path = self_exe_path,
53635214 .thread_pool = &thread_pool,
53645215 .verbose_cc = verbose_cc,
......@@ -5386,8 +5237,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
53865237 // Since incremental compilation isn't done yet, we use cache_mode = whole
53875238 // above, and thus the output file is already closed.
53885239 //try comp.makeBinFileExecutable();
5389 child_argv.items[argv_index_exe] =
5390 try dirs.local_cache.join(arena, &.{comp.cache_use.whole.bin_sub_path.?});
5240 child_argv.items[argv_index_exe] = try dirs.local_cache.join(arena, &.{
5241 "o",
5242 &Cache.binToHex(comp.digest.?),
5243 comp.emit_bin.?,
5244 });
53915245 }
53925246
53935247 if (process.can_spawn) {
......@@ -5504,16 +5358,6 @@ fn jitCmd(
55045358 .is_explicit_dynamic_linker = false,
55055359 };
55065360
5507 const exe_basename = try std.zig.binNameAlloc(arena, .{
5508 .root_name = options.cmd_name,
5509 .target = resolved_target.result,
5510 .output_mode = .Exe,
5511 });
5512 const emit_bin: Compilation.EmitLoc = .{
5513 .directory = null, // Use the global zig-cache.
5514 .basename = exe_basename,
5515 };
5516
55175361 const self_exe_path = fs.selfExePathAlloc(arena) catch |err| {
55185362 fatal("unable to find self exe path: {s}", .{@errorName(err)});
55195363 };
......@@ -5605,8 +5449,7 @@ fn jitCmd(
56055449 .config = config,
56065450 .root_mod = root_mod,
56075451 .main_mod = root_mod,
5608 .emit_bin = emit_bin,
5609 .emit_h = null,
5452 .emit_bin = .yes_cache,
56105453 .self_exe_path = self_exe_path,
56115454 .thread_pool = &thread_pool,
56125455 .cache_mode = .whole,
......@@ -5637,7 +5480,11 @@ fn jitCmd(
56375480 };
56385481 }
56395482
5640 const exe_path = try dirs.global_cache.join(arena, &.{comp.cache_use.whole.bin_sub_path.?});
5483 const exe_path = try dirs.global_cache.join(arena, &.{
5484 "o",
5485 &Cache.binToHex(comp.digest.?),
5486 comp.emit_bin.?,
5487 });
56415488 child_argv.appendAssumeCapacity(exe_path);
56425489 }
56435490
tools/incr-check.zig+1-1
......@@ -314,7 +314,7 @@ const Eval = struct {
314314 const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len];
315315 const result_dir = ".local-cache" ++ std.fs.path.sep_str ++ "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*);
316316
317 const bin_name = try std.zig.binNameAlloc(arena, .{
317 const bin_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{
318318 .root_name = "root", // corresponds to the module name "root"
319319 .target = eval.target.resolved,
320320 .output_mode = .Exe,