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 {...@@ -1834,47 +1834,16 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
1834 lp.path = b.fmt("{}", .{output_dir});1834 lp.path = b.fmt("{}", .{output_dir});
1835 }1835 }
18361836
1837 // -femit-bin[=path] (default) Output machine code1837 // zig fmt: off
1838 if (compile.generated_bin) |bin| {1838 if (compile.generated_bin) |lp| lp.path = compile.outputPath(output_dir, .bin);
1839 bin.path = output_dir.joinString(b.allocator, compile.out_filename) catch @panic("OOM");1839 if (compile.generated_pdb) |lp| lp.path = compile.outputPath(output_dir, .pdb);
1840 }1840 if (compile.generated_implib) |lp| lp.path = compile.outputPath(output_dir, .implib);
18411841 if (compile.generated_h) |lp| lp.path = compile.outputPath(output_dir, .h);
1842 const sep = std.fs.path.sep_str;1842 if (compile.generated_docs) |lp| lp.path = compile.outputPath(output_dir, .docs);
18431843 if (compile.generated_asm) |lp| lp.path = compile.outputPath(output_dir, .@"asm");
1844 // output PDB if someone requested it1844 if (compile.generated_llvm_ir) |lp| lp.path = compile.outputPath(output_dir, .llvm_ir);
1845 if (compile.generated_pdb) |pdb| {1845 if (compile.generated_llvm_bc) |lp| lp.path = compile.outputPath(output_dir, .llvm_bc);
1846 pdb.path = b.fmt("{}" ++ sep ++ "{s}.pdb", .{ output_dir, compile.name });1846 // zig fmt: on
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 }
1878 }1847 }
18791848
1880 if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic and1849 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 {...@@ -1888,6 +1857,21 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
1888 );1857 );
1889 }1858 }
1890}1859}
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
1892pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path {1876pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path {
1893 const gpa = c.step.owner.allocator;1877 const gpa = c.step.owner.allocator;
lib/std/zig.zig+29
...@@ -884,6 +884,35 @@ pub const SimpleComptimeReason = enum(u32) {...@@ -884,6 +884,35 @@ pub const SimpleComptimeReason = enum(u32) {
884 }884 }
885};885};
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
887test {916test {
888 _ = Ast;917 _ = Ast;
889 _ = AstRlAnnotate;918 _ = AstRlAnnotate;
src/Compilation.zig+366-352
...@@ -55,8 +55,7 @@ gpa: Allocator,...@@ -55,8 +55,7 @@ gpa: Allocator,
55arena: Allocator,55arena: Allocator,
56/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.56/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
57zcu: ?*Zcu,57zcu: ?*Zcu,
58/// Contains different state depending on whether the Compilation uses58/// Contains different state depending on the `CacheMode` used by this `Compilation`.
59/// incremental or whole cache mode.
60cache_use: CacheUse,59cache_use: CacheUse,
61/// All compilations have a root module because this is where some important60/// All compilations have a root module because this is where some important
62/// settings are stored, such as target and optimization mode. This module61/// settings are stored, such as target and optimization mode. This module
...@@ -67,17 +66,13 @@ root_mod: *Package.Module,...@@ -67,17 +66,13 @@ root_mod: *Package.Module,
67config: Config,66config: Config,
6867
69/// The main output file.68/// The main output file.
70/// In whole cache mode, this is null except for during the body of the update69/// In `CacheMode.whole`, this is null except for during the body of `update`.
71/// function. In incremental cache mode, this is a long-lived object.70/// In `CacheMode.none` and `CacheMode.incremental`, this is long-lived.
72/// In both cases, this is `null` when `-fno-emit-bin` is used.71/// Regardless of cache mode, this is `null` when `-fno-emit-bin` is used.
73bin_file: ?*link.File,72bin_file: ?*link.File,
7473
75/// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin)74/// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin)
76sysroot: ?[]const u8,75sysroot: ?[]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,
81root_name: [:0]const u8,76root_name: [:0]const u8,
82compiler_rt_strat: RtStrat,77compiler_rt_strat: RtStrat,
83ubsan_rt_strat: RtStrat,78ubsan_rt_strat: RtStrat,
...@@ -259,10 +254,6 @@ mutex: if (builtin.single_threaded) struct {...@@ -259,10 +254,6 @@ mutex: if (builtin.single_threaded) struct {
259test_filters: []const []const u8,254test_filters: []const []const u8,
260test_name_prefix: ?[]const u8,255test_name_prefix: ?[]const u8,
261256
262emit_asm: ?EmitLoc,
263emit_llvm_ir: ?EmitLoc,
264emit_llvm_bc: ?EmitLoc,
265
266link_task_wait_group: WaitGroup = .{},257link_task_wait_group: WaitGroup = .{},
267work_queue_progress_node: std.Progress.Node = .none,258work_queue_progress_node: std.Progress.Node = .none,
268259
...@@ -274,6 +265,31 @@ file_system_inputs: ?*std.ArrayListUnmanaged(u8),...@@ -274,6 +265,31 @@ file_system_inputs: ?*std.ArrayListUnmanaged(u8),
274/// This digest will be known after update() is called.265/// This digest will be known after update() is called.
275digest: ?[Cache.bin_digest_len]u8 = null,266digest: ?[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
277const QueuedJobs = struct {293const QueuedJobs = struct {
278 compiler_rt_lib: bool = false,294 compiler_rt_lib: bool = false,
279 compiler_rt_obj: bool = false,295 compiler_rt_obj: bool = false,
...@@ -774,13 +790,6 @@ pub const CrtFile = struct {...@@ -774,13 +790,6 @@ pub const CrtFile = struct {
774 lock: Cache.Lock,790 lock: Cache.Lock,
775 full_object_path: Cache.Path,791 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
784 pub fn deinit(self: *CrtFile, gpa: Allocator) void {793 pub fn deinit(self: *CrtFile, gpa: Allocator) void {
785 self.lock.release();794 self.lock.release();
786 gpa.free(self.full_object_path.sub_path);795 gpa.free(self.full_object_path.sub_path);
...@@ -1321,14 +1330,6 @@ pub const MiscError = struct {...@@ -1321,14 +1330,6 @@ pub const MiscError = struct {
1321 }1330 }
1322};1331};
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
1332pub const cache_helpers = struct {1333pub const cache_helpers = struct {
1333 pub fn addModule(hh: *Cache.HashHelper, mod: *const Package.Module) void {1334 pub fn addModule(hh: *Cache.HashHelper, mod: *const Package.Module) void {
1334 addResolvedTarget(hh, mod.resolved_target);1335 addResolvedTarget(hh, mod.resolved_target);
...@@ -1368,15 +1369,6 @@ pub const cache_helpers = struct {...@@ -1368,15 +1369,6 @@ pub const cache_helpers = struct {
1368 hh.add(resolved_target.is_explicit_dynamic_linker);1369 hh.add(resolved_target.is_explicit_dynamic_linker);
1369 }1370 }
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
1380 pub fn addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?Config.DebugFormat) void {1372 pub fn addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?Config.DebugFormat) void {
1381 hh.add(x != null);1373 hh.add(x != null);
1382 addDebugFormat(hh, x orelse return);1374 addDebugFormat(hh, x orelse return);
...@@ -1423,7 +1415,38 @@ pub const ClangPreprocessorMode = enum {...@@ -1423,7 +1415,38 @@ pub const ClangPreprocessorMode = enum {
1423pub const Framework = link.File.MachO.Framework;1415pub const Framework = link.File.MachO.Framework;
1424pub const SystemLib = link.SystemLib;1416pub 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
1428pub const ParentWholeCache = struct {1451pub const ParentWholeCache = struct {
1429 manifest: *Cache.Manifest,1452 manifest: *Cache.Manifest,
...@@ -1432,22 +1455,33 @@ pub const ParentWholeCache = struct {...@@ -1432,22 +1455,33 @@ pub const ParentWholeCache = struct {
1432};1455};
14331456
1434const CacheUse = union(CacheMode) {1457const CacheUse = union(CacheMode) {
1458 none: *None,
1435 incremental: *Incremental,1459 incremental: *Incremental,
1436 whole: *Whole,1460 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
1438 const Whole = struct {1476 const Whole = struct {
1439 /// This is a pointer to a local variable inside `update()`.1477 /// Since we don't open the output file until `update`, we must save these options for then.
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,
1450 lf_open_opts: link.File.OpenOptions,1478 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`.
1451 tmp_artifact_directory: ?Cache.Directory,1485 tmp_artifact_directory: ?Cache.Directory,
1452 /// Prevents other processes from clobbering files in the output directory.1486 /// Prevents other processes from clobbering files in the output directory.
1453 lock: ?Cache.Lock,1487 lock: ?Cache.Lock,
...@@ -1466,17 +1500,16 @@ const CacheUse = union(CacheMode) {...@@ -1466,17 +1500,16 @@ const CacheUse = union(CacheMode) {
1466 }1500 }
1467 };1501 };
14681502
1469 const Incremental = struct {
1470 /// Where build artifacts and incremental compilation metadata serialization go.
1471 artifact_directory: Cache.Directory,
1472 };
1473
1474 fn deinit(cu: CacheUse) void {1503 fn deinit(cu: CacheUse) void {
1475 switch (cu) {1504 switch (cu) {
1505 .none => |none| {
1506 assert(none.tmp_artifact_directory == null);
1507 },
1476 .incremental => |incremental| {1508 .incremental => |incremental| {
1477 incremental.artifact_directory.handle.close();1509 incremental.artifact_directory.handle.close();
1478 },1510 },
1479 .whole => |whole| {1511 .whole => |whole| {
1512 assert(whole.tmp_artifact_directory == null);
1480 whole.releaseLock();1513 whole.releaseLock();
1481 },1514 },
1482 }1515 }
...@@ -1503,28 +1536,14 @@ pub const CreateOptions = struct {...@@ -1503,28 +1536,14 @@ pub const CreateOptions = struct {
1503 std_mod: ?*Package.Module = null,1536 std_mod: ?*Package.Module = null,
1504 root_name: []const u8,1537 root_name: []const u8,
1505 sysroot: ?[]const u8 = null,1538 sysroot: ?[]const u8 = null,
1506 /// `null` means to not emit a binary file.1539 cache_mode: CacheMode,
1507 emit_bin: ?EmitLoc,1540 emit_h: Emit = .no,
1508 /// `null` means to not emit a C header file.1541 emit_bin: Emit,
1509 emit_h: ?EmitLoc = null,1542 emit_asm: Emit = .no,
1510 /// `null` means to not emit assembly.1543 emit_implib: Emit = .no,
1511 emit_asm: ?EmitLoc = null,1544 emit_llvm_ir: Emit = .no,
1512 /// `null` means to not emit LLVM IR.1545 emit_llvm_bc: Emit = .no,
1513 emit_llvm_ir: ?EmitLoc = null,1546 emit_docs: Emit = .no,
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,
1528 /// This field is intended to be removed.1547 /// This field is intended to be removed.
1529 /// The ELF implementation no longer uses this data, however the MachO and COFF1548 /// The ELF implementation no longer uses this data, however the MachO and COFF
1530 /// implementations still do.1549 /// implementations still do.
...@@ -1662,6 +1681,38 @@ pub const CreateOptions = struct {...@@ -1662,6 +1681,38 @@ pub const CreateOptions = struct {
1662 parent_whole_cache: ?ParentWholeCache = null,1681 parent_whole_cache: ?ParentWholeCache = null,
16631682
1664 pub const Entry = link.File.OpenOptions.Entry;1683 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 };
1665};1716};
16661717
1667fn addModuleTableToCacheHash(1718fn addModuleTableToCacheHash(
...@@ -1869,13 +1920,18 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1869,13 +1920,18 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1869 cache.hash.add(options.config.link_libunwind);1920 cache.hash.add(options.config.link_libunwind);
1870 cache.hash.add(output_mode);1921 cache.hash.add(output_mode);
1871 cache_helpers.addDebugFormat(&cache.hash, options.config.debug_format);1922 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);
1875 cache.hash.addBytes(options.root_name);1923 cache.hash.addBytes(options.root_name);
1876 cache.hash.add(options.config.wasi_exec_model);1924 cache.hash.add(options.config.wasi_exec_model);
1877 cache.hash.add(options.config.san_cov_trace_pc_guard);1925 cache.hash.add(options.config.san_cov_trace_pc_guard);
1878 cache.hash.add(options.debug_compiler_runtime_libs);1926 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);
1879 // TODO audit this and make sure everything is in it1935 // TODO audit this and make sure everything is in it
18801936
1881 const main_mod = options.main_mod orelse options.root_mod;1937 const main_mod = options.main_mod orelse options.root_mod;
...@@ -1925,7 +1981,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1925,7 +1981,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1925 try zcu.init(options.thread_pool.getIdCount());1981 try zcu.init(options.thread_pool.getIdCount());
1926 break :blk zcu;1982 break :blk zcu;
1927 } else blk: {1983 } else blk: {
1928 if (options.emit_h != null) return error.NoZigModuleForCHeader;1984 if (options.emit_h != .no) return error.NoZigModuleForCHeader;
1929 break :blk null;1985 break :blk null;
1930 };1986 };
1931 errdefer if (opt_zcu) |zcu| zcu.deinit();1987 errdefer if (opt_zcu) |zcu| zcu.deinit();
...@@ -1938,18 +1994,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1938,18 +1994,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1938 .arena = arena,1994 .arena = arena,
1939 .zcu = opt_zcu,1995 .zcu = opt_zcu,
1940 .cache_use = undefined, // populated below1996 .cache_use = undefined, // populated below
1941 .bin_file = null, // populated below1997 .bin_file = null, // populated below if necessary
1942 .implib_emit = null, // handled below
1943 .docs_emit = null, // handled below
1944 .root_mod = options.root_mod,1998 .root_mod = options.root_mod,
1945 .config = options.config,1999 .config = options.config,
1946 .dirs = options.dirs,2000 .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,
1950 .work_queues = @splat(.init(gpa)),2001 .work_queues = @splat(.init(gpa)),
1951 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),2002 .c_object_work_queue = .init(gpa),
1952 .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{},2003 .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) .init(gpa) else .{},
1953 .c_source_files = options.c_source_files,2004 .c_source_files = options.c_source_files,
1954 .rc_source_files = options.rc_source_files,2005 .rc_source_files = options.rc_source_files,
1955 .cache_parent = cache,2006 .cache_parent = cache,
...@@ -2002,6 +2053,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -2002,6 +2053,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
2002 .file_system_inputs = options.file_system_inputs,2053 .file_system_inputs = options.file_system_inputs,
2003 .parent_whole_cache = options.parent_whole_cache,2054 .parent_whole_cache = options.parent_whole_cache,
2004 .link_diags = .init(gpa),2055 .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),
2005 };2062 };
20062063
2007 // Prevent some footguns by making the "any" fields of config reflect2064 // 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...@@ -2068,7 +2125,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
2068 .soname = options.soname,2125 .soname = options.soname,
2069 .compatibility_version = options.compatibility_version,2126 .compatibility_version = options.compatibility_version,
2070 .build_id = build_id,2127 .build_id = build_id,
2071 .disable_lld_caching = options.disable_lld_caching or options.cache_mode == .whole,
2072 .subsystem = options.subsystem,2128 .subsystem = options.subsystem,
2073 .hash_style = options.hash_style,2129 .hash_style = options.hash_style,
2074 .enable_link_snapshots = options.enable_link_snapshots,2130 .enable_link_snapshots = options.enable_link_snapshots,
...@@ -2087,6 +2143,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -2087,6 +2143,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
2087 };2143 };
20882144
2089 switch (options.cache_mode) {2145 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 },
2090 .incremental => {2157 .incremental => {
2091 // Options that are specific to zig source files, that cannot be2158 // Options that are specific to zig source files, that cannot be
2092 // modified between incremental updates.2159 // modified between incremental updates.
...@@ -2100,7 +2167,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -2100,7 +2167,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
2100 hash.addListOfBytes(options.test_filters);2167 hash.addListOfBytes(options.test_filters);
2101 hash.addOptionalBytes(options.test_name_prefix);2168 hash.addOptionalBytes(options.test_name_prefix);
2102 hash.add(options.skip_linker_dependencies);2169 hash.add(options.skip_linker_dependencies);
2103 hash.add(options.emit_h != null);2170 hash.add(options.emit_h != .no);
2104 hash.add(error_limit);2171 hash.add(error_limit);
21052172
2106 // Here we put the root source file path name, but *not* with addFile.2173 // 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...@@ -2135,49 +2202,26 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
2135 };2202 };
2136 comp.cache_use = .{ .incremental = incremental };2203 comp.cache_use = .{ .incremental = incremental };
21372204
2138 if (options.emit_bin) |emit_bin| {2205 if (comp.emit_bin) |cache_rel_path| {
2139 const emit: Cache.Path = .{2206 const emit: Cache.Path = .{
2140 .root_dir = emit_bin.directory orelse artifact_directory,2207 .root_dir = artifact_directory,
2141 .sub_path = emit_bin.basename,2208 .sub_path = cache_rel_path,
2142 };2209 };
2143 comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts);2210 comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts);
2144 }2211 }
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 }
2159 },2212 },
2160 .whole => {2213 .whole => {
2161 // For whole cache mode, we don't know where to put outputs from2214 // For whole cache mode, we don't know where to put outputs from the linker until
2162 // the linker until the final cache hash, which is available after2215 // the final cache hash, which is available after the compilation is complete.
2163 // the compilation is complete.
2164 //2216 //
2165 // Therefore, bin_file is left null until the beginning of update(),2217 // Therefore, `comp.bin_file` is left `null` (already done) until `update`, where
2166 // where it may find a cache hit, or use a temporary directory to2218 // it may find a cache hit, or else will use a temporary directory to hold output
2167 // hold output artifacts.2219 // artifacts.
2168 const whole = try arena.create(CacheUse.Whole);2220 const whole = try arena.create(CacheUse.Whole);
2169 whole.* = .{2221 whole.* = .{
2170 // This is kept here so that link.File.open can be called later.
2171 .lf_open_opts = lf_open_opts,2222 .lf_open_opts = lf_open_opts,
2172 // This is so that when doing `CacheMode.whole`, the mechanism in update()2223 .cache_manifest = null,
2173 // can use it for communicating the result directory via `bin_file.emit`.2224 .cache_manifest_mutex = .{},
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),
2181 .tmp_artifact_directory = null,2225 .tmp_artifact_directory = null,
2182 .lock = null,2226 .lock = null,
2183 };2227 };
...@@ -2245,12 +2289,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -2245,12 +2289,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
2245 }2289 }
2246 }2290 }
22472291
2248 const have_bin_emit = switch (comp.cache_use) {2292 if (comp.emit_bin != null and target.ofmt != .c) {
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) {
2254 if (!comp.skip_linker_dependencies) {2293 if (!comp.skip_linker_dependencies) {
2255 // If we need to build libc for the target, add work items for it.2294 // If we need to build libc for the target, add work items for it.
2256 // We go through the work queue so that building can be done in parallel.2295 // We go through the work queue so that building can be done in parallel.
...@@ -2544,8 +2583,23 @@ pub fn hotCodeSwap(...@@ -2544,8 +2583,23 @@ pub fn hotCodeSwap(
2544 try lf.makeExecutable();2583 try lf.makeExecutable();
2545}2584}
25462585
2547fn cleanupAfterUpdate(comp: *Compilation) void {2586fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
2548 switch (comp.cache_use) {2587 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 },
2549 .incremental => return,2603 .incremental => return,
2550 .whole => |whole| {2604 .whole => |whole| {
2551 if (whole.cache_manifest) |man| {2605 if (whole.cache_manifest) |man| {
...@@ -2556,10 +2610,18 @@ fn cleanupAfterUpdate(comp: *Compilation) void {...@@ -2556,10 +2610,18 @@ fn cleanupAfterUpdate(comp: *Compilation) void {
2556 lf.destroy();2610 lf.destroy();
2557 comp.bin_file = null;2611 comp.bin_file = null;
2558 }2612 }
2559 if (whole.tmp_artifact_directory) |*directory| {2613 if (whole.tmp_artifact_directory) |*tmp_dir| {
2560 directory.handle.close();2614 tmp_dir.handle.close();
2561 if (directory.path) |p| comp.gpa.free(p);
2562 whole.tmp_artifact_directory = null;2615 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 };
2563 }2625 }
2564 },2626 },
2565 }2627 }
...@@ -2579,14 +2641,27 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2579,14 +2641,27 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2579 comp.clearMiscFailures();2641 comp.clearMiscFailures();
2580 comp.last_update_was_cache_hit = false;2642 comp.last_update_was_cache_hit = false;
25812643
2582 var man: Cache.Manifest = undefined;
2583 defer cleanupAfterUpdate(comp);
2584
2585 var tmp_dir_rand_int: u64 = undefined;2644 var tmp_dir_rand_int: u64 = undefined;
2645 var man: Cache.Manifest = undefined;
2646 defer cleanupAfterUpdate(comp, tmp_dir_rand_int);
25862647
2587 // If using the whole caching strategy, we check for *everything* up front, including2648 // If using the whole caching strategy, we check for *everything* up front, including
2588 // C source files.2649 // C source files.
2650 log.debug("Compilation.update for {s}, CacheMode.{s}", .{ comp.root_name, @tagName(comp.cache_use) });
2589 switch (comp.cache_use) {2651 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 => {},
2590 .whole => |whole| {2665 .whole => |whole| {
2591 assert(comp.bin_file == null);2666 assert(comp.bin_file == null);
2592 // We are about to obtain this lock, so here we give other processes a chance first.2667 // 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 {...@@ -2633,10 +2708,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2633 comp.last_update_was_cache_hit = true;2708 comp.last_update_was_cache_hit = true;
2634 log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name});2709 log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name});
2635 const bin_digest = man.finalBin();2710 const bin_digest = man.finalBin();
2636 const hex_digest = Cache.binToHex(bin_digest);
26372711
2638 comp.digest = bin_digest;2712 comp.digest = bin_digest;
2639 comp.wholeCacheModeSetBinFilePath(whole, &hex_digest);
26402713
2641 assert(whole.lock == null);2714 assert(whole.lock == null);
2642 whole.lock = man.toOwnedLock();2715 whole.lock = man.toOwnedLock();
...@@ -2645,52 +2718,23 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2645,52 +2718,23 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2645 log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name});2718 log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name});
26462719
2647 // Compile the artifacts to a temporary directory.2720 // Compile the artifacts to a temporary directory.
2648 const tmp_artifact_directory: Cache.Directory = d: {2721 whole.tmp_artifact_directory = d: {
2649 const s = std.fs.path.sep_str;
2650 tmp_dir_rand_int = std.crypto.random.int(u64);2722 tmp_dir_rand_int = std.crypto.random.int(u64);
2651 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int);2723 const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
26522724 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
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
2659 break :d .{2725 break :d .{
2660 .path = path,2726 .path = path,
2661 .handle = handle,2727 .handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}),
2662 };2728 };
2663 };2729 };
2664 whole.tmp_artifact_directory = tmp_artifact_directory;2730 if (comp.emit_bin) |sub_path| {
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| {
2684 const emit: Cache.Path = .{2731 const emit: Cache.Path = .{
2685 .root_dir = tmp_artifact_directory,2732 .root_dir = whole.tmp_artifact_directory.?,
2686 .sub_path = std.fs.path.basename(sub_path),2733 .sub_path = sub_path,
2687 };2734 };
2688 comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts);2735 comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts);
2689 }2736 }
2690 },2737 },
2691 .incremental => {
2692 log.debug("Compilation.update for {s}, CacheMode.incremental", .{comp.root_name});
2693 },
2694 }2738 }
26952739
2696 // From this point we add a preliminary set of file system inputs that2740 // 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 {...@@ -2789,11 +2833,18 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2789 return;2833 return;
2790 }2834 }
27912835
2792 // Flush below handles -femit-bin but there is still -femit-llvm-ir,2836 if (comp.zcu == null and comp.config.output_mode == .Obj and comp.c_object_table.count() == 1) {
2793 // -femit-llvm-bc, and -femit-asm, in the case of C objects.2837 // This is `zig build-obj foo.c`. We can emit asm and LLVM IR/bitcode.
2794 comp.emitOthers();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
2796 switch (comp.cache_use) {2844 switch (comp.cache_use) {
2845 .none, .incremental => {
2846 try flush(comp, arena, .main, main_progress_node);
2847 },
2797 .whole => |whole| {2848 .whole => |whole| {
2798 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);2849 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
2799 if (comp.parent_whole_cache) |pwc| {2850 if (comp.parent_whole_cache) |pwc| {
...@@ -2805,18 +2856,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2805,18 +2856,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2805 const bin_digest = man.finalBin();2856 const bin_digest = man.finalBin();
2806 const hex_digest = Cache.binToHex(bin_digest);2857 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
2820 // Work around windows `AccessDenied` if any files within this2859 // Work around windows `AccessDenied` if any files within this
2821 // directory are open by closing and reopening the file handles.2860 // directory are open by closing and reopening the file handles.
2822 const need_writable_dance: enum { no, lf_only, lf_and_debug } = w: {2861 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 {...@@ -2841,6 +2880,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2841 break :w .no;2880 break :w .no;
2842 };2881 };
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;
2844 renameTmpIntoCache(comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| {2890 renameTmpIntoCache(comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| {
2845 return comp.setMiscFailure(2891 return comp.setMiscFailure(
2846 .rename_results,2892 .rename_results,
...@@ -2853,7 +2899,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2853,7 +2899,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2853 );2899 );
2854 };2900 };
2855 comp.digest = bin_digest;2901 comp.digest = bin_digest;
2856 comp.wholeCacheModeSetBinFilePath(whole, &hex_digest);
28572902
2858 // The linker flush functions need to know the final output path2903 // The linker flush functions need to know the final output path
2859 // for debug info purposes because executable debug info contains2904 // 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 {...@@ -2861,10 +2906,9 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2861 if (comp.bin_file) |lf| {2906 if (comp.bin_file) |lf| {
2862 lf.emit = .{2907 lf.emit = .{
2863 .root_dir = comp.dirs.local_cache,2908 .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.? }),
2865 };2910 };
28662911
2867 // Has to be after the `wholeCacheModeSetBinFilePath` above.
2868 switch (need_writable_dance) {2912 switch (need_writable_dance) {
2869 .no => {},2913 .no => {},
2870 .lf_only => try lf.makeWritable(),2914 .lf_only => try lf.makeWritable(),
...@@ -2875,10 +2919,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2875,10 +2919,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2875 }2919 }
2876 }2920 }
28772921
2878 try flush(comp, arena, .{2922 try flush(comp, arena, .main, main_progress_node);
2879 .root_dir = comp.dirs.local_cache,
2880 .sub_path = o_sub_path,
2881 }, .main, main_progress_node);
28822923
2883 // Calling `flush` may have produced errors, in which case the2924 // Calling `flush` may have produced errors, in which case the
2884 // cache manifest must not be written.2925 // cache manifest must not be written.
...@@ -2897,11 +2938,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2897,11 +2938,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2897 assert(whole.lock == null);2938 assert(whole.lock == null);
2898 whole.lock = man.toOwnedLock();2939 whole.lock = man.toOwnedLock();
2899 },2940 },
2900 .incremental => |incremental| {
2901 try flush(comp, arena, .{
2902 .root_dir = incremental.artifact_directory,
2903 }, .main, main_progress_node);
2904 },
2905 }2941 }
2906}2942}
29072943
...@@ -2931,10 +2967,47 @@ pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocat...@@ -2931,10 +2967,47 @@ pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocat
2931 fsi.appendSliceAssumeCapacity(path.sub_path);2967 fsi.appendSliceAssumeCapacity(path.sub_path);
2932}2968}
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}
2934fn flush(3008fn flush(
2935 comp: *Compilation,3009 comp: *Compilation,
2936 arena: Allocator,3010 arena: Allocator,
2937 default_artifact_directory: Cache.Path,
2938 tid: Zcu.PerThread.Id,3011 tid: Zcu.PerThread.Id,
2939 prog_node: std.Progress.Node,3012 prog_node: std.Progress.Node,
2940) !void {3013) !void {
...@@ -2942,19 +3015,32 @@ fn flush(...@@ -2942,19 +3015,32 @@ fn flush(
2942 if (zcu.llvm_object) |llvm_object| {3015 if (zcu.llvm_object) |llvm_object| {
2943 // Emit the ZCU object from LLVM now; it's required to flush the output file.3016 // Emit the ZCU object from LLVM now; it's required to flush the output file.
2944 // If there's an output file, it wants to decide where the LLVM object goes!3017 // 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;
2949 const sub_prog_node = prog_node.start("LLVM Emit Object", 0);3018 const sub_prog_node = prog_node.start("LLVM Emit Object", 0);
2950 defer sub_prog_node.end();3019 defer sub_prog_node.end();
2951 try llvm_object.emit(.{3020 try llvm_object.emit(.{
2952 .pre_ir_path = comp.verbose_llvm_ir,3021 .pre_ir_path = comp.verbose_llvm_ir,
2953 .pre_bc_path = comp.verbose_llvm_bc,3022 .pre_bc_path = comp.verbose_llvm_bc,
2954 .bin_path = try resolveEmitLoc(arena, default_artifact_directory, zcu_obj_emit_loc),3023
2955 .asm_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_asm),3024 .bin_path = p: {
2956 .post_ir_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_ir),3025 const lf = comp.bin_file orelse break :p null;
2957 .post_bc_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_bc),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
2959 .is_debug = comp.root_mod.optimize_mode == .Debug,3045 .is_debug = comp.root_mod.optimize_mode == .Debug,
2960 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,3046 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,
...@@ -3025,45 +3111,6 @@ fn renameTmpIntoCache(...@@ -3025,45 +3111,6 @@ fn renameTmpIntoCache(
3025 }3111 }
3026}3112}
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
3067/// This is only observed at compile-time and used to emit a compile error3114/// This is only observed at compile-time and used to emit a compile error
3068/// to remind the programmer to update multiple related pieces of code that3115/// to remind the programmer to update multiple related pieces of code that
3069/// are in different locations. Bump this number when adding or deleting3116/// are in different locations. Bump this number when adding or deleting
...@@ -3084,7 +3131,7 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -3084,7 +3131,7 @@ fn addNonIncrementalStuffToCacheManifest(
3084 man.hash.addListOfBytes(comp.test_filters);3131 man.hash.addListOfBytes(comp.test_filters);
3085 man.hash.addOptionalBytes(comp.test_name_prefix);3132 man.hash.addOptionalBytes(comp.test_name_prefix);
3086 man.hash.add(comp.skip_linker_dependencies);3133 man.hash.add(comp.skip_linker_dependencies);
3087 //man.hash.add(zcu.emit_h != null);3134 //man.hash.add(zcu.emit_h != .no);
3088 man.hash.add(zcu.error_limit);3135 man.hash.add(zcu.error_limit);
3089 } else {3136 } else {
3090 cache_helpers.addModule(&man.hash, comp.root_mod);3137 cache_helpers.addModule(&man.hash, comp.root_mod);
...@@ -3130,10 +3177,6 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -3130,10 +3177,6 @@ fn addNonIncrementalStuffToCacheManifest(
3130 man.hash.addListOfBytes(comp.framework_dirs);3177 man.hash.addListOfBytes(comp.framework_dirs);
3131 man.hash.addListOfBytes(comp.windows_libs.keys());3178 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
3137 man.hash.addListOfBytes(comp.global_cc_argv);3180 man.hash.addListOfBytes(comp.global_cc_argv);
31383181
3139 const opts = comp.cache_use.whole.lf_open_opts;3182 const opts = comp.cache_use.whole.lf_open_opts;
...@@ -3211,54 +3254,39 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -3211,54 +3254,39 @@ fn addNonIncrementalStuffToCacheManifest(
3211 man.hash.addOptional(opts.minor_subsystem_version);3254 man.hash.addOptional(opts.minor_subsystem_version);
3212}3255}
32133256
3214fn emitOthers(comp: *Compilation) void {3257fn emitFromCObject(
3215 if (comp.config.output_mode != .Obj or comp.zcu != null or3258 comp: *Compilation,
3216 comp.c_object_table.count() == 0)3259 arena: Allocator,
3217 {3260 c_obj_path: Cache.Path,
3218 return;3261 new_ext: []const u8,
3219 }3262 unresolved_emit_path: []const u8,
3220 const obj_path = comp.c_object_table.keys()[0].status.success.object_path;3263) Allocator.Error!void {
3221 const ext = std.fs.path.extension(obj_path.sub_path);3264 // The dirname and stem (i.e. everything but the extension), of the sub path of the C object.
3222 const dirname = obj_path.sub_path[0 .. obj_path.sub_path.len - ext.len];3265 // We'll append `new_ext` to it to get the path to the right thing (asm, LLVM IR, etc).
3223 // This obj path always ends with the object file extension, but if we change the3266 const c_obj_dir_and_stem: []const u8 = p: {
3224 // extension to .ll, .bc, or .s, then it will be the path to those things.3267 const p = c_obj_path.sub_path;
3225 const outs = [_]struct {3268 const ext_len = fs.path.extension(p).len;
3226 emit: ?EmitLoc,3269 break :p p[0 .. p.len - ext_len];
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" },
3232 };3270 };
3233 for (outs) |out| {3271 const src_path: Cache.Path = .{
3234 if (out.emit) |loc| {3272 .root_dir = c_obj_path.root_dir,
3235 if (loc.directory) |directory| {3273 .sub_path = try std.fmt.allocPrint(arena, "{s}{s}", .{
3236 const src_path = std.fmt.allocPrint(comp.gpa, "{s}{s}", .{3274 c_obj_dir_and_stem,
3237 dirname, out.ext,3275 new_ext,
3238 }) catch |err| {3276 }),
3239 log.err("unable to copy {s}{s}: {s}", .{ dirname, out.ext, @errorName(err) });3277 };
3240 continue;3278 const emit_path = comp.resolveEmitPath(unresolved_emit_path);
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}
32503279
3251fn resolveEmitLoc(3280 src_path.root_dir.handle.copyFile(
3252 arena: Allocator,3281 src_path.sub_path,
3253 default_artifact_directory: Cache.Path,3282 emit_path.root_dir.handle,
3254 opt_loc: ?EmitLoc,3283 emit_path.sub_path,
3255) Allocator.Error!?[*:0]const u8 {3284 .{},
3256 const loc = opt_loc orelse return null;3285 ) catch |err| log.err("unable to copy '{}' to '{}': {s}", .{
3257 const slice = if (loc.directory) |directory|3286 src_path,
3258 try directory.joinZ(arena, &.{loc.basename})3287 emit_path,
3259 else3288 @errorName(err),
3260 try default_artifact_directory.joinStringZ(arena, loc.basename);3289 });
3261 return slice.ptr;
3262}3290}
32633291
3264/// Having the file open for writing is problematic as far as executing the3292/// Having the file open for writing is problematic as far as executing the
...@@ -4179,7 +4207,7 @@ fn performAllTheWorkInner(...@@ -4179,7 +4207,7 @@ fn performAllTheWorkInner(
41794207
4180 comp.link_task_queue.start(comp);4208 comp.link_task_queue.start(comp);
41814209
4182 if (comp.docs_emit != null) {4210 if (comp.emit_docs != null) {
4183 dev.check(.docs_emit);4211 dev.check(.docs_emit);
4184 comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp});4212 comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp});
4185 work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });4213 work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });
...@@ -4457,7 +4485,7 @@ fn performAllTheWorkInner(...@@ -4457,7 +4485,7 @@ fn performAllTheWorkInner(
4457 };4485 };
4458 }4486 }
4459 },4487 },
4460 .incremental => {},4488 .none, .incremental => {},
4461 }4489 }
44624490
4463 if (any_fatal_files or4491 if (any_fatal_files or
...@@ -4721,12 +4749,12 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void {...@@ -4721,12 +4749,12 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void {
4721 const zcu = comp.zcu orelse4749 const zcu = comp.zcu orelse
4722 return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{});4750 return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{});
47234751
4724 const emit = comp.docs_emit.?;4752 const docs_path = comp.resolveEmitPath(comp.emit_docs.?);
4725 var out_dir = emit.root_dir.handle.makeOpenPath(emit.sub_path, .{}) catch |err| {4753 var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| {
4726 return comp.lockAndSetMiscFailure(4754 return comp.lockAndSetMiscFailure(
4727 .docs_copy,4755 .docs_copy,
4728 "unable to create output directory '{}{s}': {s}",4756 "unable to create output directory '{}': {s}",
4729 .{ emit.root_dir, emit.sub_path, @errorName(err) },4757 .{ docs_path, @errorName(err) },
4730 );4758 );
4731 };4759 };
4732 defer out_dir.close();4760 defer out_dir.close();
...@@ -4745,8 +4773,8 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void {...@@ -4745,8 +4773,8 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void {
4745 var tar_file = out_dir.createFile("sources.tar", .{}) catch |err| {4773 var tar_file = out_dir.createFile("sources.tar", .{}) catch |err| {
4746 return comp.lockAndSetMiscFailure(4774 return comp.lockAndSetMiscFailure(
4747 .docs_copy,4775 .docs_copy,
4748 "unable to create '{}{s}/sources.tar': {s}",4776 "unable to create '{}/sources.tar': {s}",
4749 .{ emit.root_dir, emit.sub_path, @errorName(err) },4777 .{ docs_path, @errorName(err) },
4750 );4778 );
4751 };4779 };
4752 defer tar_file.close();4780 defer tar_file.close();
...@@ -4896,11 +4924,6 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye...@@ -4896,11 +4924,6 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
4896 .parent = root_mod,4924 .parent = root_mod,
4897 });4925 });
4898 try root_mod.deps.put(arena, "Walk", walk_mod);4926 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
4905 const sub_compilation = try Compilation.create(gpa, arena, .{4928 const sub_compilation = try Compilation.create(gpa, arena, .{
4906 .dirs = dirs,4929 .dirs = dirs,
...@@ -4912,10 +4935,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye...@@ -4912,10 +4935,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
4912 .root_name = root_name,4935 .root_name = root_name,
4913 .thread_pool = comp.thread_pool,4936 .thread_pool = comp.thread_pool,
4914 .libc_installation = comp.libc_installation,4937 .libc_installation = comp.libc_installation,
4915 .emit_bin = .{4938 .emit_bin = .yes_cache,
4916 .directory = null, // Put it in the cache directory.
4917 .basename = bin_basename,
4918 },
4919 .verbose_cc = comp.verbose_cc,4939 .verbose_cc = comp.verbose_cc,
4920 .verbose_link = comp.verbose_link,4940 .verbose_link = comp.verbose_link,
4921 .verbose_air = comp.verbose_air,4941 .verbose_air = comp.verbose_air,
...@@ -4930,27 +4950,31 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye...@@ -4930,27 +4950,31 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
49304950
4931 try comp.updateSubCompilation(sub_compilation, .docs_wasm, prog_node);4951 try comp.updateSubCompilation(sub_compilation, .docs_wasm, prog_node);
49324952
4933 const emit = comp.docs_emit.?;4953 var crt_file = try sub_compilation.toCrtFile();
4934 var out_dir = emit.root_dir.handle.makeOpenPath(emit.sub_path, .{}) catch |err| {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| {
4935 return comp.lockAndSetMiscFailure(4961 return comp.lockAndSetMiscFailure(
4936 .docs_copy,4962 .docs_copy,
4937 "unable to create output directory '{}{s}': {s}",4963 "unable to create output directory '{}': {s}",
4938 .{ emit.root_dir, emit.sub_path, @errorName(err) },4964 .{ docs_path, @errorName(err) },
4939 );4965 );
4940 };4966 };
4941 defer out_dir.close();4967 defer out_dir.close();
49424968
4943 sub_compilation.dirs.local_cache.handle.copyFile(4969 crt_file.full_object_path.root_dir.handle.copyFile(
4944 sub_compilation.cache_use.whole.bin_sub_path.?,4970 crt_file.full_object_path.sub_path,
4945 out_dir,4971 out_dir,
4946 "main.wasm",4972 "main.wasm",
4947 .{},4973 .{},
4948 ) catch |err| {4974 ) catch |err| {
4949 return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}{s}' to '{}{s}': {s}", .{4975 return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}' to '{}': {s}", .{
4950 sub_compilation.dirs.local_cache,4976 crt_file.full_object_path,
4951 sub_compilation.cache_use.whole.bin_sub_path.?,4977 docs_path,
4952 emit.root_dir,
4953 emit.sub_path,
4954 @errorName(err),4978 @errorName(err),
4955 });4979 });
4956 };4980 };
...@@ -5212,7 +5236,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module...@@ -5212,7 +5236,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module
5212 defer whole.cache_manifest_mutex.unlock();5236 defer whole.cache_manifest_mutex.unlock();
5213 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);5237 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
5214 },5238 },
5215 .incremental => {},5239 .incremental, .none => {},
5216 }5240 }
52175241
5218 const bin_digest = man.finalBin();5242 const bin_digest = man.finalBin();
...@@ -5557,9 +5581,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr...@@ -5557,9 +5581,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
5557 defer man.deinit();5581 defer man.deinit();
55585582
5559 man.hash.add(comp.clang_preprocessor_mode);5583 man.hash.add(comp.clang_preprocessor_mode);
5560 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);5584 man.hash.addOptionalBytes(comp.emit_asm);
5561 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);5585 man.hash.addOptionalBytes(comp.emit_llvm_ir);
5562 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);5586 man.hash.addOptionalBytes(comp.emit_llvm_bc);
55635587
5564 try cache_helpers.hashCSource(&man, c_object.src);5588 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...@@ -5793,7 +5817,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
5793 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);5817 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
5794 }5818 }
5795 },5819 },
5796 .incremental => {},5820 .incremental, .none => {},
5797 }5821 }
5798 }5822 }
57995823
...@@ -6037,7 +6061,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -6037,7 +6061,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
6037 defer whole.cache_manifest_mutex.unlock();6061 defer whole.cache_manifest_mutex.unlock();
6038 try whole_cache_manifest.addFilePost(dep_file_path);6062 try whole_cache_manifest.addFilePost(dep_file_path);
6039 },6063 },
6040 .incremental => {},6064 .incremental, .none => {},
6041 }6065 }
6042 }6066 }
6043 }6067 }
...@@ -7209,12 +7233,6 @@ fn buildOutputFromZig(...@@ -7209,12 +7233,6 @@ fn buildOutputFromZig(
7209 .cc_argv = &.{},7233 .cc_argv = &.{},
7210 .parent = null,7234 .parent = null,
7211 });7235 });
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
7219 const parent_whole_cache: ?ParentWholeCache = switch (comp.cache_use) {7237 const parent_whole_cache: ?ParentWholeCache = switch (comp.cache_use) {
7220 .whole => |whole| .{7238 .whole => |whole| .{
...@@ -7227,7 +7245,7 @@ fn buildOutputFromZig(...@@ -7227,7 +7245,7 @@ fn buildOutputFromZig(
7227 3, // global cache is the same7245 3, // global cache is the same
7228 },7246 },
7229 },7247 },
7230 .incremental => null,7248 .incremental, .none => null,
7231 };7249 };
72327250
7233 const sub_compilation = try Compilation.create(gpa, arena, .{7251 const sub_compilation = try Compilation.create(gpa, arena, .{
...@@ -7240,13 +7258,9 @@ fn buildOutputFromZig(...@@ -7240,13 +7258,9 @@ fn buildOutputFromZig(
7240 .root_name = root_name,7258 .root_name = root_name,
7241 .thread_pool = comp.thread_pool,7259 .thread_pool = comp.thread_pool,
7242 .libc_installation = comp.libc_installation,7260 .libc_installation = comp.libc_installation,
7243 .emit_bin = .{7261 .emit_bin = .yes_cache,
7244 .directory = null, // Put it in the cache directory.
7245 .basename = bin_basename,
7246 },
7247 .function_sections = true,7262 .function_sections = true,
7248 .data_sections = true,7263 .data_sections = true,
7249 .emit_h = null,
7250 .verbose_cc = comp.verbose_cc,7264 .verbose_cc = comp.verbose_cc,
7251 .verbose_link = comp.verbose_link,7265 .verbose_link = comp.verbose_link,
7252 .verbose_air = comp.verbose_air,7266 .verbose_air = comp.verbose_air,
...@@ -7366,13 +7380,9 @@ pub fn build_crt_file(...@@ -7366,13 +7380,9 @@ pub fn build_crt_file(
7366 .root_name = root_name,7380 .root_name = root_name,
7367 .thread_pool = comp.thread_pool,7381 .thread_pool = comp.thread_pool,
7368 .libc_installation = comp.libc_installation,7382 .libc_installation = comp.libc_installation,
7369 .emit_bin = .{7383 .emit_bin = .yes_cache,
7370 .directory = null, // Put it in the cache directory.
7371 .basename = basename,
7372 },
7373 .function_sections = options.function_sections orelse false,7384 .function_sections = options.function_sections orelse false,
7374 .data_sections = options.data_sections orelse false,7385 .data_sections = options.data_sections orelse false,
7375 .emit_h = null,
7376 .c_source_files = c_source_files,7386 .c_source_files = c_source_files,
7377 .verbose_cc = comp.verbose_cc,7387 .verbose_cc = comp.verbose_cc,
7378 .verbose_link = comp.verbose_link,7388 .verbose_link = comp.verbose_link,
...@@ -7444,7 +7454,11 @@ pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {...@@ -7444,7 +7454,11 @@ pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {
7444 return .{7454 return .{
7445 .full_object_path = .{7455 .full_object_path = .{
7446 .root_dir = comp.dirs.local_cache,7456 .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 }),
7448 },7462 },
7449 .lock = comp.cache_use.whole.moveLock(),7463 .lock = comp.cache_use.whole.moveLock(),
7450 };7464 };
src/Zcu/PerThread.zig+2-2
...@@ -2493,7 +2493,7 @@ fn newEmbedFile(...@@ -2493,7 +2493,7 @@ fn newEmbedFile(
2493 cache: {2493 cache: {
2494 const whole = switch (zcu.comp.cache_use) {2494 const whole = switch (zcu.comp.cache_use) {
2495 .whole => |whole| whole,2495 .whole => |whole| whole,
2496 .incremental => break :cache,2496 .incremental, .none => break :cache,
2497 };2497 };
2498 const man = whole.cache_manifest orelse break :cache;2498 const man = whole.cache_manifest orelse break :cache;
2499 const ip_str = opt_ip_str orelse break :cache; // this will be a compile error2499 const ip_str = opt_ip_str orelse break :cache; // this will be a compile error
...@@ -3377,7 +3377,7 @@ pub fn populateTestFunctions(...@@ -3377,7 +3377,7 @@ pub fn populateTestFunctions(
3377 }3377 }
33783378
3379 // The linker thread is not running, so we actually need to dispatch this task directly.3379 // 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);
3381 }3381 }
3382}3382}
33833383
src/libs/freebsd.zig+1-6
...@@ -1019,10 +1019,6 @@ fn buildSharedLib(...@@ -1019,10 +1019,6 @@ fn buildSharedLib(
1019 defer tracy.end();1019 defer tracy.end();
10201020
1021 const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });1021 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 };
1026 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };1022 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
1027 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);1023 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
1028 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;1024 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
...@@ -1082,8 +1078,7 @@ fn buildSharedLib(...@@ -1082,8 +1078,7 @@ fn buildSharedLib(
1082 .root_mod = root_mod,1078 .root_mod = root_mod,
1083 .root_name = lib.name,1079 .root_name = lib.name,
1084 .libc_installation = comp.libc_installation,1080 .libc_installation = comp.libc_installation,
1085 .emit_bin = emit_bin,1081 .emit_bin = .yes_cache,
1086 .emit_h = null,
1087 .verbose_cc = comp.verbose_cc,1082 .verbose_cc = comp.verbose_cc,
1088 .verbose_link = comp.verbose_link,1083 .verbose_link = comp.verbose_link,
1089 .verbose_air = comp.verbose_air,1084 .verbose_air = comp.verbose_air,
src/libs/glibc.zig+1-6
...@@ -1185,10 +1185,6 @@ fn buildSharedLib(...@@ -1185,10 +1185,6 @@ fn buildSharedLib(
1185 defer tracy.end();1185 defer tracy.end();
11861186
1187 const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });1187 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 };
1192 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };1188 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
1193 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);1189 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
1194 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;1190 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
...@@ -1248,8 +1244,7 @@ fn buildSharedLib(...@@ -1248,8 +1244,7 @@ fn buildSharedLib(
1248 .root_mod = root_mod,1244 .root_mod = root_mod,
1249 .root_name = lib.name,1245 .root_name = lib.name,
1250 .libc_installation = comp.libc_installation,1246 .libc_installation = comp.libc_installation,
1251 .emit_bin = emit_bin,1247 .emit_bin = .yes_cache,
1252 .emit_h = null,
1253 .verbose_cc = comp.verbose_cc,1248 .verbose_cc = comp.verbose_cc,
1254 .verbose_link = comp.verbose_link,1249 .verbose_link = comp.verbose_link,
1255 .verbose_air = comp.verbose_air,1250 .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!...@@ -122,17 +122,6 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!
122 const output_mode = .Lib;122 const output_mode = .Lib;
123 const link_mode = .static;123 const link_mode = .static;
124 const target = comp.root_mod.resolved_target.result;124 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
137 const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" });126 const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" });
138 const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" });127 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!...@@ -271,8 +260,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!
271 .root_name = root_name,260 .root_name = root_name,
272 .thread_pool = comp.thread_pool,261 .thread_pool = comp.thread_pool,
273 .libc_installation = comp.libc_installation,262 .libc_installation = comp.libc_installation,
274 .emit_bin = emit_bin,263 .emit_bin = .yes_cache,
275 .emit_h = null,
276 .c_source_files = c_source_files.items,264 .c_source_files = c_source_files.items,
277 .verbose_cc = comp.verbose_cc,265 .verbose_cc = comp.verbose_cc,
278 .verbose_link = comp.verbose_link,266 .verbose_link = comp.verbose_link,
...@@ -327,17 +315,6 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr...@@ -327,17 +315,6 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
327 const output_mode = .Lib;315 const output_mode = .Lib;
328 const link_mode = .static;316 const link_mode = .static;
329 const target = comp.root_mod.resolved_target.result;317 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
342 const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" });319 const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" });
343 const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" });320 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...@@ -467,8 +444,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
467 .root_name = root_name,444 .root_name = root_name,
468 .thread_pool = comp.thread_pool,445 .thread_pool = comp.thread_pool,
469 .libc_installation = comp.libc_installation,446 .libc_installation = comp.libc_installation,
470 .emit_bin = emit_bin,447 .emit_bin = .yes_cache,
471 .emit_h = null,
472 .c_source_files = c_source_files.items,448 .c_source_files = c_source_files.items,
473 .verbose_cc = comp.verbose_cc,449 .verbose_cc = comp.verbose_cc,
474 .verbose_link = comp.verbose_link,450 .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...@@ -45,11 +45,6 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
45 .link_mode = link_mode,45 .link_mode = link_mode,
46 });46 });
4747
48 const emit_bin = Compilation.EmitLoc{
49 .directory = null, // Put it in the cache directory.
50 .basename = basename,
51 };
52
53 const optimize_mode = comp.compilerRtOptMode();48 const optimize_mode = comp.compilerRtOptMode();
54 const strip = comp.compilerRtStrip();49 const strip = comp.compilerRtStrip();
55 const unwind_tables: std.builtin.UnwindTables =50 const unwind_tables: std.builtin.UnwindTables =
...@@ -287,8 +282,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo...@@ -287,8 +282,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
287 .root_mod = root_mod,282 .root_mod = root_mod,
288 .root_name = root_name,283 .root_name = root_name,
289 .libc_installation = comp.libc_installation,284 .libc_installation = comp.libc_installation,
290 .emit_bin = emit_bin,285 .emit_bin = .yes_cache,
291 .emit_h = null,
292 .c_source_files = c_source_files.items,286 .c_source_files = c_source_files.items,
293 .verbose_cc = comp.verbose_cc,287 .verbose_cc = comp.verbose_cc,
294 .verbose_link = comp.verbose_link,288 .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...@@ -31,7 +31,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
31 const unwind_tables: std.builtin.UnwindTables =31 const unwind_tables: std.builtin.UnwindTables =
32 if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .@"async";32 if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .@"async";
33 const config = Compilation.Config.resolve(.{33 const config = Compilation.Config.resolve(.{
34 .output_mode = .Lib,34 .output_mode = output_mode,
35 .resolved_target = comp.root_mod.resolved_target,35 .resolved_target = comp.root_mod.resolved_target,
36 .is_test = false,36 .is_test = false,
37 .have_zcu = false,37 .have_zcu = false,
...@@ -85,17 +85,6 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr...@@ -85,17 +85,6 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
85 };85 };
8686
87 const root_name = "unwind";87 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 };
99 var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined;88 var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined;
100 for (unwind_src_list, 0..) |unwind_src, i| {89 for (unwind_src_list, 0..) |unwind_src, i| {
101 var cflags = std.ArrayList([]const u8).init(arena);90 var cflags = std.ArrayList([]const u8).init(arena);
...@@ -160,7 +149,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr...@@ -160,7 +149,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
160 .main_mod = null,149 .main_mod = null,
161 .thread_pool = comp.thread_pool,150 .thread_pool = comp.thread_pool,
162 .libc_installation = comp.libc_installation,151 .libc_installation = comp.libc_installation,
163 .emit_bin = emit_bin,152 .emit_bin = .yes_cache,
164 .function_sections = comp.function_sections,153 .function_sections = comp.function_sections,
165 .c_source_files = &c_source_files,154 .c_source_files = &c_source_files,
166 .verbose_cc = comp.verbose_cc,155 .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...@@ -252,8 +252,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro
252 .thread_pool = comp.thread_pool,252 .thread_pool = comp.thread_pool,
253 .root_name = "c",253 .root_name = "c",
254 .libc_installation = comp.libc_installation,254 .libc_installation = comp.libc_installation,
255 .emit_bin = .{ .directory = null, .basename = "libc.so" },255 .emit_bin = .yes_cache,
256 .emit_h = null,
257 .verbose_cc = comp.verbose_cc,256 .verbose_cc = comp.verbose_cc,
258 .verbose_link = comp.verbose_link,257 .verbose_link = comp.verbose_link,
259 .verbose_air = comp.verbose_air,258 .verbose_air = comp.verbose_air,
src/libs/netbsd.zig+1-6
...@@ -684,10 +684,6 @@ fn buildSharedLib(...@@ -684,10 +684,6 @@ fn buildSharedLib(
684 defer tracy.end();684 defer tracy.end();
685685
686 const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });686 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 };
691 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };687 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
692 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);688 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
693 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;689 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
...@@ -746,8 +742,7 @@ fn buildSharedLib(...@@ -746,8 +742,7 @@ fn buildSharedLib(
746 .root_mod = root_mod,742 .root_mod = root_mod,
747 .root_name = lib.name,743 .root_name = lib.name,
748 .libc_installation = comp.libc_installation,744 .libc_installation = comp.libc_installation,
749 .emit_bin = emit_bin,745 .emit_bin = .yes_cache,
750 .emit_h = null,
751 .verbose_cc = comp.verbose_cc,746 .verbose_cc = comp.verbose_cc,
752 .verbose_link = comp.verbose_link,747 .verbose_link = comp.verbose_link,
753 .verbose_air = comp.verbose_air,748 .verbose_air = comp.verbose_air,
src/link.zig+31-5
...@@ -384,9 +384,11 @@ pub const File = struct {...@@ -384,9 +384,11 @@ pub const File = struct {
384 emit: Path,384 emit: Path,
385385
386 file: ?fs.File,386 file: ?fs.File,
387 /// When linking with LLD, this linker code will output an object file only at387 /// When using the LLVM backend, the emitted object is written to a file with this name. This
388 /// this location, and then this path can be placed on the LLD linker line.388 /// object file then becomes a normal link input to LLD or a self-hosted linker.
389 zcu_object_sub_path: ?[]const u8 = null,389 ///
390 /// To convert this to an actual path, see `Compilation.resolveEmitPath` (with `kind == .temp`).
391 zcu_object_basename: ?[]const u8 = null,
390 gc_sections: bool,392 gc_sections: bool,
391 print_gc_sections: bool,393 print_gc_sections: bool,
392 build_id: std.zig.BuildId,394 build_id: std.zig.BuildId,
...@@ -433,7 +435,6 @@ pub const File = struct {...@@ -433,7 +435,6 @@ pub const File = struct {
433 export_symbol_names: []const []const u8,435 export_symbol_names: []const []const u8,
434 global_base: ?u64,436 global_base: ?u64,
435 build_id: std.zig.BuildId,437 build_id: std.zig.BuildId,
436 disable_lld_caching: bool,
437 hash_style: Lld.Elf.HashStyle,438 hash_style: Lld.Elf.HashStyle,
438 sort_section: ?Lld.Elf.SortSection,439 sort_section: ?Lld.Elf.SortSection,
439 major_subsystem_version: ?u16,440 major_subsystem_version: ?u16,
...@@ -1083,7 +1084,7 @@ pub const File = struct {...@@ -1083,7 +1084,7 @@ pub const File = struct {
1083 // In this case, an object file is created by the LLVM backend, so1084 // In this case, an object file is created by the LLVM backend, so
1084 // there is no prelink phase. The Zig code is linked as a standard1085 // there is no prelink phase. The Zig code is linked as a standard
1085 // object along with the others.1086 // object along with the others.
1086 if (base.zcu_object_sub_path != null) return;1087 if (base.zcu_object_basename != null) return;
10871088
1088 switch (base.tag) {1089 switch (base.tag) {
1089 inline .wasm => |tag| {1090 inline .wasm => |tag| {
...@@ -1496,6 +1497,31 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {...@@ -1496,6 +1497,31 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
1496 },1497 },
1497 }1498 }
1498}1499}
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
1500/// Provided by the CLI, processed into `LinkInput` instances at the start of1526/// Provided by the CLI, processed into `LinkInput` instances at the start of
1501/// the compilation pipeline.1527/// the compilation pipeline.
src/link/Coff.zig+4-9
...@@ -224,21 +224,16 @@ pub fn createEmpty(...@@ -224,21 +224,16 @@ pub fn createEmpty(
224 else => 0x1000,224 else => 0x1000,
225 };225 };
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
235 const coff = try arena.create(Coff);227 const coff = try arena.create(Coff);
236 coff.* = .{228 coff.* = .{
237 .base = .{229 .base = .{
238 .tag = .coff,230 .tag = .coff,
239 .comp = comp,231 .comp = comp,
240 .emit = emit,232 .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,
242 .stack_size = options.stack_size orelse 16777216,237 .stack_size = options.stack_size orelse 16777216,
243 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),238 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),
244 .print_gc_sections = options.print_gc_sections,239 .print_gc_sections = options.print_gc_sections,
src/link/Elf.zig+7-16
...@@ -249,14 +249,6 @@ pub fn createEmpty(...@@ -249,14 +249,6 @@ pub fn createEmpty(
249 const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic;249 const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic;
250 const default_sym_version: elf.Versym = if (is_dyn_lib or comp.config.rdynamic) .GLOBAL else .LOCAL;250 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
260 var rpath_table: std.StringArrayHashMapUnmanaged(void) = .empty;252 var rpath_table: std.StringArrayHashMapUnmanaged(void) = .empty;
261 try rpath_table.entries.resize(arena, options.rpath_list.len);253 try rpath_table.entries.resize(arena, options.rpath_list.len);
262 @memcpy(rpath_table.entries.items(.key), options.rpath_list);254 @memcpy(rpath_table.entries.items(.key), options.rpath_list);
...@@ -268,7 +260,10 @@ pub fn createEmpty(...@@ -268,7 +260,10 @@ pub fn createEmpty(
268 .tag = .elf,260 .tag = .elf,
269 .comp = comp,261 .comp = comp,
270 .emit = emit,262 .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,
272 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),267 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
273 .print_gc_sections = options.print_gc_sections,268 .print_gc_sections = options.print_gc_sections,
274 .stack_size = options.stack_size orelse 16777216,269 .stack_size = options.stack_size orelse 16777216,
...@@ -770,17 +765,13 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {...@@ -770,17 +765,13 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
770 const gpa = comp.gpa;765 const gpa = comp.gpa;
771 const diags = &comp.link_diags;766 const diags = &comp.link_diags;
772767
773 const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{768 const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: {
774 .root_dir = self.base.emit.root_dir,769 break :p try comp.resolveEmitPathFlush(arena, .temp, raw);
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,
779 } else null;770 } else null;
780771
781 if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid);772 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
785 switch (comp.config.output_mode) {776 switch (comp.config.output_mode) {
786 .Obj => return relocatable.flushObject(self, comp),777 .Obj => return relocatable.flushObject(self, comp),
src/link/Goff.zig+1-1
...@@ -41,7 +41,7 @@ pub fn createEmpty(...@@ -41,7 +41,7 @@ pub fn createEmpty(
41 .tag = .goff,41 .tag = .goff,
42 .comp = comp,42 .comp = comp,
43 .emit = emit,43 .emit = emit,
44 .zcu_object_sub_path = emit.sub_path,44 .zcu_object_basename = emit.sub_path,
45 .gc_sections = options.gc_sections orelse false,45 .gc_sections = options.gc_sections orelse false,
46 .print_gc_sections = options.print_gc_sections,46 .print_gc_sections = options.print_gc_sections,
47 .stack_size = options.stack_size orelse 0,47 .stack_size = options.stack_size orelse 0,
src/link/Lld.zig+26-49
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1base: link.File,1base: link.File,
2disable_caching: bool,
3ofmt: union(enum) {2ofmt: union(enum) {
4 elf: Elf,3 elf: Elf,
5 coff: Coff,4 coff: Coff,
...@@ -231,7 +230,7 @@ pub fn createEmpty(...@@ -231,7 +230,7 @@ pub fn createEmpty(
231 .tag = .lld,230 .tag = .lld,
232 .comp = comp,231 .comp = comp,
233 .emit = emit,232 .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 }),
235 .gc_sections = gc_sections,234 .gc_sections = gc_sections,
236 .print_gc_sections = options.print_gc_sections,235 .print_gc_sections = options.print_gc_sections,
237 .stack_size = stack_size,236 .stack_size = stack_size,
...@@ -239,7 +238,6 @@ pub fn createEmpty(...@@ -239,7 +238,6 @@ pub fn createEmpty(
239 .file = null,238 .file = null,
240 .build_id = options.build_id,239 .build_id = options.build_id,
241 },240 },
242 .disable_caching = options.disable_lld_caching,
243 .ofmt = switch (target.ofmt) {241 .ofmt = switch (target.ofmt) {
244 .coff => .{ .coff = try .init(comp, options) },242 .coff => .{ .coff = try .init(comp, options) },
245 .elf => .{ .elf = try .init(comp, options) },243 .elf => .{ .elf = try .init(comp, options) },
...@@ -289,14 +287,11 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {...@@ -289,14 +287,11 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {
289 const full_out_path_z = try arena.dupeZ(u8, full_out_path);287 const full_out_path_z = try arena.dupeZ(u8, full_out_path);
290 const opt_zcu = comp.zcu;288 const opt_zcu = comp.zcu;
291289
292 // If there is no Zig code to compile, then we should skip flushing the output file290 const zcu_obj_path: ?Cache.Path = if (opt_zcu != null) p: {
293 // because it will not be part of the linker line anyway.291 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
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.? });
297 } else null;292 } 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
301 const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj)296 const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj)
302 comp.compiler_rt_obj.?.full_object_path297 comp.compiler_rt_obj.?.full_object_path
...@@ -330,7 +325,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {...@@ -330,7 +325,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {
330 for (comp.win32_resource_table.keys()) |key| {325 for (comp.win32_resource_table.keys()) |key| {
331 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));326 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));
332 }327 }
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));
334 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));329 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
335 if (ubsan_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));330 if (ubsan_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
336331
...@@ -368,14 +363,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {...@@ -368,14 +363,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
368 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.363 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
369 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});364 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 it366 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
372 // will not be part of the linker line anyway.367 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
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 }
379 } else null;368 } else null;
380369
381 const is_lib = comp.config.output_mode == .Lib;370 const is_lib = comp.config.output_mode == .Lib;
...@@ -402,8 +391,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {...@@ -402,8 +391,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
402 if (comp.c_object_table.count() != 0)391 if (comp.c_object_table.count() != 0)
403 break :blk comp.c_object_table.keys()[0].status.success.object_path;392 break :blk comp.c_object_table.keys()[0].status.success.object_path;
404393
405 if (module_obj_path) |p|394 if (zcu_obj_path) |p|
406 break :blk Cache.Path.initCwd(p);395 break :blk p;
407396
408 // TODO I think this is unreachable. Audit this situation when solving the above TODO397 // TODO I think this is unreachable. Audit this situation when solving the above TODO
409 // regarding eliding redundant object -> object transformations.398 // regarding eliding redundant object -> object transformations.
...@@ -513,9 +502,9 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {...@@ -513,9 +502,9 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
513502
514 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));503 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
515504
516 if (comp.implib_emit) |emit| {505 if (comp.emit_implib) |raw_emit_path| {
517 const implib_out_path = try emit.root_dir.join(arena, &[_][]const u8{emit.sub_path});506 const path = try comp.resolveEmitPathFlush(arena, .temp, raw_emit_path);
518 try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));507 try argv.append(try allocPrint(arena, "-IMPLIB:{}", .{path}));
519 }508 }
520509
521 if (comp.config.link_libc) {510 if (comp.config.link_libc) {
...@@ -556,8 +545,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {...@@ -556,8 +545,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
556 try argv.append(key.status.success.res_path);545 try argv.append(key.status.success.res_path);
557 }546 }
558547
559 if (module_obj_path) |p| {548 if (zcu_obj_path) |p| {
560 try argv.append(p);549 try argv.append(try p.toString(arena));
561 }550 }
562551
563 if (coff.module_definition_file) |def| {552 if (coff.module_definition_file) |def| {
...@@ -808,14 +797,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -808,14 +797,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
808 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.797 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
809 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});798 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 it800 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
812 // will not be part of the linker line anyway.801 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
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 }
819 } else null;802 } else null;
820803
821 const output_mode = comp.config.output_mode;804 const output_mode = comp.config.output_mode;
...@@ -862,8 +845,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -862,8 +845,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
862 if (comp.c_object_table.count() != 0)845 if (comp.c_object_table.count() != 0)
863 break :blk comp.c_object_table.keys()[0].status.success.object_path;846 break :blk comp.c_object_table.keys()[0].status.success.object_path;
864847
865 if (module_obj_path) |p|848 if (zcu_obj_path) |p|
866 break :blk Cache.Path.initCwd(p);849 break :blk p;
867850
868 // TODO I think this is unreachable. Audit this situation when solving the above TODO851 // TODO I think this is unreachable. Audit this situation when solving the above TODO
869 // regarding eliding redundant object -> object transformations.852 // regarding eliding redundant object -> object transformations.
...@@ -1151,8 +1134,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1151,8 +1134,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
1151 try argv.append(try key.status.success.object_path.toString(arena));1134 try argv.append(try key.status.success.object_path.toString(arena));
1152 }1135 }
11531136
1154 if (module_obj_path) |p| {1137 if (zcu_obj_path) |p| {
1155 try argv.append(p);1138 try argv.append(try p.toString(arena));
1156 }1139 }
11571140
1158 if (comp.tsan_lib) |lib| {1141 if (comp.tsan_lib) |lib| {
...@@ -1387,14 +1370,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {...@@ -1387,14 +1370,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
1387 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.1370 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
1388 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});1371 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 it1373 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
1391 // will not be part of the linker line anyway.1374 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
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 }
1398 } else null;1375 } else null;
13991376
1400 const is_obj = comp.config.output_mode == .Obj;1377 const is_obj = comp.config.output_mode == .Obj;
...@@ -1419,8 +1396,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {...@@ -1419,8 +1396,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
1419 if (comp.c_object_table.count() != 0)1396 if (comp.c_object_table.count() != 0)
1420 break :blk comp.c_object_table.keys()[0].status.success.object_path;1397 break :blk comp.c_object_table.keys()[0].status.success.object_path;
14211398
1422 if (module_obj_path) |p|1399 if (zcu_obj_path) |p|
1423 break :blk Cache.Path.initCwd(p);1400 break :blk p;
14241401
1425 // TODO I think this is unreachable. Audit this situation when solving the above TODO1402 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1426 // regarding eliding redundant object -> object transformations.1403 // regarding eliding redundant object -> object transformations.
...@@ -1610,8 +1587,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {...@@ -1610,8 +1587,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
1610 for (comp.c_object_table.keys()) |key| {1587 for (comp.c_object_table.keys()) |key| {
1611 try argv.append(try key.status.success.object_path.toString(arena));1588 try argv.append(try key.status.success.object_path.toString(arena));
1612 }1589 }
1613 if (module_obj_path) |p| {1590 if (zcu_obj_path) |p| {
1614 try argv.append(p);1591 try argv.append(try p.toString(arena));
1615 }1592 }
16161593
1617 if (compiler_rt_path) |p| {1594 if (compiler_rt_path) |p| {
src/link/MachO.zig+14-26
...@@ -173,13 +173,6 @@ pub fn createEmpty(...@@ -173,13 +173,6 @@ pub fn createEmpty(
173 const output_mode = comp.config.output_mode;173 const output_mode = comp.config.output_mode;
174 const link_mode = comp.config.link_mode;174 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});
183 const allow_shlib_undefined = options.allow_shlib_undefined orelse false;176 const allow_shlib_undefined = options.allow_shlib_undefined orelse false;
184177
185 const self = try arena.create(MachO);178 const self = try arena.create(MachO);
...@@ -188,7 +181,10 @@ pub fn createEmpty(...@@ -188,7 +181,10 @@ pub fn createEmpty(
188 .tag = .macho,181 .tag = .macho,
189 .comp = comp,182 .comp = comp,
190 .emit = emit,183 .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,
192 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),188 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),
193 .print_gc_sections = options.print_gc_sections,189 .print_gc_sections = options.print_gc_sections,
194 .stack_size = options.stack_size orelse 16777216,190 .stack_size = options.stack_size orelse 16777216,
...@@ -351,21 +347,16 @@ pub fn flush(...@@ -351,21 +347,16 @@ pub fn flush(
351 const sub_prog_node = prog_node.start("MachO Flush", 0);347 const sub_prog_node = prog_node.start("MachO Flush", 0);
352 defer sub_prog_node.end();348 defer sub_prog_node.end();
353349
354 const directory = self.base.emit.root_dir;350 const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: {
355 const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{351 break :p try comp.resolveEmitPathFlush(arena, .temp, raw);
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,
361 } else null;352 } else null;
362353
363 // --verbose-link354 // --verbose-link
364 if (comp.verbose_link) try self.dumpArgv(comp);355 if (comp.verbose_link) try self.dumpArgv(comp);
365356
366 if (self.getZigObject()) |zo| try zo.flush(self, tid);357 if (self.getZigObject()) |zo| try zo.flush(self, tid);
367 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);358 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, zcu_obj_path);
368 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);359 if (self.base.isObject()) return relocatable.flushObject(self, comp, zcu_obj_path);
369360
370 var positionals = std.ArrayList(link.Input).init(gpa);361 var positionals = std.ArrayList(link.Input).init(gpa);
371 defer positionals.deinit();362 defer positionals.deinit();
...@@ -387,7 +378,7 @@ pub fn flush(...@@ -387,7 +378,7 @@ pub fn flush(
387 positionals.appendAssumeCapacity(try link.openObjectInput(diags, key.status.success.object_path));378 positionals.appendAssumeCapacity(try link.openObjectInput(diags, key.status.success.object_path));
388 }379 }
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
392 if (comp.config.any_sanitize_thread) {383 if (comp.config.any_sanitize_thread) {
393 try positionals.append(try link.openObjectInput(diags, comp.tsan_lib.?.full_object_path));384 try positionals.append(try link.openObjectInput(diags, comp.tsan_lib.?.full_object_path));
...@@ -636,12 +627,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -636,12 +627,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
636627
637 const directory = self.base.emit.root_dir;628 const directory = self.base.emit.root_dir;
638 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});629 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: {630 const zcu_obj_path: ?[]const u8 = if (self.base.zcu_object_basename) |raw| p: {
640 if (fs.path.dirname(full_out_path)) |dirname| {631 const p = try comp.resolveEmitPathFlush(arena, .temp, raw);
641 break :blk try fs.path.join(arena, &.{ dirname, path });632 break :p try p.toString(arena);
642 } else {
643 break :blk path;
644 }
645 } else null;633 } else null;
646634
647 var argv = std.ArrayList([]const u8).init(arena);635 var argv = std.ArrayList([]const u8).init(arena);
...@@ -670,7 +658,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -670,7 +658,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
670 try argv.append(try key.status.success.object_path.toString(arena));658 try argv.append(try key.status.success.object_path.toString(arena));
671 }659 }
672660
673 if (module_obj_path) |p| {661 if (zcu_obj_path) |p| {
674 try argv.append(p);662 try argv.append(p);
675 }663 }
676 } else {664 } else {
...@@ -762,7 +750,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -762,7 +750,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
762 try argv.append(try key.status.success.object_path.toString(arena));750 try argv.append(try key.status.success.object_path.toString(arena));
763 }751 }
764752
765 if (module_obj_path) |p| {753 if (zcu_obj_path) |p| {
766 try argv.append(p);754 try argv.append(p);
767 }755 }
768756
src/link/Wasm.zig+7-18
...@@ -2951,21 +2951,16 @@ pub fn createEmpty(...@@ -2951,21 +2951,16 @@ pub fn createEmpty(
2951 const output_mode = comp.config.output_mode;2951 const output_mode = comp.config.output_mode;
2952 const wasi_exec_model = comp.config.wasi_exec_model;2952 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
2962 const wasm = try arena.create(Wasm);2954 const wasm = try arena.create(Wasm);
2963 wasm.* = .{2955 wasm.* = .{
2964 .base = .{2956 .base = .{
2965 .tag = .wasm,2957 .tag = .wasm,
2966 .comp = comp,2958 .comp = comp,
2967 .emit = emit,2959 .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,
2969 // Garbage collection is so crucial to WebAssembly that we design2964 // Garbage collection is so crucial to WebAssembly that we design
2970 // the linker around the assumption that it will be on in the vast2965 // the linker around the assumption that it will be on in the vast
2971 // majority of cases, and therefore express "no garbage collection"2966 // majority of cases, and therefore express "no garbage collection"
...@@ -3834,15 +3829,9 @@ pub fn flush(...@@ -3834,15 +3829,9 @@ pub fn flush(
38343829
3835 if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items);3830 if (comp.verbose_link) Compilation.dump_argv(wasm.dump_argv_list.items);
38363831
3837 if (wasm.base.zcu_object_sub_path) |path| {3832 if (wasm.base.zcu_object_basename) |raw| {
3838 const module_obj_path: Path = .{3833 const zcu_obj_path: Path = try comp.resolveEmitPathFlush(arena, .temp, raw);
3839 .root_dir = wasm.base.emit.root_dir,3834 openParseObjectReportingFailure(wasm, zcu_obj_path);
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);
3846 try prelink(wasm, prog_node);3835 try prelink(wasm, prog_node);
3847 }3836 }
38483837
src/link/Xcoff.zig+1-1
...@@ -41,7 +41,7 @@ pub fn createEmpty(...@@ -41,7 +41,7 @@ pub fn createEmpty(
41 .tag = .xcoff,41 .tag = .xcoff,
42 .comp = comp,42 .comp = comp,
43 .emit = emit,43 .emit = emit,
44 .zcu_object_sub_path = emit.sub_path,44 .zcu_object_basename = emit.sub_path,
45 .gc_sections = options.gc_sections orelse false,45 .gc_sections = options.gc_sections orelse false,
46 .print_gc_sections = options.print_gc_sections,46 .print_gc_sections = options.print_gc_sections,
47 .stack_size = options.stack_size orelse 0,47 .stack_size = options.stack_size orelse 0,
src/main.zig+101-254
...@@ -699,55 +699,21 @@ const Emit = union(enum) {...@@ -699,55 +699,21 @@ const Emit = union(enum) {
699 yes_default_path,699 yes_default_path,
700 yes: []const u8,700 yes: []const u8,
701701
702 const Resolved = struct {702 const OutputToCacheReason = enum { listen, @"zig run", @"zig test" };
703 data: ?Compilation.EmitLoc,703 fn resolve(emit: Emit, default_basename: []const u8, output_to_cache: ?OutputToCacheReason) Compilation.CreateOptions.Emit {
704 dir: ?fs.Dir,704 return switch (emit) {
705705 .no => .no,
706 fn deinit(self: *Resolved) void {706 .yes_default_path => if (output_to_cache != null) .yes_cache else .{ .yes_path = default_basename },
707 if (self.dir) |*dir| {707 .yes => |path| if (output_to_cache) |reason| {
708 dir.close();708 switch (reason) {
709 }709 .listen => fatal("--listen incompatible with explicit output path '{s}'", .{path}),
710 }710 .@"zig run", .@"zig test" => fatal(
711 };711 "'{s}' with explicit output path '{s}' requires explicit '-femit-bin=path' or '-fno-emit-bin'",
712712 .{ @tagName(reason), path },
713 fn resolve(emit: Emit, default_basename: []const u8, output_to_cache: bool) !Resolved {713 ),
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 };
747 }714 }
748 },715 } else .{ .yes_path = path },
749 }716 };
750 return resolved;
751 }717 }
752};718};
753719
...@@ -2830,7 +2796,7 @@ fn buildOutputType(...@@ -2830,7 +2796,7 @@ fn buildOutputType(
2830 .link => {2796 .link => {
2831 create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe;2797 create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe;
2832 if (emit_bin != .no) {2798 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;
2834 }2800 }
2835 if (emit_llvm) {2801 if (emit_llvm) {
2836 fatal("-emit-llvm cannot be used when linking", .{});2802 fatal("-emit-llvm cannot be used when linking", .{});
...@@ -3208,7 +3174,17 @@ fn buildOutputType(...@@ -3208,7 +3174,17 @@ fn buildOutputType(
3208 var cleanup_emit_bin_dir: ?fs.Dir = null;3174 var cleanup_emit_bin_dir: ?fs.Dir = null;
3209 defer if (cleanup_emit_bin_dir) |*dir| dir.close();3175 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 };
3212 const optional_version = if (have_version) version else null;3188 const optional_version = if (have_version) version else null;
32133189
3214 const root_name = if (provided_name) |n| n else main_mod.fully_qualified_name;3190 const root_name = if (provided_name) |n| n else main_mod.fully_qualified_name;
...@@ -3225,150 +3201,48 @@ fn buildOutputType(...@@ -3225,150 +3201,48 @@ fn buildOutputType(
3225 },3201 },
3226 };3202 };
32273203
3228 const a_out_basename = switch (target.ofmt) {3204 const emit_bin_resolved: Compilation.CreateOptions.Emit = switch (emit_bin) {
3229 .coff => "a.exe",3205 .no => .no,
3230 else => "a.out",3206 .yes_default_path => emit: {
3231 };3207 if (output_to_cache != null) break :emit .yes_cache;
32323208 const name = switch (clang_preprocessor_mode) {
3233 const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) {3209 .pch => try std.fmt.allocPrint(arena, "{s}.pch", .{root_name}),
3234 .no => null,3210 else => try std.zig.binNameAlloc(arena, .{
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, .{
3252 .root_name = root_name,3211 .root_name = root_name,
3253 .target = target,3212 .target = target,
3254 .output_mode = create_module.resolved_options.output_mode,3213 .output_mode = create_module.resolved_options.output_mode,
3255 .link_mode = create_module.resolved_options.link_mode,3214 .link_mode = create_module.resolved_options.link_mode,
3256 .version = optional_version,3215 .version = optional_version,
3257 }),3216 }),
3217 };
3218 break :emit .{ .yes_path = name };
3258 },3219 },
3259 .yes => |full_path| b: {3220 .yes => |path| if (output_to_cache != null) {
3260 const basename = fs.path.basename(full_path);3221 assert(output_to_cache == .listen); // there was an explicit bin path
3261 if (fs.path.dirname(full_path)) |dirname| {3222 fatal("--listen incompatible with explicit output path '{s}'", .{path});
3262 const handle = fs.cwd().openDir(dirname, .{}) catch |err| {3223 } else .{ .yes_path = path },
3263 fatal("unable to open output directory '{s}': {s}", .{ dirname, @errorName(err) });3224 .yes_a_out => emit: {
3264 };3225 assert(output_to_cache == null);
3265 cleanup_emit_bin_dir = handle;3226 break :emit .{ .yes_path = switch (target.ofmt) {
3266 break :b Compilation.EmitLoc{3227 .coff => "a.exe",
3267 .basename = basename,3228 else => "a.out",
3268 .directory = .{3229 } };
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,
3283 },3230 },
3284 };3231 };
32853232
3286 const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name});3233 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| {3234 const emit_h_resolved = emit_h.resolve(default_h_basename, output_to_cache);
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();
33033235
3304 const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name});3236 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| {3237 const emit_asm_resolved = emit_asm.resolve(default_asm_basename, output_to_cache);
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();
33213238
3322 const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name});3239 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| {3240 const emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename, output_to_cache);
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();
33393241
3340 const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name});3242 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| {3243 const emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename, output_to_cache);
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();
33573244
3358 var emit_docs_resolved = emit_docs.resolve("docs", output_to_cache) catch |err| {3245 const emit_docs_resolved = emit_docs.resolve("docs", output_to_cache);
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();
33723246
3373 const is_exe_or_dyn_lib = switch (create_module.resolved_options.output_mode) {3247 const is_exe_or_dyn_lib = switch (create_module.resolved_options.output_mode) {
3374 .Obj => false,3248 .Obj => false,
...@@ -3378,7 +3252,7 @@ fn buildOutputType(...@@ -3378,7 +3252,7 @@ fn buildOutputType(
3378 // Note that cmake when targeting Windows will try to execute3252 // Note that cmake when targeting Windows will try to execute
3379 // zig cc to make an executable and output an implib too.3253 // zig cc to make an executable and output an implib too.
3380 const implib_eligible = is_exe_or_dyn_lib and3254 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;
3382 if (!implib_eligible) {3256 if (!implib_eligible) {
3383 if (!emit_implib_arg_provided) {3257 if (!emit_implib_arg_provided) {
3384 emit_implib = .no;3258 emit_implib = .no;
...@@ -3387,22 +3261,18 @@ fn buildOutputType(...@@ -3387,22 +3261,18 @@ fn buildOutputType(
3387 }3261 }
3388 }3262 }
3389 const default_implib_basename = try std.fmt.allocPrint(arena, "{s}.lib", .{root_name});3263 const default_implib_basename = try std.fmt.allocPrint(arena, "{s}.lib", .{root_name});
3390 var emit_implib_resolved = switch (emit_implib) {3264 const emit_implib_resolved: Compilation.CreateOptions.Emit = switch (emit_implib) {
3391 .no => Emit.Resolved{ .data = null, .dir = null },3265 .no => .no,
3392 .yes => |p| emit_implib.resolve(default_implib_basename, output_to_cache) catch |err| {3266 .yes => emit_implib.resolve(default_implib_basename, output_to_cache),
3393 fatal("unable to open directory from argument '-femit-implib', '{s}': {s}", .{3267 .yes_default_path => emit: {
3394 p, @errorName(err),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,
3395 });3272 });
3396 },3273 break :emit .{ .yes_path = p };
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,
3403 },3274 },
3404 };3275 };
3405 defer emit_implib_resolved.deinit();
34063276
3407 var thread_pool: ThreadPool = undefined;3277 var thread_pool: ThreadPool = undefined;
3408 try thread_pool.init(.{3278 try thread_pool.init(.{
...@@ -3456,7 +3326,7 @@ fn buildOutputType(...@@ -3456,7 +3326,7 @@ fn buildOutputType(
3456 src.src_path = try dirs.local_cache.join(arena, &.{sub_path});3326 src.src_path = try dirs.local_cache.join(arena, &.{sub_path});
3457 }3327 }
34583328
3459 if (build_options.have_llvm and emit_asm != .no) {3329 if (build_options.have_llvm and emit_asm_resolved != .no) {
3460 // LLVM has no way to set this non-globally.3330 // LLVM has no way to set this non-globally.
3461 const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" };3331 const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" };
3462 @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv);3332 @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv);
...@@ -3472,23 +3342,11 @@ fn buildOutputType(...@@ -3472,23 +3342,11 @@ fn buildOutputType(
3472 fatal("--debug-incremental requires -fincremental", .{});3342 fatal("--debug-incremental requires -fincremental", .{});
3473 }3343 }
34743344
3475 const disable_lld_caching = !output_to_cache;
3476
3477 const cache_mode: Compilation.CacheMode = b: {3345 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;
3478 if (incremental) break :b .incremental;3349 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
3492 break :b .whole;3350 break :b .whole;
3493 };3351 };
34943352
...@@ -3510,13 +3368,13 @@ fn buildOutputType(...@@ -3510,13 +3368,13 @@ fn buildOutputType(
3510 .main_mod = main_mod,3368 .main_mod = main_mod,
3511 .root_mod = root_mod,3369 .root_mod = root_mod,
3512 .std_mod = std_mod,3370 .std_mod = std_mod,
3513 .emit_bin = emit_bin_loc,3371 .emit_bin = emit_bin_resolved,
3514 .emit_h = emit_h_resolved.data,3372 .emit_h = emit_h_resolved,
3515 .emit_asm = emit_asm_resolved.data,3373 .emit_asm = emit_asm_resolved,
3516 .emit_llvm_ir = emit_llvm_ir_resolved.data,3374 .emit_llvm_ir = emit_llvm_ir_resolved,
3517 .emit_llvm_bc = emit_llvm_bc_resolved.data,3375 .emit_llvm_bc = emit_llvm_bc_resolved,
3518 .emit_docs = emit_docs_resolved.data,3376 .emit_docs = emit_docs_resolved,
3519 .emit_implib = emit_implib_resolved.data,3377 .emit_implib = emit_implib_resolved,
3520 .lib_directories = create_module.lib_directories.items,3378 .lib_directories = create_module.lib_directories.items,
3521 .rpath_list = create_module.rpath_list.items,3379 .rpath_list = create_module.rpath_list.items,
3522 .symbol_wrap_set = symbol_wrap_set,3380 .symbol_wrap_set = symbol_wrap_set,
...@@ -3599,7 +3457,6 @@ fn buildOutputType(...@@ -3599,7 +3457,6 @@ fn buildOutputType(
3599 .test_filters = test_filters.items,3457 .test_filters = test_filters.items,
3600 .test_name_prefix = test_name_prefix,3458 .test_name_prefix = test_name_prefix,
3601 .test_runner_path = test_runner_path,3459 .test_runner_path = test_runner_path,
3602 .disable_lld_caching = disable_lld_caching,
3603 .cache_mode = cache_mode,3460 .cache_mode = cache_mode,
3604 .subsystem = subsystem,3461 .subsystem = subsystem,
3605 .debug_compile_errors = debug_compile_errors,3462 .debug_compile_errors = debug_compile_errors,
...@@ -3744,13 +3601,8 @@ fn buildOutputType(...@@ -3744,13 +3601,8 @@ fn buildOutputType(
3744 }) {3601 }) {
3745 dev.checkAny(&.{ .run_command, .test_command });3602 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) {
3748 // Default to using `zig run` to execute the produced .c code from `zig test`.3605 // 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 });
3754 try test_exec_args.appendSlice(arena, &.{ self_exe_path, "run" });3606 try test_exec_args.appendSlice(arena, &.{ self_exe_path, "run" });
3755 if (dirs.zig_lib.path) |p| {3607 if (dirs.zig_lib.path) |p| {
3756 try test_exec_args.appendSlice(arena, &.{ "-I", p });3608 try test_exec_args.appendSlice(arena, &.{ "-I", p });
...@@ -3775,7 +3627,7 @@ fn buildOutputType(...@@ -3775,7 +3627,7 @@ fn buildOutputType(
3775 if (create_module.dynamic_linker) |dl| {3627 if (create_module.dynamic_linker) |dl| {
3776 try test_exec_args.appendSlice(arena, &.{ "--dynamic-linker", dl });3628 try test_exec_args.appendSlice(arena, &.{ "--dynamic-linker", dl });
3777 }3629 }
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
3779 }3631 }
37803632
3781 try runOrTest(3633 try runOrTest(
...@@ -4354,12 +4206,22 @@ fn runOrTest(...@@ -4354,12 +4206,22 @@ fn runOrTest(
4354 runtime_args_start: ?usize,4206 runtime_args_start: ?usize,
4355 link_libc: bool,4207 link_libc: bool,
4356) !void {4208) !void {
4357 const lf = comp.bin_file orelse return;4209 const raw_emit_bin = comp.emit_bin orelse return;
4358 // A naive `directory.join` here will indeed get the correct path to the binary,4210 const exe_path = switch (comp.cache_use) {
4359 // however, in the case of cwd, we actually want `./foo` so that the path can be executed.4211 .none => p: {
4360 const exe_path = try fs.path.join(arena, &[_][]const u8{4212 if (fs.path.isAbsolute(raw_emit_bin)) break :p raw_emit_bin;
4361 lf.emit.root_dir.path orelse ".", lf.emit.sub_path,4213 // Use `fs.path.join` to make a file in the cwd is still executed properly.
4362 });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
4364 var argv = std.ArrayList([]const u8).init(gpa);4226 var argv = std.ArrayList([]const u8).init(gpa);
4365 defer argv.deinit();4227 defer argv.deinit();
...@@ -5087,16 +4949,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5087,16 +4949,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5087 };4949 };
5088 };4950 };
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
5100 process.raiseFileDescriptorLimit();4952 process.raiseFileDescriptorLimit();
51014953
5102 const cwd_path = try introspect.getResolvedCwd(arena);4954 const cwd_path = try introspect.getResolvedCwd(arena);
...@@ -5357,8 +5209,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5357,8 +5209,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5357 .config = config,5209 .config = config,
5358 .root_mod = root_mod,5210 .root_mod = root_mod,
5359 .main_mod = build_mod,5211 .main_mod = build_mod,
5360 .emit_bin = emit_bin,5212 .emit_bin = .yes_cache,
5361 .emit_h = null,
5362 .self_exe_path = self_exe_path,5213 .self_exe_path = self_exe_path,
5363 .thread_pool = &thread_pool,5214 .thread_pool = &thread_pool,
5364 .verbose_cc = verbose_cc,5215 .verbose_cc = verbose_cc,
...@@ -5386,8 +5237,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5386,8 +5237,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5386 // Since incremental compilation isn't done yet, we use cache_mode = whole5237 // Since incremental compilation isn't done yet, we use cache_mode = whole
5387 // above, and thus the output file is already closed.5238 // above, and thus the output file is already closed.
5388 //try comp.makeBinFileExecutable();5239 //try comp.makeBinFileExecutable();
5389 child_argv.items[argv_index_exe] =5240 child_argv.items[argv_index_exe] = try dirs.local_cache.join(arena, &.{
5390 try dirs.local_cache.join(arena, &.{comp.cache_use.whole.bin_sub_path.?});5241 "o",
5242 &Cache.binToHex(comp.digest.?),
5243 comp.emit_bin.?,
5244 });
5391 }5245 }
53925246
5393 if (process.can_spawn) {5247 if (process.can_spawn) {
...@@ -5504,16 +5358,6 @@ fn jitCmd(...@@ -5504,16 +5358,6 @@ fn jitCmd(
5504 .is_explicit_dynamic_linker = false,5358 .is_explicit_dynamic_linker = false,
5505 };5359 };
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
5517 const self_exe_path = fs.selfExePathAlloc(arena) catch |err| {5361 const self_exe_path = fs.selfExePathAlloc(arena) catch |err| {
5518 fatal("unable to find self exe path: {s}", .{@errorName(err)});5362 fatal("unable to find self exe path: {s}", .{@errorName(err)});
5519 };5363 };
...@@ -5605,8 +5449,7 @@ fn jitCmd(...@@ -5605,8 +5449,7 @@ fn jitCmd(
5605 .config = config,5449 .config = config,
5606 .root_mod = root_mod,5450 .root_mod = root_mod,
5607 .main_mod = root_mod,5451 .main_mod = root_mod,
5608 .emit_bin = emit_bin,5452 .emit_bin = .yes_cache,
5609 .emit_h = null,
5610 .self_exe_path = self_exe_path,5453 .self_exe_path = self_exe_path,
5611 .thread_pool = &thread_pool,5454 .thread_pool = &thread_pool,
5612 .cache_mode = .whole,5455 .cache_mode = .whole,
...@@ -5637,7 +5480,11 @@ fn jitCmd(...@@ -5637,7 +5480,11 @@ fn jitCmd(
5637 };5480 };
5638 }5481 }
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 });
5641 child_argv.appendAssumeCapacity(exe_path);5488 child_argv.appendAssumeCapacity(exe_path);
5642 }5489 }
56435490
tools/incr-check.zig+1-1
...@@ -314,7 +314,7 @@ const Eval = struct {...@@ -314,7 +314,7 @@ const Eval = struct {
314 const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len];314 const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len];
315 const result_dir = ".local-cache" ++ std.fs.path.sep_str ++ "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*);315 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, .{
318 .root_name = "root", // corresponds to the module name "root"318 .root_name = "root", // corresponds to the module name "root"
319 .target = eval.target.resolved,319 .target = eval.target.resolved,
320 .output_mode = .Exe,320 .output_mode = .Exe,