master
1const Compilation = @This();
2const builtin = @import("builtin");
3
4const std = @import("std");
5const Io = std.Io;
6const Writer = std.Io.Writer;
7const fs = std.fs;
8const mem = std.mem;
9const Allocator = std.mem.Allocator;
10const assert = std.debug.assert;
11const log = std.log.scoped(.compilation);
12const Target = std.Target;
13const ThreadPool = std.Thread.Pool;
14const WaitGroup = std.Thread.WaitGroup;
15const ErrorBundle = std.zig.ErrorBundle;
16const fatal = std.process.fatal;
17
18const Value = @import("Value.zig");
19const Type = @import("Type.zig");
20const target_util = @import("target.zig");
21const Package = @import("Package.zig");
22const introspect = @import("introspect.zig");
23const link = @import("link.zig");
24const tracy = @import("tracy.zig");
25const trace = tracy.trace;
26const build_options = @import("build_options");
27const LibCInstallation = std.zig.LibCInstallation;
28const glibc = @import("libs/glibc.zig");
29const musl = @import("libs/musl.zig");
30const freebsd = @import("libs/freebsd.zig");
31const netbsd = @import("libs/netbsd.zig");
32const mingw = @import("libs/mingw.zig");
33const libunwind = @import("libs/libunwind.zig");
34const libcxx = @import("libs/libcxx.zig");
35const wasi_libc = @import("libs/wasi_libc.zig");
36const clangMain = @import("main.zig").clangMain;
37const Zcu = @import("Zcu.zig");
38const Sema = @import("Sema.zig");
39const InternPool = @import("InternPool.zig");
40const Cache = std.Build.Cache;
41const c_codegen = @import("codegen/c.zig");
42const libtsan = @import("libs/libtsan.zig");
43const Zir = std.zig.Zir;
44const Air = @import("Air.zig");
45const Builtin = @import("Builtin.zig");
46const LlvmObject = @import("codegen/llvm.zig").Object;
47const dev = @import("dev.zig");
48
49pub const Config = @import("Compilation/Config.zig");
50
51/// General-purpose allocator. Used for both temporary and long-term storage.
52gpa: Allocator,
53/// Arena-allocated memory, mostly used during initialization. However, it can
54/// be used for other things requiring the same lifetime as the `Compilation`.
55/// Not thread-safe - lock `mutex` if potentially accessing from multiple
56/// threads at once.
57arena: Allocator,
58io: Io,
59/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
60zcu: ?*Zcu,
61/// Contains different state depending on the `CacheMode` used by this `Compilation`.
62cache_use: CacheUse,
63/// All compilations have a root module because this is where some important
64/// settings are stored, such as target and optimization mode. This module
65/// might not have any .zig code associated with it, however.
66root_mod: *Package.Module,
67
68/// User-specified settings that have all the defaults resolved into concrete values.
69config: Config,
70
71/// The main output file.
72/// In `CacheMode.whole`, this is null except for during the body of `update`.
73/// In `CacheMode.none` and `CacheMode.incremental`, this is long-lived.
74/// Regardless of cache mode, this is `null` when `-fno-emit-bin` is used.
75bin_file: ?*link.File,
76
77/// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin)
78sysroot: ?[]const u8,
79root_name: [:0]const u8,
80compiler_rt_strat: RtStrat,
81ubsan_rt_strat: RtStrat,
82/// Resolved into known paths, any GNU ld scripts already resolved.
83link_inputs: []const link.Input,
84/// Needed only for passing -F args to clang.
85framework_dirs: []const []const u8,
86/// These are only for DLLs dependencies fulfilled by the `.def` files shipped
87/// with Zig. Static libraries are provided as `link.Input` values.
88windows_libs: std.StringArrayHashMapUnmanaged(void),
89version: ?std.SemanticVersion,
90libc_installation: ?*const LibCInstallation,
91skip_linker_dependencies: bool,
92function_sections: bool,
93data_sections: bool,
94link_eh_frame_hdr: bool,
95native_system_include_paths: []const []const u8,
96/// List of symbols forced as undefined in the symbol table
97/// thus forcing their resolution by the linker.
98/// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE.
99force_undefined_symbols: std.StringArrayHashMapUnmanaged(void),
100
101c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .empty,
102win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMapUnmanaged(*Win32Resource, void) else struct {
103 pub fn keys(_: @This()) [0]void {
104 return .{};
105 }
106 pub fn count(_: @This()) u0 {
107 return 0;
108 }
109 pub fn deinit(_: @This(), _: Allocator) void {}
110} = .{},
111
112link_diags: link.Diags,
113link_task_queue: link.Queue = .empty,
114
115/// Set of work that can be represented by only flags to determine whether the
116/// work is queued or not.
117queued_jobs: QueuedJobs,
118
119work_queues: [
120 len: {
121 var len: usize = 0;
122 for (std.enums.values(Job.Tag)) |tag| {
123 len = @max(Job.stage(tag) + 1, len);
124 }
125 break :len len;
126 }
127]std.Deque(Job),
128
129/// These jobs are to invoke the Clang compiler to create an object file, which
130/// gets linked with the Compilation.
131c_object_work_queue: std.Deque(*CObject),
132
133/// These jobs are to invoke the RC compiler to create a compiled resource file (.res), which
134/// gets linked with the Compilation.
135win32_resource_work_queue: if (dev.env.supports(.win32_resource)) std.Deque(*Win32Resource) else struct {
136 pub const empty: @This() = .{};
137 pub fn ensureUnusedCapacity(_: @This(), _: Allocator, _: u0) error{}!void {}
138 pub fn popFront(_: @This()) ?noreturn {
139 return null;
140 }
141 pub fn deinit(_: @This(), _: Allocator) void {}
142},
143
144/// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator.
145/// This data is accessed by multiple threads and is protected by `mutex`.
146failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.Diag.Bundle) = .empty,
147
148/// The ErrorBundle memory is owned by the `Win32Resource`, using Compilation's general purpose allocator.
149/// This data is accessed by multiple threads and is protected by `mutex`.
150failed_win32_resources: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMapUnmanaged(*Win32Resource, ErrorBundle) else struct {
151 pub fn values(_: @This()) [0]void {
152 return .{};
153 }
154 pub fn deinit(_: @This(), _: Allocator) void {}
155} = .{},
156
157/// Miscellaneous things that can fail.
158misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .empty,
159
160/// When this is `true` it means invoking clang as a sub-process is expected to inherit
161/// stdin, stdout, stderr, and if it returns non success, to forward the exit code.
162/// Otherwise we attempt to parse the error messages and expose them via the Compilation API.
163/// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`.
164clang_passthrough_mode: bool,
165clang_preprocessor_mode: ClangPreprocessorMode,
166/// Whether to print clang argvs to stdout.
167verbose_cc: bool,
168verbose_air: bool,
169verbose_intern_pool: bool,
170verbose_generic_instances: bool,
171verbose_llvm_ir: ?[]const u8,
172verbose_llvm_bc: ?[]const u8,
173verbose_cimport: bool,
174verbose_llvm_cpu_features: bool,
175verbose_link: bool,
176disable_c_depfile: bool,
177stack_report: bool,
178debug_compiler_runtime_libs: bool,
179debug_compile_errors: bool,
180/// Do not check this field directly. Instead, use the `debugIncremental` wrapper function.
181debug_incremental: bool,
182alloc_failure_occurred: bool = false,
183last_update_was_cache_hit: bool = false,
184
185c_source_files: []const CSourceFile,
186rc_source_files: []const RcSourceFile,
187global_cc_argv: []const []const u8,
188cache_parent: *Cache,
189/// Populated when a sub-Compilation is created during the `update` of its parent.
190/// In this case the child must additionally add file system inputs to this object.
191parent_whole_cache: ?ParentWholeCache,
192/// Path to own executable for invoking `zig clang`.
193self_exe_path: ?[]const u8,
194/// Owned by the caller of `Compilation.create`.
195dirs: Directories,
196libc_include_dir_list: []const []const u8,
197libc_framework_dir_list: []const []const u8,
198rc_includes: std.zig.RcIncludes,
199mingw_unicode_entry_point: bool,
200thread_pool: *ThreadPool,
201
202/// Populated when we build the libc++ static library. A Job to build this is placed in the queue
203/// and resolved before calling linker.flush().
204libcxx_static_lib: ?CrtFile = null,
205/// Populated when we build the libc++abi static library. A Job to build this is placed in the queue
206/// and resolved before calling linker.flush().
207libcxxabi_static_lib: ?CrtFile = null,
208/// Populated when we build the libunwind static library. A Job to build this is placed in the queue
209/// and resolved before calling linker.flush().
210libunwind_static_lib: ?CrtFile = null,
211/// Populated when we build the TSAN library. A Job to build this is placed in the queue
212/// and resolved before calling linker.flush().
213tsan_lib: ?CrtFile = null,
214/// Populated when we build the UBSAN library. A Job to build this is placed in the queue
215/// and resolved before calling linker.flush().
216ubsan_rt_lib: ?CrtFile = null,
217/// Populated when we build the UBSAN object. A Job to build this is placed in the queue
218/// and resolved before calling linker.flush().
219ubsan_rt_obj: ?CrtFile = null,
220/// Populated when we build the libc static library. A Job to build this is placed in the queue
221/// and resolved before calling linker.flush().
222zigc_static_lib: ?CrtFile = null,
223/// Populated when we build the libcompiler_rt static library. A Job to build this is indicated
224/// by setting `queued_jobs.compiler_rt_lib` and resolved before calling linker.flush().
225compiler_rt_lib: ?CrtFile = null,
226/// Populated when we build the compiler_rt_obj object. A Job to build this is indicated
227/// by setting `queued_jobs.compiler_rt_obj` and resolved before calling linker.flush().
228compiler_rt_obj: ?CrtFile = null,
229/// hack for stage2_x86_64 + coff
230compiler_rt_dyn_lib: ?CrtFile = null,
231/// Populated when we build the libfuzzer static library. A Job to build this
232/// is indicated by setting `queued_jobs.fuzzer_lib` and resolved before
233/// calling linker.flush().
234fuzzer_lib: ?CrtFile = null,
235
236glibc_so_files: ?glibc.BuiltSharedObjects = null,
237freebsd_so_files: ?freebsd.BuiltSharedObjects = null,
238netbsd_so_files: ?netbsd.BuiltSharedObjects = null,
239
240/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
241/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.
242/// The key is the basename, and the value is the absolute path to the completed build artifact.
243crt_files: std.StringHashMapUnmanaged(CrtFile) = .empty,
244
245/// How many lines of reference trace should be included per compile error.
246/// Null means only show snippet on first error.
247reference_trace: ?u32 = null,
248
249/// This mutex guards all `Compilation` mutable state.
250/// Disabled in single-threaded mode because the thread pool spawns in the same thread.
251mutex: if (builtin.single_threaded) struct {
252 pub inline fn tryLock(_: @This()) void {}
253 pub inline fn lock(_: @This()) void {}
254 pub inline fn unlock(_: @This()) void {}
255} else std.Thread.Mutex = .{},
256
257test_filters: []const []const u8,
258
259link_task_wait_group: WaitGroup = .{},
260link_prog_node: std.Progress.Node = .none,
261
262llvm_opt_bisect_limit: c_int,
263
264time_report: ?TimeReport,
265
266file_system_inputs: ?*std.ArrayList(u8),
267
268/// This is the digest of the cache for the current compilation.
269/// This digest will be known after update() is called.
270digest: ?[Cache.bin_digest_len]u8 = null,
271
272/// Non-`null` iff we are emitting a binary.
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_bin: ?[]const u8,
276/// Non-`null` iff we are emitting assembly.
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_asm: ?[]const u8,
280/// Non-`null` iff we are emitting an implib.
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_implib: ?[]const u8,
284/// Non-`null` iff we are emitting LLVM IR.
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_ir: ?[]const u8,
288/// Non-`null` iff we are emitting LLVM bitcode.
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_llvm_bc: ?[]const u8,
292/// Non-`null` iff we are emitting documentation.
293/// Does not change for the lifetime of this `Compilation`.
294/// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
295emit_docs: ?[]const u8,
296
297const QueuedJobs = struct {
298 /// hack for stage2_x86_64 + coff
299 compiler_rt_dyn_lib: bool = false,
300 compiler_rt_lib: bool = false,
301 compiler_rt_obj: bool = false,
302 ubsan_rt_lib: bool = false,
303 ubsan_rt_obj: bool = false,
304 fuzzer_lib: bool = false,
305 musl_crt_file: [@typeInfo(musl.CrtFile).@"enum".fields.len]bool = @splat(false),
306 glibc_crt_file: [@typeInfo(glibc.CrtFile).@"enum".fields.len]bool = @splat(false),
307 freebsd_crt_file: [@typeInfo(freebsd.CrtFile).@"enum".fields.len]bool = @splat(false),
308 netbsd_crt_file: [@typeInfo(netbsd.CrtFile).@"enum".fields.len]bool = @splat(false),
309 /// one of WASI libc static objects
310 wasi_libc_crt_file: [@typeInfo(wasi_libc.CrtFile).@"enum".fields.len]bool = @splat(false),
311 /// one of the mingw-w64 static objects
312 mingw_crt_file: [@typeInfo(mingw.CrtFile).@"enum".fields.len]bool = @splat(false),
313 /// all of the glibc shared objects
314 glibc_shared_objects: bool = false,
315 freebsd_shared_objects: bool = false,
316 netbsd_shared_objects: bool = false,
317 /// libunwind.a, usually needed when linking libc
318 libunwind: bool = false,
319 libcxx: bool = false,
320 libcxxabi: bool = false,
321 libtsan: bool = false,
322 zigc_lib: bool = false,
323};
324
325pub const Timer = union(enum) {
326 unused,
327 active: struct {
328 start: std.time.Instant,
329 saved_ns: u64,
330 },
331 paused: u64,
332 stopped,
333
334 pub fn pause(t: *Timer) void {
335 switch (t.*) {
336 .unused => return,
337 .active => |a| {
338 const current = std.time.Instant.now() catch unreachable;
339 const new_ns = switch (current.order(a.start)) {
340 .lt, .eq => 0,
341 .gt => current.since(a.start),
342 };
343 t.* = .{ .paused = a.saved_ns + new_ns };
344 },
345 .paused => unreachable,
346 .stopped => unreachable,
347 }
348 }
349 pub fn @"resume"(t: *Timer) void {
350 switch (t.*) {
351 .unused => return,
352 .active => unreachable,
353 .paused => |saved_ns| t.* = .{ .active = .{
354 .start = std.time.Instant.now() catch unreachable,
355 .saved_ns = saved_ns,
356 } },
357 .stopped => unreachable,
358 }
359 }
360 pub fn finish(t: *Timer) ?u64 {
361 defer t.* = .stopped;
362 switch (t.*) {
363 .unused => return null,
364 .active => |a| {
365 const current = std.time.Instant.now() catch unreachable;
366 const new_ns = switch (current.order(a.start)) {
367 .lt, .eq => 0,
368 .gt => current.since(a.start),
369 };
370 return a.saved_ns + new_ns;
371 },
372 .paused => |ns| return ns,
373 .stopped => unreachable,
374 }
375 }
376};
377
378/// Starts a timer for measuring a `--time-report` value. If `comp.time_report` is `null`, the
379/// returned timer does nothing. When the thing being timed is done, call `Timer.finish`. If that
380/// function returns non-`null`, then the value is a number of nanoseconds, and `comp.time_report`
381/// is set.
382pub fn startTimer(comp: *Compilation) Timer {
383 if (comp.time_report == null) return .unused;
384 const now = std.time.Instant.now() catch @panic("std.time.Timer unsupported; cannot emit time report");
385 return .{ .active = .{
386 .start = now,
387 .saved_ns = 0,
388 } };
389}
390
391/// A filesystem path, represented relative to one of a few specific directories where possible.
392/// Every path (considering symlinks as distinct paths) has a canonical representation in this form.
393/// This abstraction allows us to:
394/// * always open files relative to a consistent root on the filesystem
395/// * detect when two paths correspond to the same file, e.g. for deduplicating `@import`s
396pub const Path = struct {
397 root: Root,
398 /// This path is always in a normalized form, where:
399 /// * All components are separated by `fs.path.sep`
400 /// * There are no repeated separators (like "foo//bar")
401 /// * There are no "." or ".." components
402 /// * There is no trailing path separator
403 ///
404 /// There is a leading separator iff `root` is `.none` *and* `builtin.target.os.tag != .wasi`.
405 ///
406 /// If this `Path` exactly represents a `Root`, the sub path is "", not ".".
407 sub_path: []u8,
408
409 const Root = enum {
410 /// `sub_path` is relative to the Zig lib directory on `Compilation`.
411 zig_lib,
412 /// `sub_path` is relative to the global cache directory on `Compilation`.
413 global_cache,
414 /// `sub_path` is relative to the local cache directory on `Compilation`.
415 local_cache,
416 /// `sub_path` is not relative to any of the roots listed above.
417 /// It is resolved starting with `Directories.cwd`; so it is an absolute path on most
418 /// targets, but cwd-relative on WASI. We do not make it cwd-relative on other targets
419 /// so that `Path.digest` gives hashes which can be stored in the Zig cache (as they
420 /// don't depend on a specific compiler instance).
421 none,
422 };
423
424 /// In general, we can only construct canonical `Path`s at runtime, because weird nesting might
425 /// mean that e.g. a sub path inside zig/lib/ is actually in the global cache. However, because
426 /// `Directories` guarantees that `zig_lib` is a distinct path from both cache directories, it's
427 /// okay for us to construct this path, and only this path, as a comptime constant.
428 pub const zig_lib_root: Path = .{ .root = .zig_lib, .sub_path = "" };
429
430 pub fn deinit(p: Path, gpa: Allocator) void {
431 gpa.free(p.sub_path);
432 }
433
434 /// The added data is relocatable across any compiler process using the same lib and cache
435 /// directories; it does not depend on cwd.
436 pub fn addToHasher(p: Path, h: *Cache.Hasher) void {
437 h.update(&.{@intFromEnum(p.root)});
438 h.update(p.sub_path);
439 }
440
441 /// Small convenience wrapper around `addToHasher`.
442 pub fn digest(p: Path) Cache.BinDigest {
443 var h = Cache.hasher_init;
444 p.addToHasher(&h);
445 return h.finalResult();
446 }
447
448 /// Given a `Path`, returns the directory handle and sub path to be used to open the path.
449 pub fn openInfo(p: Path, dirs: Directories) struct { fs.Dir, []const u8 } {
450 const dir = switch (p.root) {
451 .none => {
452 const cwd_sub_path = absToCwdRelative(p.sub_path, dirs.cwd);
453 return .{ fs.cwd(), cwd_sub_path };
454 },
455 .zig_lib => dirs.zig_lib.handle,
456 .global_cache => dirs.global_cache.handle,
457 .local_cache => dirs.local_cache.handle,
458 };
459 if (p.sub_path.len == 0) return .{ dir, "." };
460 assert(!fs.path.isAbsolute(p.sub_path));
461 return .{ dir, p.sub_path };
462 }
463
464 pub const format = unreachable; // do not format direcetly
465 pub fn fmt(p: Path, comp: *Compilation) Formatter {
466 return .{ .p = p, .comp = comp };
467 }
468 const Formatter = struct {
469 p: Path,
470 comp: *Compilation,
471 pub fn format(f: Formatter, w: *Writer) Writer.Error!void {
472 const root_path: []const u8 = switch (f.p.root) {
473 .zig_lib => f.comp.dirs.zig_lib.path orelse ".",
474 .global_cache => f.comp.dirs.global_cache.path orelse ".",
475 .local_cache => f.comp.dirs.local_cache.path orelse ".",
476 .none => {
477 const cwd_sub_path = absToCwdRelative(f.p.sub_path, f.comp.dirs.cwd);
478 try w.writeAll(cwd_sub_path);
479 return;
480 },
481 };
482 assert(root_path.len != 0);
483 try w.writeAll(root_path);
484 if (f.p.sub_path.len > 0) {
485 try w.writeByte(fs.path.sep);
486 try w.writeAll(f.p.sub_path);
487 }
488 }
489 };
490
491 /// Given the `sub_path` of a `Path` with `Path.root == .none`, attempts to convert
492 /// the (absolute) path to a cwd-relative path. Otherwise, returns the absolute path
493 /// unmodified. The returned string is never empty: "" is converted to ".".
494 fn absToCwdRelative(sub_path: []const u8, cwd_path: []const u8) []const u8 {
495 if (builtin.target.os.tag == .wasi) {
496 if (sub_path.len == 0) return ".";
497 assert(!fs.path.isAbsolute(sub_path));
498 return sub_path;
499 }
500 assert(fs.path.isAbsolute(sub_path));
501 if (!std.mem.startsWith(u8, sub_path, cwd_path)) return sub_path;
502 if (sub_path.len == cwd_path.len) return "."; // the strings are equal
503 if (sub_path[cwd_path.len] != fs.path.sep) return sub_path; // last component before cwd differs
504 return sub_path[cwd_path.len + 1 ..]; // remove '/path/to/cwd/' prefix
505 }
506
507 /// From an unresolved path (which can be made of multiple not-yet-joined strings), construct a
508 /// canonical `Path`.
509 pub fn fromUnresolved(gpa: Allocator, dirs: Compilation.Directories, unresolved_parts: []const []const u8) Allocator.Error!Path {
510 const resolved = try introspect.resolvePath(gpa, dirs.cwd, unresolved_parts);
511 errdefer gpa.free(resolved);
512
513 // If, for instance, `dirs.local_cache.path` is within the lib dir, it must take priority,
514 // so that we prefer `.root = .local_cache` over `.root = .zig_lib`. The easiest way to do
515 // this is simply to prioritize the longest root path.
516 const PathAndRoot = struct { ?[]const u8, Root };
517 var roots: [3]PathAndRoot = .{
518 .{ dirs.zig_lib.path, .zig_lib },
519 .{ dirs.global_cache.path, .global_cache },
520 .{ dirs.local_cache.path, .local_cache },
521 };
522 // This must be a stable sort, because the global and local cache directories may be the same, in
523 // which case we need to make a consistent choice.
524 std.mem.sort(PathAndRoot, &roots, {}, struct {
525 fn lessThan(_: void, lhs: PathAndRoot, rhs: PathAndRoot) bool {
526 const lhs_path_len = if (lhs[0]) |p| p.len else 0;
527 const rhs_path_len = if (rhs[0]) |p| p.len else 0;
528 return lhs_path_len > rhs_path_len; // '>' instead of '<' to sort descending
529 }
530 }.lessThan);
531
532 for (roots) |path_and_root| {
533 const opt_root_path, const root = path_and_root;
534 const root_path = opt_root_path orelse {
535 // This root is the cwd.
536 if (!fs.path.isAbsolute(resolved)) {
537 return .{
538 .root = root,
539 .sub_path = resolved,
540 };
541 }
542 continue;
543 };
544 if (!mem.startsWith(u8, resolved, root_path)) continue;
545 const sub: []const u8 = if (resolved.len != root_path.len) sub: {
546 // Check the trailing slash, so that we don't match e.g. `/foo/bar` with `/foo/barren`
547 if (resolved[root_path.len] != fs.path.sep) continue;
548 break :sub resolved[root_path.len + 1 ..];
549 } else "";
550 const duped = try gpa.dupe(u8, sub);
551 gpa.free(resolved);
552 return .{ .root = root, .sub_path = duped };
553 }
554
555 // We're not relative to any root, so we will use an absolute path (on targets where they are available).
556
557 if (builtin.target.os.tag == .wasi or fs.path.isAbsolute(resolved)) {
558 // `resolved` is already absolute (or we're on WASI, where absolute paths don't really exist).
559 return .{ .root = .none, .sub_path = resolved };
560 }
561
562 if (resolved.len == 0) {
563 // We just need the cwd path, no trailing separator. Note that `gpa.free(resolved)` would be a nop.
564 return .{ .root = .none, .sub_path = try gpa.dupe(u8, dirs.cwd) };
565 }
566
567 // We need to make an absolute path. Because `resolved` came from `introspect.resolvePath`, we can just
568 // join the paths with a simple format string.
569 const abs_path = try std.fmt.allocPrint(gpa, "{s}{c}{s}", .{ dirs.cwd, fs.path.sep, resolved });
570 gpa.free(resolved);
571 return .{ .root = .none, .sub_path = abs_path };
572 }
573
574 /// Constructs a canonical `Path` representing `sub_path` relative to `root`.
575 ///
576 /// If `sub_path` is resolved, this is almost like directly constructing a `Path`, but this
577 /// function also canonicalizes the result, which matters because `sub_path` may move us into
578 /// a different root.
579 ///
580 /// For instance, if the Zig lib directory is inside the global cache, passing `root` as
581 /// `.global_cache` could still end up returning a `Path` with `Path.root == .zig_lib`.
582 pub fn fromRoot(
583 gpa: Allocator,
584 dirs: Compilation.Directories,
585 root: Path.Root,
586 sub_path: []const u8,
587 ) Allocator.Error!Path {
588 // Currently, this just wraps `fromUnresolved` for simplicity. A more efficient impl is
589 // probably possible if this function ever ends up impacting performance somehow.
590 return .fromUnresolved(gpa, dirs, &.{
591 switch (root) {
592 .zig_lib => dirs.zig_lib.path orelse "",
593 .global_cache => dirs.global_cache.path orelse "",
594 .local_cache => dirs.local_cache.path orelse "",
595 .none => "",
596 },
597 sub_path,
598 });
599 }
600
601 /// Given a `Path` and an (unresolved) sub path relative to it, construct a `Path` representing
602 /// the joined path `p/sub_path`. Note that, like with `fromRoot`, the `sub_path` might cause us
603 /// to move into a different `Path.Root`.
604 pub fn join(
605 p: Path,
606 gpa: Allocator,
607 dirs: Compilation.Directories,
608 sub_path: []const u8,
609 ) Allocator.Error!Path {
610 // Currently, this just wraps `fromUnresolved` for simplicity. A more efficient impl is
611 // probably possible if this function ever ends up impacting performance somehow.
612 return .fromUnresolved(gpa, dirs, &.{
613 switch (p.root) {
614 .zig_lib => dirs.zig_lib.path orelse "",
615 .global_cache => dirs.global_cache.path orelse "",
616 .local_cache => dirs.local_cache.path orelse "",
617 .none => "",
618 },
619 p.sub_path,
620 sub_path,
621 });
622 }
623
624 /// Like `join`, but `sub_path` is relative to the dirname of `p` instead of `p` itself.
625 pub fn upJoin(
626 p: Path,
627 gpa: Allocator,
628 dirs: Compilation.Directories,
629 sub_path: []const u8,
630 ) Allocator.Error!Path {
631 return .fromUnresolved(gpa, dirs, &.{
632 switch (p.root) {
633 .zig_lib => dirs.zig_lib.path orelse "",
634 .global_cache => dirs.global_cache.path orelse "",
635 .local_cache => dirs.local_cache.path orelse "",
636 .none => "",
637 },
638 p.sub_path,
639 "..",
640 sub_path,
641 });
642 }
643
644 pub fn toCachePath(p: Path, dirs: Directories) Cache.Path {
645 const root_dir: Cache.Directory = switch (p.root) {
646 .zig_lib => dirs.zig_lib,
647 .global_cache => dirs.global_cache,
648 .local_cache => dirs.local_cache,
649 else => {
650 const cwd_sub_path = absToCwdRelative(p.sub_path, dirs.cwd);
651 return .{
652 .root_dir = .cwd(),
653 .sub_path = cwd_sub_path,
654 };
655 },
656 };
657 assert(!fs.path.isAbsolute(p.sub_path));
658 return .{
659 .root_dir = root_dir,
660 .sub_path = p.sub_path,
661 };
662 }
663
664 /// This should not be used for most of the compiler pipeline, but is useful when emitting
665 /// paths from the compilation (e.g. in debug info), because they will not depend on the cwd.
666 /// The returned path is owned by the caller and allocated into `gpa`.
667 pub fn toAbsolute(p: Path, dirs: Directories, gpa: Allocator) Allocator.Error![]u8 {
668 const root_path: []const u8 = switch (p.root) {
669 .zig_lib => dirs.zig_lib.path orelse "",
670 .global_cache => dirs.global_cache.path orelse "",
671 .local_cache => dirs.local_cache.path orelse "",
672 .none => "",
673 };
674 return fs.path.resolve(gpa, &.{
675 dirs.cwd,
676 root_path,
677 p.sub_path,
678 });
679 }
680
681 pub fn isNested(inner: Path, outer: Path) union(enum) {
682 /// Value is the sub path, which is a sub-slice of `inner.sub_path`.
683 yes: []const u8,
684 no,
685 different_roots,
686 } {
687 if (inner.root != outer.root) return .different_roots;
688 if (!mem.startsWith(u8, inner.sub_path, outer.sub_path)) return .no;
689 if (inner.sub_path.len == outer.sub_path.len) return .no;
690 if (outer.sub_path.len == 0) return .{ .yes = inner.sub_path };
691 if (inner.sub_path[outer.sub_path.len] != fs.path.sep) return .no;
692 return .{ .yes = inner.sub_path[outer.sub_path.len + 1 ..] };
693 }
694
695 /// Returns whether this `Path` is illegal to have as a user-imported `Zcu.File` (including
696 /// as the root of a module). Such paths exist in directories which the Zig compiler treats
697 /// specially, like 'global_cache/b/', which stores 'builtin.zig' files.
698 pub fn isIllegalZigImport(p: Path, gpa: Allocator, dirs: Directories) Allocator.Error!bool {
699 const zig_builtin_dir: Path = try .fromRoot(gpa, dirs, .global_cache, "b");
700 defer zig_builtin_dir.deinit(gpa);
701 return switch (p.isNested(zig_builtin_dir)) {
702 .yes => true,
703 .no, .different_roots => false,
704 };
705 }
706};
707
708pub const Directories = struct {
709 /// The string returned by `introspect.getResolvedCwd`. This is typically an absolute path,
710 /// but on WASI is the empty string "" instead, because WASI does not have absolute paths.
711 cwd: []const u8,
712 /// The Zig 'lib' directory.
713 /// `zig_lib.path` is resolved (`introspect.resolvePath`) or `null` for cwd.
714 /// Guaranteed to be a different path from `global_cache` and `local_cache`.
715 zig_lib: Cache.Directory,
716 /// The global Zig cache directory.
717 /// `global_cache.path` is resolved (`introspect.resolvePath`) or `null` for cwd.
718 global_cache: Cache.Directory,
719 /// The local Zig cache directory.
720 /// `local_cache.path` is resolved (`introspect.resolvePath`) or `null` for cwd.
721 /// This may be the same as `global_cache`.
722 local_cache: Cache.Directory,
723
724 pub fn deinit(dirs: *Directories) void {
725 // The local and global caches could be the same.
726 const close_local = dirs.local_cache.handle.fd != dirs.global_cache.handle.fd;
727
728 dirs.global_cache.handle.close();
729 if (close_local) dirs.local_cache.handle.close();
730 dirs.zig_lib.handle.close();
731 }
732
733 /// Returns a `Directories` where `local_cache` is replaced with `global_cache`, intended for
734 /// use by sub-compilations (e.g. compiler_rt). Do not `deinit` the returned `Directories`; it
735 /// shares handles with `dirs`.
736 pub fn withoutLocalCache(dirs: Directories) Directories {
737 return .{
738 .cwd = dirs.cwd,
739 .zig_lib = dirs.zig_lib,
740 .global_cache = dirs.global_cache,
741 .local_cache = dirs.global_cache,
742 };
743 }
744
745 /// Uses `std.process.fatal` on error conditions.
746 pub fn init(
747 arena: Allocator,
748 override_zig_lib: ?[]const u8,
749 override_global_cache: ?[]const u8,
750 local_cache_strat: union(enum) {
751 override: []const u8,
752 search,
753 global,
754 },
755 wasi_preopens: switch (builtin.target.os.tag) {
756 .wasi => fs.wasi.Preopens,
757 else => void,
758 },
759 self_exe_path: switch (builtin.target.os.tag) {
760 .wasi => void,
761 else => []const u8,
762 },
763 ) Directories {
764 const wasi = builtin.target.os.tag == .wasi;
765
766 const cwd = introspect.getResolvedCwd(arena) catch |err| {
767 fatal("unable to get cwd: {s}", .{@errorName(err)});
768 };
769
770 const zig_lib: Cache.Directory = d: {
771 if (override_zig_lib) |path| break :d openUnresolved(arena, cwd, path, .@"zig lib");
772 if (wasi) break :d openWasiPreopen(wasi_preopens, "/lib");
773 break :d introspect.findZigLibDirFromSelfExe(arena, cwd, self_exe_path) catch |err| {
774 fatal("unable to find zig installation directory '{s}': {s}", .{ self_exe_path, @errorName(err) });
775 };
776 };
777
778 const global_cache: Cache.Directory = d: {
779 if (override_global_cache) |path| break :d openUnresolved(arena, cwd, path, .@"global cache");
780 if (wasi) break :d openWasiPreopen(wasi_preopens, "/cache");
781 const path = introspect.resolveGlobalCacheDir(arena) catch |err| {
782 fatal("unable to resolve zig cache directory: {s}", .{@errorName(err)});
783 };
784 break :d openUnresolved(arena, cwd, path, .@"global cache");
785 };
786
787 const local_cache: Cache.Directory = switch (local_cache_strat) {
788 .override => |path| openUnresolved(arena, cwd, path, .@"local cache"),
789 .search => d: {
790 const maybe_path = introspect.resolveSuitableLocalCacheDir(arena, cwd) catch |err| {
791 fatal("unable to resolve zig cache directory: {s}", .{@errorName(err)});
792 };
793 const path = maybe_path orelse break :d global_cache;
794 break :d openUnresolved(arena, cwd, path, .@"local cache");
795 },
796 .global => global_cache,
797 };
798
799 if (std.mem.eql(u8, zig_lib.path orelse "", global_cache.path orelse "")) {
800 fatal("zig lib directory '{f}' cannot be equal to global cache directory '{f}'", .{ zig_lib, global_cache });
801 }
802 if (std.mem.eql(u8, zig_lib.path orelse "", local_cache.path orelse "")) {
803 fatal("zig lib directory '{f}' cannot be equal to local cache directory '{f}'", .{ zig_lib, local_cache });
804 }
805
806 return .{
807 .cwd = cwd,
808 .zig_lib = zig_lib,
809 .global_cache = global_cache,
810 .local_cache = local_cache,
811 };
812 }
813 fn openWasiPreopen(preopens: fs.wasi.Preopens, name: []const u8) Cache.Directory {
814 return .{
815 .path = if (std.mem.eql(u8, name, ".")) null else name,
816 .handle = .{
817 .fd = preopens.find(name) orelse fatal("WASI preopen not found: '{s}'", .{name}),
818 },
819 };
820 }
821 fn openUnresolved(arena: Allocator, cwd: []const u8, unresolved_path: []const u8, thing: enum { @"zig lib", @"global cache", @"local cache" }) Cache.Directory {
822 const path = introspect.resolvePath(arena, cwd, &.{unresolved_path}) catch |err| {
823 fatal("unable to resolve {s} directory: {s}", .{ @tagName(thing), @errorName(err) });
824 };
825 const nonempty_path = if (path.len == 0) "." else path;
826 const handle_or_err = switch (thing) {
827 .@"zig lib" => fs.cwd().openDir(nonempty_path, .{}),
828 .@"global cache", .@"local cache" => fs.cwd().makeOpenPath(nonempty_path, .{}),
829 };
830 return .{
831 .path = if (path.len == 0) null else path,
832 .handle = handle_or_err catch |err| {
833 const extra_str: []const u8 = e: {
834 if (thing == .@"global cache") switch (err) {
835 error.AccessDenied, error.ReadOnlyFileSystem => break :e "\n" ++
836 "If this location is not writable then consider specifying an alternative with " ++
837 "the ZIG_GLOBAL_CACHE_DIR environment variable or the --global-cache-dir option.",
838 else => {},
839 };
840 break :e "";
841 };
842 fatal("unable to open {s} directory '{s}': {s}{s}", .{ @tagName(thing), nonempty_path, @errorName(err), extra_str });
843 },
844 };
845 }
846};
847
848/// This small wrapper function just checks whether debug extensions are enabled before checking
849/// `comp.debug_incremental`. It is inline so that comptime-known `false` propagates to the caller,
850/// preventing debugging features from making it into release builds of the compiler.
851pub inline fn debugIncremental(comp: *const Compilation) bool {
852 if (!build_options.enable_debug_extensions or builtin.single_threaded) return false;
853 return comp.debug_incremental;
854}
855
856pub const TimeReport = struct {
857 stats: std.Build.abi.time_report.CompileResult.Stats,
858
859 /// Allocated into `gpa`. The pass time statistics emitted by LLVM's "time-passes" option.
860 /// LLVM provides this data in ASCII form as a table, which can be directly shown to users.
861 ///
862 /// Ideally, we would be able to use `printAllJSONValues` to get *structured* data which we can
863 /// then display more nicely. Unfortunately, that function seems to trip an assertion on one of
864 /// the pass timer names at the time of writing.
865 llvm_pass_timings: []u8,
866
867 /// Key is a ZIR `declaration` instruction; value is the number of nanoseconds spent analyzing
868 /// it. This is the total across all instances of the generic parent namespace, and (if this is
869 /// a function) all generic instances of this function. It also includes time spent analyzing
870 /// function bodies if this is a function (generic or otherwise).
871 /// An entry not existing means the declaration has not been analyzed (so far).
872 decl_sema_info: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, struct {
873 ns: u64,
874 count: u32,
875 }),
876
877 /// Key is a ZIR `declaration` instruction which is a function or test; value is the number of
878 /// nanoseconds spent running codegen on it. As above, this is the total across all generic
879 /// instances, both of this function itself and of its parent namespace.
880 /// An entry not existing means the declaration has not been codegenned (so far).
881 /// Every key in `decl_codegen_ns` is also in `decl_sema_ns`.
882 decl_codegen_ns: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, u64),
883
884 /// Key is a ZIR `declaration` instruction which is anything other than a `comptime` decl; value
885 /// is the number of nanoseconds spent linking it into the binary. As above, this is the total
886 /// across all generic instances.
887 /// An entry not existing means the declaration has not been linked (so far).
888 /// Every key in `decl_link_ns` is also in `decl_sema_ns`.
889 decl_link_ns: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, u64),
890
891 pub fn deinit(tr: *TimeReport, gpa: Allocator) void {
892 tr.stats = undefined;
893 gpa.free(tr.llvm_pass_timings);
894 tr.decl_sema_info.deinit(gpa);
895 tr.decl_codegen_ns.deinit(gpa);
896 tr.decl_link_ns.deinit(gpa);
897 }
898
899 pub const init: TimeReport = .{
900 .stats = .init,
901 .llvm_pass_timings = &.{},
902 .decl_sema_info = .empty,
903 .decl_codegen_ns = .empty,
904 .decl_link_ns = .empty,
905 };
906};
907
908pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;
909pub const SemaError = Zcu.SemaError;
910
911pub const CrtFile = struct {
912 lock: Cache.Lock,
913 full_object_path: Cache.Path,
914
915 pub fn deinit(self: *CrtFile, gpa: Allocator) void {
916 self.lock.release();
917 gpa.free(self.full_object_path.sub_path);
918 self.* = undefined;
919 }
920};
921
922/// Supported languages for "zig clang -x <lang>".
923/// Loosely based on llvm-project/clang/include/clang/Driver/Types.def
924pub const LangToExt = std.StaticStringMap(FileExt).initComptime(.{
925 .{ "c", .c },
926 .{ "c-header", .h },
927 .{ "c++", .cpp },
928 .{ "c++-header", .hpp },
929 .{ "objective-c", .m },
930 .{ "objective-c-header", .hm },
931 .{ "objective-c++", .mm },
932 .{ "objective-c++-header", .hmm },
933 .{ "assembler", .assembly },
934 .{ "assembler-with-cpp", .assembly_with_cpp },
935});
936
937/// For passing to a C compiler.
938pub const CSourceFile = struct {
939 /// Many C compiler flags are determined by settings contained in the owning Module.
940 owner: *Package.Module,
941 src_path: []const u8,
942 extra_flags: []const []const u8 = &.{},
943 /// Same as extra_flags except they are not added to the Cache hash.
944 cache_exempt_flags: []const []const u8 = &.{},
945 /// This field is non-null if and only if the language was explicitly set
946 /// with "-x lang".
947 ext: ?FileExt = null,
948};
949
950/// For passing to resinator.
951pub const RcSourceFile = struct {
952 owner: *Package.Module,
953 src_path: []const u8,
954 extra_flags: []const []const u8 = &.{},
955};
956
957const Job = union(enum) {
958 /// Given the generated AIR for a function, put it onto the code generation queue.
959 /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that
960 /// all types are resolved before the linker task is queued.
961 /// If the backend does not support `Zcu.Feature.separate_thread`, codegen and linking happen immediately.
962 /// Before queueing this `Job`, increase the estimated total item count for both
963 /// `comp.zcu.?.codegen_prog_node` and `comp.link_prog_node`.
964 codegen_func: struct {
965 func: InternPool.Index,
966 /// The AIR emitted from analyzing `func`; owned by this `Job` in `gpa`.
967 air: Air,
968 },
969 /// Queue a `link.ZcuTask` to emit this non-function `Nav` into the output binary.
970 /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that
971 /// all types are resolved before the linker task is queued.
972 /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately.
973 /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`.
974 link_nav: InternPool.Nav.Index,
975 /// Queue a `link.ZcuTask` to emit debug information for this container type.
976 /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that
977 /// all types are resolved before the linker task is queued.
978 /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately.
979 /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`.
980 link_type: InternPool.Index,
981 /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`.
982 update_line_number: InternPool.TrackedInst.Index,
983 /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed.
984 /// This may be its first time being analyzed, or it may be outdated.
985 /// If the unit is a test function, an `analyze_func` job will then be queued.
986 analyze_comptime_unit: InternPool.AnalUnit,
987 /// This function must be semantically analyzed.
988 /// This may be its first time being analyzed, or it may be outdated.
989 /// After analysis, a `codegen_func` job will be queued.
990 /// These must be separate jobs to ensure any needed type resolution occurs *before* codegen.
991 /// This job is separate from `analyze_comptime_unit` because it has a different priority.
992 analyze_func: InternPool.Index,
993 /// The main source file for the module needs to be analyzed.
994 analyze_mod: *Package.Module,
995 /// Fully resolve the given `struct` or `union` type.
996 resolve_type_fully: InternPool.Index,
997
998 /// The value is the index into `windows_libs`.
999 windows_import_lib: usize,
1000
1001 const Tag = @typeInfo(Job).@"union".tag_type.?;
1002 fn stage(tag: Tag) usize {
1003 return switch (tag) {
1004 // Prioritize functions so that codegen can get to work on them on a
1005 // separate thread, while Sema goes back to its own work.
1006 .resolve_type_fully, .analyze_func, .codegen_func => 0,
1007 else => 1,
1008 };
1009 }
1010 comptime {
1011 // Job dependencies
1012 assert(stage(.resolve_type_fully) <= stage(.codegen_func));
1013 }
1014};
1015
1016pub const CObject = struct {
1017 /// Relative to cwd. Owned by arena.
1018 src: CSourceFile,
1019 status: union(enum) {
1020 new,
1021 success: struct {
1022 /// The outputted result. `sub_path` owned by gpa.
1023 object_path: Cache.Path,
1024 /// This is a file system lock on the cache hash manifest representing this
1025 /// object. It prevents other invocations of the Zig compiler from interfering
1026 /// with this object until released.
1027 lock: Cache.Lock,
1028 },
1029 /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects.
1030 failure,
1031 /// A transient failure happened when trying to compile the C Object; it may
1032 /// succeed if we try again. There may be a corresponding ErrorMsg in
1033 /// Compilation.failed_c_objects. If there is not, the failure is out of memory.
1034 failure_retryable,
1035 },
1036
1037 pub const Diag = struct {
1038 level: u32 = 0,
1039 category: u32 = 0,
1040 msg: []const u8 = &.{},
1041 src_loc: SrcLoc = .{},
1042 src_ranges: []const SrcRange = &.{},
1043 sub_diags: []const Diag = &.{},
1044
1045 pub const SrcLoc = struct {
1046 file: u32 = 0,
1047 line: u32 = 0,
1048 column: u32 = 0,
1049 offset: u32 = 0,
1050 };
1051
1052 pub const SrcRange = struct {
1053 start: SrcLoc = .{},
1054 end: SrcLoc = .{},
1055 };
1056
1057 pub fn deinit(diag: *Diag, gpa: Allocator) void {
1058 gpa.free(diag.msg);
1059 gpa.free(diag.src_ranges);
1060 for (diag.sub_diags) |sub_diag| {
1061 var sub_diag_mut = sub_diag;
1062 sub_diag_mut.deinit(gpa);
1063 }
1064 gpa.free(diag.sub_diags);
1065 diag.* = undefined;
1066 }
1067
1068 pub fn count(diag: *const Diag) u32 {
1069 var total: u32 = 1;
1070 for (diag.sub_diags) |sub_diag| total += sub_diag.count();
1071 return total;
1072 }
1073
1074 pub fn addToErrorBundle(diag: *const Diag, io: Io, eb: *ErrorBundle.Wip, bundle: Bundle, note: *u32) !void {
1075 const err_msg = try eb.addErrorMessage(try diag.toErrorMessage(io, eb, bundle, 0));
1076 eb.extra.items[note.*] = @intFromEnum(err_msg);
1077 note.* += 1;
1078 for (diag.sub_diags) |sub_diag| try sub_diag.addToErrorBundle(io, eb, bundle, note);
1079 }
1080
1081 pub fn toErrorMessage(
1082 diag: *const Diag,
1083 io: Io,
1084 eb: *ErrorBundle.Wip,
1085 bundle: Bundle,
1086 notes_len: u32,
1087 ) !ErrorBundle.ErrorMessage {
1088 var start = diag.src_loc.offset;
1089 var end = diag.src_loc.offset;
1090 for (diag.src_ranges) |src_range| {
1091 if (src_range.start.file == diag.src_loc.file and
1092 src_range.start.line == diag.src_loc.line)
1093 {
1094 start = @min(src_range.start.offset, start);
1095 }
1096 if (src_range.end.file == diag.src_loc.file and
1097 src_range.end.line == diag.src_loc.line)
1098 {
1099 end = @max(src_range.end.offset, end);
1100 }
1101 }
1102
1103 const file_name = bundle.file_names.get(diag.src_loc.file) orelse "";
1104 const source_line = source_line: {
1105 if (diag.src_loc.offset == 0 or diag.src_loc.column == 0) break :source_line 0;
1106
1107 const file = fs.cwd().openFile(file_name, .{}) catch break :source_line 0;
1108 defer file.close();
1109 var buffer: [1024]u8 = undefined;
1110 var file_reader = file.reader(io, &buffer);
1111 file_reader.seekTo(diag.src_loc.offset + 1 - diag.src_loc.column) catch break :source_line 0;
1112 var aw: Writer.Allocating = .init(eb.gpa);
1113 defer aw.deinit();
1114 _ = file_reader.interface.streamDelimiterEnding(&aw.writer, '\n') catch break :source_line 0;
1115 break :source_line try eb.addString(aw.written());
1116 };
1117
1118 return .{
1119 .msg = try eb.addString(diag.msg),
1120 .src_loc = try eb.addSourceLocation(.{
1121 .src_path = try eb.addString(file_name),
1122 .line = diag.src_loc.line -| 1,
1123 .column = diag.src_loc.column -| 1,
1124 .span_start = start,
1125 .span_main = diag.src_loc.offset,
1126 .span_end = end + 1,
1127 .source_line = source_line,
1128 }),
1129 .notes_len = notes_len,
1130 };
1131 }
1132
1133 pub const Bundle = struct {
1134 file_names: std.AutoArrayHashMapUnmanaged(u32, []const u8) = .empty,
1135 category_names: std.AutoArrayHashMapUnmanaged(u32, []const u8) = .empty,
1136 diags: []Diag = &.{},
1137
1138 pub fn destroy(bundle: *Bundle, gpa: Allocator) void {
1139 for (bundle.file_names.values()) |file_name| gpa.free(file_name);
1140 bundle.file_names.deinit(gpa);
1141 for (bundle.category_names.values()) |category_name| gpa.free(category_name);
1142 bundle.category_names.deinit(gpa);
1143 for (bundle.diags) |*diag| diag.deinit(gpa);
1144 gpa.free(bundle.diags);
1145 gpa.destroy(bundle);
1146 }
1147
1148 pub fn parse(gpa: Allocator, io: Io, path: []const u8) !*Bundle {
1149 const BlockId = enum(u32) {
1150 Meta = 8,
1151 Diag,
1152 _,
1153 };
1154 const RecordId = enum(u32) {
1155 Version = 1,
1156 DiagInfo,
1157 SrcRange,
1158 DiagFlag,
1159 CatName,
1160 FileName,
1161 FixIt,
1162 _,
1163 };
1164 const WipDiag = struct {
1165 level: u32 = 0,
1166 category: u32 = 0,
1167 msg: []const u8 = &.{},
1168 src_loc: SrcLoc = .{},
1169 src_ranges: std.ArrayList(SrcRange) = .empty,
1170 sub_diags: std.ArrayList(Diag) = .empty,
1171
1172 fn deinit(wip_diag: *@This(), allocator: Allocator) void {
1173 allocator.free(wip_diag.msg);
1174 wip_diag.src_ranges.deinit(allocator);
1175 for (wip_diag.sub_diags.items) |*sub_diag| sub_diag.deinit(allocator);
1176 wip_diag.sub_diags.deinit(allocator);
1177 wip_diag.* = undefined;
1178 }
1179 };
1180
1181 var buffer: [1024]u8 = undefined;
1182 const file = try fs.cwd().openFile(path, .{});
1183 defer file.close();
1184 var file_reader = file.reader(io, &buffer);
1185 var bc = std.zig.llvm.BitcodeReader.init(gpa, .{ .reader = &file_reader.interface });
1186 defer bc.deinit();
1187
1188 var file_names: std.AutoArrayHashMapUnmanaged(u32, []const u8) = .empty;
1189 errdefer {
1190 for (file_names.values()) |file_name| gpa.free(file_name);
1191 file_names.deinit(gpa);
1192 }
1193
1194 var category_names: std.AutoArrayHashMapUnmanaged(u32, []const u8) = .empty;
1195 errdefer {
1196 for (category_names.values()) |category_name| gpa.free(category_name);
1197 category_names.deinit(gpa);
1198 }
1199
1200 var stack: std.ArrayList(WipDiag) = .empty;
1201 defer {
1202 for (stack.items) |*wip_diag| wip_diag.deinit(gpa);
1203 stack.deinit(gpa);
1204 }
1205 try stack.append(gpa, .{});
1206
1207 try bc.checkMagic("DIAG");
1208 while (try bc.next()) |item| switch (item) {
1209 .start_block => |block| switch (@as(BlockId, @enumFromInt(block.id))) {
1210 .Meta => if (stack.items.len > 0) try bc.skipBlock(block),
1211 .Diag => try stack.append(gpa, .{}),
1212 _ => try bc.skipBlock(block),
1213 },
1214 .record => |record| switch (@as(RecordId, @enumFromInt(record.id))) {
1215 .Version => if (record.operands[0] != 2) return error.InvalidVersion,
1216 .DiagInfo => {
1217 const top = &stack.items[stack.items.len - 1];
1218 top.level = @intCast(record.operands[0]);
1219 top.src_loc = .{
1220 .file = @intCast(record.operands[1]),
1221 .line = @intCast(record.operands[2]),
1222 .column = @intCast(record.operands[3]),
1223 .offset = @intCast(record.operands[4]),
1224 };
1225 top.category = @intCast(record.operands[5]);
1226 top.msg = try gpa.dupe(u8, record.blob);
1227 },
1228 .SrcRange => try stack.items[stack.items.len - 1].src_ranges.append(gpa, .{
1229 .start = .{
1230 .file = @intCast(record.operands[0]),
1231 .line = @intCast(record.operands[1]),
1232 .column = @intCast(record.operands[2]),
1233 .offset = @intCast(record.operands[3]),
1234 },
1235 .end = .{
1236 .file = @intCast(record.operands[4]),
1237 .line = @intCast(record.operands[5]),
1238 .column = @intCast(record.operands[6]),
1239 .offset = @intCast(record.operands[7]),
1240 },
1241 }),
1242 .DiagFlag => {},
1243 .CatName => {
1244 try category_names.ensureUnusedCapacity(gpa, 1);
1245 category_names.putAssumeCapacity(
1246 @intCast(record.operands[0]),
1247 try gpa.dupe(u8, record.blob),
1248 );
1249 },
1250 .FileName => {
1251 try file_names.ensureUnusedCapacity(gpa, 1);
1252 file_names.putAssumeCapacity(
1253 @intCast(record.operands[0]),
1254 try gpa.dupe(u8, record.blob),
1255 );
1256 },
1257 .FixIt => {},
1258 _ => {},
1259 },
1260 .end_block => |block| switch (@as(BlockId, @enumFromInt(block.id))) {
1261 .Meta => {},
1262 .Diag => {
1263 var wip_diag = stack.pop().?;
1264 errdefer wip_diag.deinit(gpa);
1265
1266 const src_ranges = try wip_diag.src_ranges.toOwnedSlice(gpa);
1267 errdefer gpa.free(src_ranges);
1268
1269 const sub_diags = try wip_diag.sub_diags.toOwnedSlice(gpa);
1270 errdefer {
1271 for (sub_diags) |*sub_diag| sub_diag.deinit(gpa);
1272 gpa.free(sub_diags);
1273 }
1274
1275 try stack.items[stack.items.len - 1].sub_diags.append(gpa, .{
1276 .level = wip_diag.level,
1277 .category = wip_diag.category,
1278 .msg = wip_diag.msg,
1279 .src_loc = wip_diag.src_loc,
1280 .src_ranges = src_ranges,
1281 .sub_diags = sub_diags,
1282 });
1283 },
1284 _ => {},
1285 },
1286 };
1287
1288 const bundle = try gpa.create(Bundle);
1289 assert(stack.items.len == 1);
1290 bundle.* = .{
1291 .file_names = file_names,
1292 .category_names = category_names,
1293 .diags = try stack.items[0].sub_diags.toOwnedSlice(gpa),
1294 };
1295 return bundle;
1296 }
1297
1298 pub fn addToErrorBundle(bundle: Bundle, io: Io, eb: *ErrorBundle.Wip) !void {
1299 for (bundle.diags) |diag| {
1300 const notes_len = diag.count() - 1;
1301 try eb.addRootErrorMessage(try diag.toErrorMessage(io, eb, bundle, notes_len));
1302 if (notes_len > 0) {
1303 var note = try eb.reserveNotes(notes_len);
1304 for (diag.sub_diags) |sub_diag|
1305 try sub_diag.addToErrorBundle(io, eb, bundle, ¬e);
1306 }
1307 }
1308 }
1309 };
1310 };
1311
1312 /// Returns if there was failure.
1313 pub fn clearStatus(self: *CObject, gpa: Allocator) bool {
1314 switch (self.status) {
1315 .new => return false,
1316 .failure, .failure_retryable => {
1317 self.status = .new;
1318 return true;
1319 },
1320 .success => |*success| {
1321 gpa.free(success.object_path.sub_path);
1322 success.lock.release();
1323 self.status = .new;
1324 return false;
1325 },
1326 }
1327 }
1328
1329 pub fn destroy(self: *CObject, gpa: Allocator) void {
1330 _ = self.clearStatus(gpa);
1331 gpa.destroy(self);
1332 }
1333};
1334
1335pub const Win32Resource = struct {
1336 /// Relative to cwd. Owned by arena.
1337 src: union(enum) {
1338 rc: RcSourceFile,
1339 manifest: []const u8,
1340 },
1341 status: union(enum) {
1342 new,
1343 success: struct {
1344 /// The outputted result. Owned by gpa.
1345 res_path: []u8,
1346 /// This is a file system lock on the cache hash manifest representing this
1347 /// object. It prevents other invocations of the Zig compiler from interfering
1348 /// with this object until released.
1349 lock: Cache.Lock,
1350 },
1351 /// There will be a corresponding ErrorMsg in Compilation.failed_win32_resources.
1352 failure,
1353 /// A transient failure happened when trying to compile the resource file; it may
1354 /// succeed if we try again. There may be a corresponding ErrorMsg in
1355 /// Compilation.failed_win32_resources. If there is not, the failure is out of memory.
1356 failure_retryable,
1357 },
1358
1359 /// Returns true if there was failure.
1360 pub fn clearStatus(self: *Win32Resource, gpa: Allocator) bool {
1361 switch (self.status) {
1362 .new => return false,
1363 .failure, .failure_retryable => {
1364 self.status = .new;
1365 return true;
1366 },
1367 .success => |*success| {
1368 gpa.free(success.res_path);
1369 success.lock.release();
1370 self.status = .new;
1371 return false;
1372 },
1373 }
1374 }
1375
1376 pub fn destroy(self: *Win32Resource, gpa: Allocator) void {
1377 _ = self.clearStatus(gpa);
1378 gpa.destroy(self);
1379 }
1380};
1381
1382pub const MiscTask = enum {
1383 open_output,
1384 write_builtin_zig,
1385 rename_results,
1386 check_whole_cache,
1387 glibc_crt_file,
1388 glibc_shared_objects,
1389 musl_crt_file,
1390 freebsd_crt_file,
1391 freebsd_shared_objects,
1392 netbsd_crt_file,
1393 netbsd_shared_objects,
1394 mingw_crt_file,
1395 windows_import_lib,
1396 libunwind,
1397 libcxx,
1398 libcxxabi,
1399 libtsan,
1400 libubsan,
1401 libfuzzer,
1402 wasi_libc_crt_file,
1403 compiler_rt,
1404 libzigc,
1405 analyze_mod,
1406 docs_copy,
1407 docs_wasm,
1408
1409 @"musl crt1.o",
1410 @"musl rcrt1.o",
1411 @"musl Scrt1.o",
1412 @"musl libc.a",
1413 @"musl libc.so",
1414
1415 @"wasi crt1-reactor.o",
1416 @"wasi crt1-command.o",
1417 @"wasi libc.a",
1418
1419 @"glibc Scrt1.o",
1420 @"glibc libc_nonshared.a",
1421 @"glibc shared object",
1422
1423 @"freebsd libc Scrt1.o",
1424 @"freebsd libc shared object",
1425
1426 @"netbsd libc Scrt0.o",
1427 @"netbsd libc shared object",
1428
1429 @"mingw-w64 crt2.o",
1430 @"mingw-w64 dllcrt2.o",
1431 @"mingw-w64 libmingw32.lib",
1432};
1433
1434pub const MiscError = struct {
1435 /// Allocated with gpa.
1436 msg: []u8,
1437 children: ?ErrorBundle = null,
1438
1439 pub fn deinit(misc_err: *MiscError, gpa: Allocator) void {
1440 gpa.free(misc_err.msg);
1441 if (misc_err.children) |*children| {
1442 children.deinit(gpa);
1443 }
1444 misc_err.* = undefined;
1445 }
1446};
1447
1448pub const cache_helpers = struct {
1449 pub fn addModule(hh: *Cache.HashHelper, mod: *const Package.Module) void {
1450 addResolvedTarget(hh, mod.resolved_target);
1451 hh.add(mod.optimize_mode);
1452 hh.add(mod.code_model);
1453 hh.add(mod.single_threaded);
1454 hh.add(mod.error_tracing);
1455 hh.add(mod.valgrind);
1456 hh.add(mod.pic);
1457 hh.add(mod.strip);
1458 hh.add(mod.omit_frame_pointer);
1459 hh.add(mod.stack_check);
1460 hh.add(mod.red_zone);
1461 hh.add(mod.sanitize_c);
1462 hh.add(mod.sanitize_thread);
1463 hh.add(mod.fuzz);
1464 hh.add(mod.unwind_tables);
1465 hh.add(mod.structured_cfg);
1466 hh.add(mod.no_builtin);
1467 hh.addListOfBytes(mod.cc_argv);
1468 }
1469
1470 pub fn addResolvedTarget(
1471 hh: *Cache.HashHelper,
1472 resolved_target: Package.Module.ResolvedTarget,
1473 ) void {
1474 const target = &resolved_target.result;
1475 hh.add(target.cpu.arch);
1476 hh.addBytes(target.cpu.model.name);
1477 hh.add(target.cpu.features.ints);
1478 hh.add(target.os.tag);
1479 hh.add(target.os.versionRange());
1480 hh.add(target.abi);
1481 hh.add(target.ofmt);
1482 hh.add(resolved_target.is_native_os);
1483 hh.add(resolved_target.is_native_abi);
1484 hh.add(resolved_target.is_explicit_dynamic_linker);
1485 }
1486
1487 pub fn addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?Config.DebugFormat) void {
1488 hh.add(x != null);
1489 addDebugFormat(hh, x orelse return);
1490 }
1491
1492 pub fn addDebugFormat(hh: *Cache.HashHelper, x: Config.DebugFormat) void {
1493 const tag: @typeInfo(Config.DebugFormat).@"union".tag_type.? = x;
1494 hh.add(tag);
1495 switch (x) {
1496 .strip, .code_view => {},
1497 .dwarf => |f| hh.add(f),
1498 }
1499 }
1500
1501 pub fn hashCSource(self: *Cache.Manifest, c_source: CSourceFile) !void {
1502 _ = try self.addFile(c_source.src_path, null);
1503 // Hash the extra flags, with special care to call addFile for file parameters.
1504 // TODO this logic can likely be improved by utilizing clang_options_data.zig.
1505 const file_args = [_][]const u8{"-include"};
1506 var arg_i: usize = 0;
1507 while (arg_i < c_source.extra_flags.len) : (arg_i += 1) {
1508 const arg = c_source.extra_flags[arg_i];
1509 self.hash.addBytes(arg);
1510 for (file_args) |file_arg| {
1511 if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) {
1512 arg_i += 1;
1513 _ = try self.addFile(c_source.extra_flags[arg_i], null);
1514 }
1515 }
1516 }
1517 }
1518};
1519
1520pub const ClangPreprocessorMode = enum {
1521 no,
1522 /// This means we are doing `zig cc -E -o <path>`.
1523 yes,
1524 /// This means we are doing `zig cc -E`.
1525 stdout,
1526 /// precompiled C header
1527 pch,
1528};
1529
1530pub const Framework = link.File.MachO.Framework;
1531pub const SystemLib = link.SystemLib;
1532
1533pub const CacheMode = enum {
1534 /// The results of this compilation are not cached. The compilation is always performed, and the
1535 /// results are emitted directly to their output locations. Temporary files will be placed in a
1536 /// temporary directory in the cache, but deleted after the compilation is done, unless they are
1537 /// needed for the output binary to work correctly.
1538 ///
1539 /// This mode is typically used for direct CLI invocations like `zig build-exe`, because such
1540 /// processes are typically low-level usages which would not make efficient use of the cache.
1541 none,
1542 /// The compilation is cached based only on the options given when creating the `Compilation`.
1543 /// In particular, Zig source file contents are not included in the cache manifest. This mode
1544 /// allows incremental compilation, because the old cached compilation state can be restored
1545 /// and the old binary patched up with the changes. All files, including temporary files, are
1546 /// stored in the cache directory like '<cache>/o/<hash>/'. Temporary files are not deleted.
1547 ///
1548 /// At the time of writing, incremental compilation is only supported with the `-fincremental`
1549 /// command line flag, so this mode is rarely used. However, it is required in order to use
1550 /// incremental compilation.
1551 incremental,
1552 /// The compilation is cached based on the `Compilation` options and every input, including Zig
1553 /// source files, linker inputs, and `@embedFile` targets. If any of them change, we will see a
1554 /// cache miss, and the entire compilation will be re-run. On a cache miss, we initially write
1555 /// all output files to a directory under '<cache>/tmp/', because we don't know the final
1556 /// manifest digest until the update is almost done. Once we can compute the final digest, this
1557 /// directory is moved to '<cache>/o/<hash>/'. Temporary files are not deleted.
1558 ///
1559 /// At the time of writing, this is the most commonly used cache mode: it is used by the build
1560 /// system (and any other parent using `--listen`) unless incremental compilation is enabled.
1561 /// Once incremental compilation is more mature, it will be replaced by `incremental` in many
1562 /// cases, but still has use cases, such as for release binaries, particularly globally cached
1563 /// artifacts like compiler_rt.
1564 whole,
1565};
1566
1567pub const ParentWholeCache = struct {
1568 manifest: *Cache.Manifest,
1569 mutex: *std.Thread.Mutex,
1570 prefix_map: [4]u8,
1571};
1572
1573const CacheUse = union(CacheMode) {
1574 none: *None,
1575 incremental: *Incremental,
1576 whole: *Whole,
1577
1578 const None = struct {
1579 /// User-requested artifacts are written directly to their output path in this cache mode.
1580 /// However, if we need to emit any temporary files, they are placed in this directory.
1581 /// We will recursively delete this directory at the end of this update if possible. This
1582 /// field is non-`null` only inside `update`.
1583 tmp_artifact_directory: ?Cache.Directory,
1584 };
1585
1586 const Incremental = struct {
1587 /// All output files, including artifacts and incremental compilation metadata, are placed
1588 /// in this directory, which is some 'o/<hash>' in a cache directory.
1589 artifact_directory: Cache.Directory,
1590 };
1591
1592 const Whole = struct {
1593 /// Since we don't open the output file until `update`, we must save these options for then.
1594 lf_open_opts: link.File.OpenOptions,
1595 /// This is a pointer to a local variable inside `update`.
1596 cache_manifest: ?*Cache.Manifest,
1597 cache_manifest_mutex: std.Thread.Mutex,
1598 /// This is non-`null` for most of the body of `update`. It is the temporary directory which
1599 /// we initially emit our artifacts to. After the main part of the update is done, it will
1600 /// be closed and moved to its final location, and this field set to `null`.
1601 tmp_artifact_directory: ?Cache.Directory,
1602 /// Prevents other processes from clobbering files in the output directory.
1603 lock: ?Cache.Lock,
1604
1605 fn releaseLock(whole: *Whole) void {
1606 if (whole.lock) |*lock| {
1607 lock.release();
1608 whole.lock = null;
1609 }
1610 }
1611
1612 fn moveLock(whole: *Whole) Cache.Lock {
1613 const result = whole.lock.?;
1614 whole.lock = null;
1615 return result;
1616 }
1617 };
1618
1619 fn deinit(cu: CacheUse) void {
1620 switch (cu) {
1621 .none => |none| {
1622 assert(none.tmp_artifact_directory == null);
1623 },
1624 .incremental => |incremental| {
1625 incremental.artifact_directory.handle.close();
1626 },
1627 .whole => |whole| {
1628 assert(whole.tmp_artifact_directory == null);
1629 whole.releaseLock();
1630 },
1631 }
1632 }
1633};
1634
1635pub const CreateOptions = struct {
1636 dirs: Directories,
1637 thread_pool: *ThreadPool,
1638 self_exe_path: ?[]const u8 = null,
1639
1640 /// Options that have been resolved by calling `resolveDefaults`.
1641 config: Compilation.Config,
1642
1643 root_mod: *Package.Module,
1644 /// Normally, `main_mod` and `root_mod` are the same. The exception is `zig
1645 /// test`, in which `root_mod` is the test runner, and `main_mod` is the
1646 /// user's source file which has the tests.
1647 main_mod: ?*Package.Module = null,
1648 /// This is provided so that the API user has a chance to tweak the
1649 /// per-module settings of the standard library.
1650 /// When this is null, a default configuration of the std lib is created
1651 /// based on the settings of root_mod.
1652 std_mod: ?*Package.Module = null,
1653 root_name: []const u8,
1654 sysroot: ?[]const u8 = null,
1655 cache_mode: CacheMode,
1656 emit_h: Emit = .no,
1657 emit_bin: Emit,
1658 emit_asm: Emit = .no,
1659 emit_implib: Emit = .no,
1660 emit_llvm_ir: Emit = .no,
1661 emit_llvm_bc: Emit = .no,
1662 emit_docs: Emit = .no,
1663 /// This field is intended to be removed.
1664 /// The ELF implementation no longer uses this data, however the MachO and COFF
1665 /// implementations still do.
1666 lib_directories: []const Cache.Directory = &.{},
1667 rpath_list: []const []const u8 = &[0][]const u8{},
1668 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,
1669 c_source_files: []const CSourceFile = &.{},
1670 rc_source_files: []const RcSourceFile = &.{},
1671 manifest_file: ?[]const u8 = null,
1672 rc_includes: std.zig.RcIncludes = .any,
1673 link_inputs: []const link.Input = &.{},
1674 framework_dirs: []const []const u8 = &[0][]const u8{},
1675 frameworks: []const Framework = &.{},
1676 windows_lib_names: []const []const u8 = &.{},
1677 /// This means that if the output mode is an executable it will be a
1678 /// Position Independent Executable. If the output mode is not an
1679 /// executable this field is ignored.
1680 want_compiler_rt: ?bool = null,
1681 want_ubsan_rt: ?bool = null,
1682 function_sections: bool = false,
1683 data_sections: bool = false,
1684 time_report: bool = false,
1685 stack_report: bool = false,
1686 link_eh_frame_hdr: bool = false,
1687 link_emit_relocs: bool = false,
1688 linker_script: ?[]const u8 = null,
1689 version_script: ?[]const u8 = null,
1690 linker_allow_undefined_version: bool = false,
1691 linker_enable_new_dtags: ?bool = null,
1692 soname: ?[]const u8 = null,
1693 linker_gc_sections: ?bool = null,
1694 linker_repro: ?bool = null,
1695 linker_allow_shlib_undefined: ?bool = null,
1696 linker_bind_global_refs_locally: ?bool = null,
1697 linker_import_symbols: bool = false,
1698 linker_import_table: bool = false,
1699 linker_export_table: bool = false,
1700 linker_initial_memory: ?u64 = null,
1701 linker_max_memory: ?u64 = null,
1702 linker_global_base: ?u64 = null,
1703 linker_export_symbol_names: []const []const u8 = &.{},
1704 linker_print_gc_sections: bool = false,
1705 linker_print_icf_sections: bool = false,
1706 linker_print_map: bool = false,
1707 llvm_opt_bisect_limit: i32 = -1,
1708 build_id: ?std.zig.BuildId = null,
1709 disable_c_depfile: bool = false,
1710 linker_z_nodelete: bool = false,
1711 linker_z_notext: bool = false,
1712 linker_z_defs: bool = false,
1713 linker_z_origin: bool = false,
1714 linker_z_now: bool = true,
1715 linker_z_relro: bool = true,
1716 linker_z_nocopyreloc: bool = false,
1717 linker_z_common_page_size: ?u64 = null,
1718 linker_z_max_page_size: ?u64 = null,
1719 linker_tsaware: bool = false,
1720 linker_nxcompat: bool = false,
1721 linker_dynamicbase: bool = true,
1722 linker_compress_debug_sections: ?std.zig.CompressDebugSections = null,
1723 linker_module_definition_file: ?[]const u8 = null,
1724 linker_sort_section: ?link.File.Lld.Elf.SortSection = null,
1725 major_subsystem_version: ?u16 = null,
1726 minor_subsystem_version: ?u16 = null,
1727 clang_passthrough_mode: bool = false,
1728 verbose_cc: bool = false,
1729 verbose_link: bool = false,
1730 verbose_air: bool = false,
1731 verbose_intern_pool: bool = false,
1732 verbose_generic_instances: bool = false,
1733 verbose_llvm_ir: ?[]const u8 = null,
1734 verbose_llvm_bc: ?[]const u8 = null,
1735 verbose_cimport: bool = false,
1736 verbose_llvm_cpu_features: bool = false,
1737 debug_compiler_runtime_libs: bool = false,
1738 debug_compile_errors: bool = false,
1739 debug_incremental: bool = false,
1740 /// Normally when you create a `Compilation`, Zig will automatically build
1741 /// and link in required dependencies, such as compiler-rt and libc. When
1742 /// building such dependencies themselves, this flag must be set to avoid
1743 /// infinite recursion.
1744 skip_linker_dependencies: bool = false,
1745 hash_style: link.File.Lld.Elf.HashStyle = .both,
1746 entry: Entry = .default,
1747 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .empty,
1748 stack_size: ?u64 = null,
1749 image_base: ?u64 = null,
1750 version: ?std.SemanticVersion = null,
1751 compatibility_version: ?std.SemanticVersion = null,
1752 libc_installation: ?*const LibCInstallation = null,
1753 native_system_include_paths: []const []const u8 = &.{},
1754 clang_preprocessor_mode: ClangPreprocessorMode = .no,
1755 reference_trace: ?u32 = null,
1756 test_filters: []const []const u8 = &.{},
1757 test_runner_path: ?[]const u8 = null,
1758 subsystem: ?std.zig.Subsystem = null,
1759 mingw_unicode_entry_point: bool = false,
1760 /// (Zig compiler development) Enable dumping linker's state as JSON.
1761 enable_link_snapshots: bool = false,
1762 /// (Darwin) Install name of the dylib
1763 install_name: ?[]const u8 = null,
1764 /// (Darwin) Path to entitlements file
1765 entitlements: ?[]const u8 = null,
1766 /// (Darwin) size of the __PAGEZERO segment
1767 pagezero_size: ?u64 = null,
1768 /// (Darwin) set minimum space for future expansion of the load commands
1769 headerpad_size: ?u32 = null,
1770 /// (Darwin) set enough space as if all paths were MATPATHLEN
1771 headerpad_max_install_names: bool = false,
1772 /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
1773 dead_strip_dylibs: bool = false,
1774 /// (Darwin) Force load all members of static archives that implement an Objective-C class or category
1775 force_load_objc: bool = false,
1776 /// Whether local symbols should be discarded from the symbol table.
1777 discard_local_symbols: bool = false,
1778 /// (Windows) PDB source path prefix to instruct the linker how to resolve relative
1779 /// paths when consolidating CodeView streams into a single PDB file.
1780 pdb_source_path: ?[]const u8 = null,
1781 /// (Windows) PDB output path
1782 pdb_out_path: ?[]const u8 = null,
1783 error_limit: ?Zcu.ErrorInt = null,
1784 global_cc_argv: []const []const u8 = &.{},
1785
1786 /// Tracks all files that can cause the Compilation to be invalidated and need a rebuild.
1787 file_system_inputs: ?*std.ArrayList(u8) = null,
1788
1789 parent_whole_cache: ?ParentWholeCache = null,
1790
1791 pub const Entry = link.File.OpenOptions.Entry;
1792
1793 /// Which fields are valid depends on the `cache_mode` given.
1794 pub const Emit = union(enum) {
1795 /// Do not emit this file. Always valid.
1796 no,
1797 /// Emit this file into its default name in the cache directory.
1798 /// Requires `cache_mode` to not be `.none`.
1799 yes_cache,
1800 /// Emit this file to the given path (absolute or cwd-relative).
1801 /// Requires `cache_mode` to be `.none`.
1802 yes_path: []const u8,
1803
1804 fn resolve(emit: Emit, arena: Allocator, opts: *const CreateOptions, ea: std.zig.EmitArtifact) Allocator.Error!?[]const u8 {
1805 switch (emit) {
1806 .no => return null,
1807 .yes_cache => {
1808 assert(opts.cache_mode != .none);
1809 return try ea.cacheName(arena, .{
1810 .root_name = opts.root_name,
1811 .target = &opts.root_mod.resolved_target.result,
1812 .output_mode = opts.config.output_mode,
1813 .link_mode = opts.config.link_mode,
1814 .version = opts.version,
1815 });
1816 },
1817 .yes_path => |path| {
1818 assert(opts.cache_mode == .none);
1819 return try arena.dupe(u8, path);
1820 },
1821 }
1822 }
1823 };
1824};
1825
1826fn addModuleTableToCacheHash(
1827 zcu: *Zcu,
1828 arena: Allocator,
1829 hash: *Cache.HashHelper,
1830 hash_type: union(enum) { path_bytes, files: *Cache.Manifest },
1831) error{
1832 OutOfMemory,
1833 Unexpected,
1834 CurrentWorkingDirectoryUnlinked,
1835}!void {
1836 assert(zcu.module_roots.count() != 0); // module_roots is populated
1837
1838 for (zcu.module_roots.keys(), zcu.module_roots.values()) |mod, opt_mod_root_file| {
1839 if (mod == zcu.std_mod) continue; // redundant
1840 if (opt_mod_root_file.unwrap()) |mod_root_file| {
1841 if (zcu.fileByIndex(mod_root_file).is_builtin) continue; // redundant
1842 }
1843 cache_helpers.addModule(hash, mod);
1844 switch (hash_type) {
1845 .path_bytes => {
1846 hash.add(mod.root.root);
1847 hash.addBytes(mod.root.sub_path);
1848 hash.addBytes(mod.root_src_path);
1849 },
1850 .files => |man| if (mod.root_src_path.len != 0) {
1851 const root_src_path = try mod.root.toCachePath(zcu.comp.dirs).join(arena, mod.root_src_path);
1852 _ = try man.addFilePath(root_src_path, null);
1853 },
1854 }
1855 hash.addListOfBytes(mod.deps.keys());
1856 }
1857}
1858
1859const RtStrat = enum { none, lib, obj, zcu, dyn_lib };
1860
1861pub const CreateDiagnostic = union(enum) {
1862 export_table_import_table_conflict,
1863 emit_h_without_zcu,
1864 illegal_zig_import,
1865 cross_libc_unavailable,
1866 find_native_libc: std.zig.LibCInstallation.FindError,
1867 libc_installation_missing_crt_dir,
1868 create_cache_path: CreateCachePath,
1869 open_output_bin: link.File.OpenError,
1870 pub const CreateCachePath = struct {
1871 which: enum { local, global },
1872 sub: []const u8,
1873 err: (fs.Dir.MakeError || fs.Dir.OpenError || fs.Dir.StatFileError),
1874 };
1875 pub fn format(diag: CreateDiagnostic, w: *Writer) Writer.Error!void {
1876 switch (diag) {
1877 .export_table_import_table_conflict => try w.writeAll("'--import-table' and '--export-table' cannot be used together"),
1878 .emit_h_without_zcu => try w.writeAll("cannot emit C header with no Zig source files"),
1879 .illegal_zig_import => try w.writeAll("this compiler implementation does not support importing the root source file of a provided module"),
1880 .cross_libc_unavailable => try w.writeAll("unable to provide libc for this target"),
1881 .find_native_libc => |err| try w.print("failed to find libc installation: {t}", .{err}),
1882 .libc_installation_missing_crt_dir => try w.writeAll("libc installation is missing crt directory"),
1883 .create_cache_path => |cache| try w.print("failed to create path '{s}' in {t} cache directory: {t}", .{
1884 cache.sub,
1885 cache.which,
1886 cache.err,
1887 }),
1888 .open_output_bin => |err| try w.print("failed to open output binary: {t}", .{err}),
1889 }
1890 }
1891
1892 fn fail(out: *CreateDiagnostic, result: CreateDiagnostic) error{CreateFail} {
1893 out.* = result;
1894 return error.CreateFail;
1895 }
1896};
1897pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, options: CreateOptions) error{
1898 OutOfMemory,
1899 Unexpected,
1900 CurrentWorkingDirectoryUnlinked,
1901 /// An error has been stored to `diag`.
1902 CreateFail,
1903}!*Compilation {
1904 const output_mode = options.config.output_mode;
1905 const is_dyn_lib = switch (output_mode) {
1906 .Obj, .Exe => false,
1907 .Lib => options.config.link_mode == .dynamic,
1908 };
1909 const is_exe_or_dyn_lib = switch (output_mode) {
1910 .Obj => false,
1911 .Lib => is_dyn_lib,
1912 .Exe => true,
1913 };
1914
1915 if (options.linker_export_table and options.linker_import_table) {
1916 return diag.fail(.export_table_import_table_conflict);
1917 }
1918
1919 const have_zcu = options.config.have_zcu;
1920 const use_llvm = options.config.use_llvm;
1921 const target = &options.root_mod.resolved_target.result;
1922
1923 const comp: *Compilation = comp: {
1924 // We put the `Compilation` itself in the arena. Freeing the arena will free the module.
1925 // It's initialized later after we prepare the initialization options.
1926 const root_name = try arena.dupeZ(u8, options.root_name);
1927
1928 // The "any" values provided by resolved config only account for
1929 // explicitly-provided settings. We now make them additionally account
1930 // for default setting resolution.
1931 const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables != .none;
1932 const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded;
1933 const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread;
1934 const any_sanitize_c: std.zig.SanitizeC = switch (options.config.any_sanitize_c) {
1935 .off => options.root_mod.sanitize_c,
1936 .trap => if (options.root_mod.sanitize_c == .full)
1937 .full
1938 else
1939 .trap,
1940 .full => .full,
1941 };
1942 const any_fuzz = options.config.any_fuzz or options.root_mod.fuzz;
1943
1944 const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables;
1945 const build_id = options.build_id orelse .none;
1946
1947 const link_libc = options.config.link_libc;
1948
1949 const libc_dirs = std.zig.LibCDirs.detect(
1950 arena,
1951 options.dirs.zig_lib.path.?,
1952 target,
1953 options.root_mod.resolved_target.is_native_abi,
1954 link_libc,
1955 options.libc_installation,
1956 ) catch |err| switch (err) {
1957 error.OutOfMemory => |e| return e,
1958 // Every other error is specifically related to finding the native installation
1959 else => |e| return diag.fail(.{ .find_native_libc = e }),
1960 };
1961
1962 const sysroot = options.sysroot orelse libc_dirs.sysroot;
1963
1964 const compiler_rt_strat: RtStrat = s: {
1965 if (options.skip_linker_dependencies) break :s .none;
1966 const want = options.want_compiler_rt orelse is_exe_or_dyn_lib;
1967 if (!want) break :s .none;
1968 const need_llvm = switch (target_util.canBuildLibCompilerRt(target)) {
1969 .no => break :s .none, // impossible to build
1970 .yes => false,
1971 .llvm_only => true,
1972 };
1973 if (have_zcu and (!need_llvm or use_llvm)) {
1974 if (output_mode == .Obj) break :s .zcu;
1975 switch (target_util.zigBackend(target, use_llvm)) {
1976 else => {},
1977 .stage2_aarch64, .stage2_x86_64 => if (target.ofmt == .coff) {
1978 break :s if (is_exe_or_dyn_lib and build_options.have_llvm) .dyn_lib else .zcu;
1979 },
1980 }
1981 }
1982 if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm
1983 if (is_exe_or_dyn_lib) break :s .lib;
1984 break :s .obj;
1985 };
1986
1987 if (compiler_rt_strat == .zcu) {
1988 // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");`
1989 // injected into the object.
1990 const compiler_rt_mod = Package.Module.create(arena, .{
1991 .paths = .{
1992 .root = .zig_lib_root,
1993 .root_src_path = "compiler_rt.zig",
1994 },
1995 .fully_qualified_name = "compiler_rt",
1996 .cc_argv = &.{},
1997 .inherited = .{
1998 .stack_check = false,
1999 .stack_protector = 0,
2000 .no_builtin = true,
2001 },
2002 .global = options.config,
2003 .parent = options.root_mod,
2004 }) catch |err| switch (err) {
2005 error.OutOfMemory => |e| return e,
2006 // None of these are possible because the configuration matches the root module
2007 // which already passed these checks.
2008 error.ValgrindUnsupportedOnTarget => unreachable,
2009 error.TargetRequiresSingleThreaded => unreachable,
2010 error.BackendRequiresSingleThreaded => unreachable,
2011 error.TargetRequiresPic => unreachable,
2012 error.PieRequiresPic => unreachable,
2013 error.DynamicLinkingRequiresPic => unreachable,
2014 error.TargetHasNoRedZone => unreachable,
2015 // These are not possible because are explicitly *not* requesting these things.
2016 error.StackCheckUnsupportedByTarget => unreachable,
2017 error.StackProtectorUnsupportedByTarget => unreachable,
2018 error.StackProtectorUnavailableWithoutLibC => unreachable,
2019 };
2020 try options.root_mod.deps.putNoClobber(arena, "compiler_rt", compiler_rt_mod);
2021 }
2022
2023 // unlike compiler_rt, we always want to go through the `_ = @import("ubsan-rt")`
2024 // approach if possible, since the ubsan runtime uses quite a lot of the standard
2025 // library and this reduces unnecessary bloat.
2026 const ubsan_rt_strat: RtStrat = s: {
2027 if (options.skip_linker_dependencies) break :s .none;
2028 const want = options.want_ubsan_rt orelse (any_sanitize_c == .full and is_exe_or_dyn_lib);
2029 if (!want) break :s .none;
2030 const need_llvm = switch (target_util.canBuildLibUbsanRt(target)) {
2031 .no => break :s .none, // impossible to build
2032 .yes => false,
2033 .llvm_only => true,
2034 .llvm_lld_only => if (!options.config.use_lld) {
2035 break :s .none; // only LLD can handle ubsan-rt for this target
2036 } else true,
2037 };
2038 if (have_zcu and (!need_llvm or use_llvm)) {
2039 // ubsan-rt's exports use hidden visibility. If we're building a Windows DLL and
2040 // exported functions are going to be dllexported, LLVM will complain that
2041 // dllexported functions must use default or protected visibility. So we can't use
2042 // the ZCU strategy in this case.
2043 if (options.config.dll_export_fns) break :s .lib;
2044 break :s .zcu;
2045 }
2046 if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm
2047 if (is_exe_or_dyn_lib) break :s .lib;
2048 break :s .obj;
2049 };
2050
2051 if (ubsan_rt_strat == .zcu) {
2052 const ubsan_rt_mod = Package.Module.create(arena, .{
2053 .paths = .{
2054 .root = .zig_lib_root,
2055 .root_src_path = "ubsan_rt.zig",
2056 },
2057 .fully_qualified_name = "ubsan_rt",
2058 .cc_argv = &.{},
2059 .inherited = .{},
2060 .global = options.config,
2061 .parent = options.root_mod,
2062 }) catch |err| switch (err) {
2063 error.OutOfMemory => |e| return e,
2064 // None of these are possible because the configuration matches the root module
2065 // which already passed these checks.
2066 error.ValgrindUnsupportedOnTarget => unreachable,
2067 error.TargetRequiresSingleThreaded => unreachable,
2068 error.BackendRequiresSingleThreaded => unreachable,
2069 error.TargetRequiresPic => unreachable,
2070 error.PieRequiresPic => unreachable,
2071 error.DynamicLinkingRequiresPic => unreachable,
2072 error.TargetHasNoRedZone => unreachable,
2073 error.StackCheckUnsupportedByTarget => unreachable,
2074 error.StackProtectorUnsupportedByTarget => unreachable,
2075 error.StackProtectorUnavailableWithoutLibC => unreachable,
2076 };
2077 try options.root_mod.deps.putNoClobber(arena, "ubsan_rt", ubsan_rt_mod);
2078 }
2079
2080 if (options.verbose_llvm_cpu_features) {
2081 if (options.root_mod.resolved_target.llvm_cpu_features) |cf| print: {
2082 const stderr_w, _ = std.debug.lockStderrWriter(&.{});
2083 defer std.debug.unlockStderrWriter();
2084 stderr_w.print("compilation: {s}\n", .{options.root_name}) catch break :print;
2085 stderr_w.print(" target: {s}\n", .{try target.zigTriple(arena)}) catch break :print;
2086 stderr_w.print(" cpu: {s}\n", .{target.cpu.model.name}) catch break :print;
2087 stderr_w.print(" features: {s}\n", .{cf}) catch {};
2088 }
2089 }
2090
2091 const error_limit = options.error_limit orelse (std.math.maxInt(u16) - 1);
2092
2093 // We put everything into the cache hash that *cannot be modified
2094 // during an incremental update*. For example, one cannot change the
2095 // target between updates, but one can change source files, so the
2096 // target goes into the cache hash, but source files do not. This is so
2097 // that we can find the same binary and incrementally update it even if
2098 // there are modified source files. We do this even if outputting to
2099 // the current directory because we need somewhere to store incremental
2100 // compilation metadata.
2101 const cache = try arena.create(Cache);
2102 cache.* = .{
2103 .gpa = gpa,
2104 .io = io,
2105 .manifest_dir = options.dirs.local_cache.handle.makeOpenPath("h", .{}) catch |err| {
2106 return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = "h", .err = err } });
2107 },
2108 };
2109 // These correspond to std.zig.Server.Message.PathPrefix.
2110 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
2111 cache.addPrefix(options.dirs.zig_lib);
2112 cache.addPrefix(options.dirs.local_cache);
2113 cache.addPrefix(options.dirs.global_cache);
2114 errdefer cache.manifest_dir.close();
2115
2116 // This is shared hasher state common to zig source and all C source files.
2117 cache.hash.addBytes(build_options.version);
2118 cache.hash.add(builtin.zig_backend);
2119 cache.hash.add(options.config.pie);
2120 cache.hash.add(options.config.lto);
2121 cache.hash.add(options.config.link_mode);
2122 cache.hash.add(options.config.any_unwind_tables);
2123 cache.hash.add(options.config.any_non_single_threaded);
2124 cache.hash.add(options.config.any_sanitize_thread);
2125 cache.hash.add(options.config.any_sanitize_c);
2126 cache.hash.add(options.config.any_fuzz);
2127 cache.hash.add(options.function_sections);
2128 cache.hash.add(options.data_sections);
2129 cache.hash.add(link_libc);
2130 cache.hash.add(options.config.link_libcpp);
2131 cache.hash.add(options.config.link_libunwind);
2132 cache.hash.add(output_mode);
2133 cache_helpers.addDebugFormat(&cache.hash, options.config.debug_format);
2134 cache.hash.addBytes(options.root_name);
2135 cache.hash.add(options.config.wasi_exec_model);
2136 cache.hash.add(options.config.san_cov_trace_pc_guard);
2137 cache.hash.add(options.debug_compiler_runtime_libs);
2138 // The actual emit paths don't matter. They're only user-specified if we aren't using the
2139 // cache! However, it does matter whether the files are emitted at all.
2140 cache.hash.add(options.emit_bin != .no);
2141 cache.hash.add(options.emit_asm != .no);
2142 cache.hash.add(options.emit_implib != .no);
2143 cache.hash.add(options.emit_llvm_ir != .no);
2144 cache.hash.add(options.emit_llvm_bc != .no);
2145 cache.hash.add(options.emit_docs != .no);
2146 // TODO audit this and make sure everything is in it
2147
2148 const main_mod = options.main_mod orelse options.root_mod;
2149 const comp = try arena.create(Compilation);
2150 const opt_zcu: ?*Zcu = if (have_zcu) blk: {
2151 // Pre-open the directory handles for cached ZIR code so that it does not need
2152 // to redundantly happen for each AstGen operation.
2153 const zir_sub_dir = "z";
2154
2155 var local_zir_dir = options.dirs.local_cache.handle.makeOpenPath(zir_sub_dir, .{}) catch |err| {
2156 return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = zir_sub_dir, .err = err } });
2157 };
2158 errdefer local_zir_dir.close();
2159 const local_zir_cache: Cache.Directory = .{
2160 .handle = local_zir_dir,
2161 .path = try options.dirs.local_cache.join(arena, &.{zir_sub_dir}),
2162 };
2163 var global_zir_dir = options.dirs.global_cache.handle.makeOpenPath(zir_sub_dir, .{}) catch |err| {
2164 return diag.fail(.{ .create_cache_path = .{ .which = .global, .sub = zir_sub_dir, .err = err } });
2165 };
2166 errdefer global_zir_dir.close();
2167 const global_zir_cache: Cache.Directory = .{
2168 .handle = global_zir_dir,
2169 .path = try options.dirs.global_cache.join(arena, &.{zir_sub_dir}),
2170 };
2171
2172 const std_mod = options.std_mod orelse Package.Module.create(arena, .{
2173 .paths = .{
2174 .root = try .fromRoot(arena, options.dirs, .zig_lib, "std"),
2175 .root_src_path = "std.zig",
2176 },
2177 .fully_qualified_name = "std",
2178 .cc_argv = &.{},
2179 .inherited = .{},
2180 .global = options.config,
2181 .parent = options.root_mod,
2182 }) catch |err| switch (err) {
2183 error.OutOfMemory => return error.OutOfMemory,
2184 // None of these are possible because the configuration matches the root module
2185 // which already passed these checks.
2186 error.ValgrindUnsupportedOnTarget => unreachable,
2187 error.TargetRequiresSingleThreaded => unreachable,
2188 error.BackendRequiresSingleThreaded => unreachable,
2189 error.TargetRequiresPic => unreachable,
2190 error.PieRequiresPic => unreachable,
2191 error.DynamicLinkingRequiresPic => unreachable,
2192 error.TargetHasNoRedZone => unreachable,
2193 error.StackCheckUnsupportedByTarget => unreachable,
2194 error.StackProtectorUnsupportedByTarget => unreachable,
2195 error.StackProtectorUnavailableWithoutLibC => unreachable,
2196 };
2197
2198 const zcu = try arena.create(Zcu);
2199 zcu.* = .{
2200 .gpa = gpa,
2201 .comp = comp,
2202 .main_mod = main_mod,
2203 .root_mod = options.root_mod,
2204 .std_mod = std_mod,
2205 .global_zir_cache = global_zir_cache,
2206 .local_zir_cache = local_zir_cache,
2207 .error_limit = error_limit,
2208 .llvm_object = null,
2209 .analysis_roots_buffer = undefined,
2210 .analysis_roots_len = 0,
2211 };
2212 try zcu.init(options.thread_pool.getIdCount());
2213 break :blk zcu;
2214 } else blk: {
2215 if (options.emit_h != .no) return diag.fail(.emit_h_without_zcu);
2216 break :blk null;
2217 };
2218 errdefer if (opt_zcu) |zcu| zcu.deinit();
2219
2220 comp.* = .{
2221 .gpa = gpa,
2222 .arena = arena,
2223 .io = io,
2224 .zcu = opt_zcu,
2225 .cache_use = undefined, // populated below
2226 .bin_file = null, // populated below if necessary
2227 .root_mod = options.root_mod,
2228 .config = options.config,
2229 .dirs = options.dirs,
2230 .work_queues = @splat(.empty),
2231 .c_object_work_queue = .empty,
2232 .win32_resource_work_queue = .empty,
2233 .c_source_files = options.c_source_files,
2234 .rc_source_files = options.rc_source_files,
2235 .cache_parent = cache,
2236 .self_exe_path = options.self_exe_path,
2237 .libc_include_dir_list = libc_dirs.libc_include_dir_list,
2238 .libc_framework_dir_list = libc_dirs.libc_framework_dir_list,
2239 .rc_includes = options.rc_includes,
2240 .mingw_unicode_entry_point = options.mingw_unicode_entry_point,
2241 .thread_pool = options.thread_pool,
2242 .clang_passthrough_mode = options.clang_passthrough_mode,
2243 .clang_preprocessor_mode = options.clang_preprocessor_mode,
2244 .verbose_cc = options.verbose_cc,
2245 .verbose_air = options.verbose_air,
2246 .verbose_intern_pool = options.verbose_intern_pool,
2247 .verbose_generic_instances = options.verbose_generic_instances,
2248 .verbose_llvm_ir = options.verbose_llvm_ir,
2249 .verbose_llvm_bc = options.verbose_llvm_bc,
2250 .verbose_cimport = options.verbose_cimport,
2251 .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features,
2252 .verbose_link = options.verbose_link,
2253 .disable_c_depfile = options.disable_c_depfile,
2254 .reference_trace = options.reference_trace,
2255 .time_report = if (options.time_report) .init else null,
2256 .stack_report = options.stack_report,
2257 .test_filters = options.test_filters,
2258 .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs,
2259 .debug_compile_errors = options.debug_compile_errors,
2260 .debug_incremental = options.debug_incremental,
2261 .root_name = root_name,
2262 .sysroot = sysroot,
2263 .windows_libs = .empty,
2264 .version = options.version,
2265 .libc_installation = libc_dirs.libc_installation,
2266 .compiler_rt_strat = compiler_rt_strat,
2267 .ubsan_rt_strat = ubsan_rt_strat,
2268 .link_inputs = options.link_inputs,
2269 .framework_dirs = options.framework_dirs,
2270 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
2271 .skip_linker_dependencies = options.skip_linker_dependencies,
2272 .queued_jobs = .{},
2273 .function_sections = options.function_sections,
2274 .data_sections = options.data_sections,
2275 .native_system_include_paths = options.native_system_include_paths,
2276 .force_undefined_symbols = options.force_undefined_symbols,
2277 .link_eh_frame_hdr = link_eh_frame_hdr,
2278 .global_cc_argv = options.global_cc_argv,
2279 .file_system_inputs = options.file_system_inputs,
2280 .parent_whole_cache = options.parent_whole_cache,
2281 .link_diags = .init(gpa),
2282 .emit_bin = try options.emit_bin.resolve(arena, &options, .bin),
2283 .emit_asm = try options.emit_asm.resolve(arena, &options, .@"asm"),
2284 .emit_implib = try options.emit_implib.resolve(arena, &options, .implib),
2285 .emit_llvm_ir = try options.emit_llvm_ir.resolve(arena, &options, .llvm_ir),
2286 .emit_llvm_bc = try options.emit_llvm_bc.resolve(arena, &options, .llvm_bc),
2287 .emit_docs = try options.emit_docs.resolve(arena, &options, .docs),
2288 };
2289
2290 errdefer {
2291 for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib);
2292 comp.windows_libs.deinit(gpa);
2293 }
2294 try comp.windows_libs.ensureUnusedCapacity(gpa, options.windows_lib_names.len);
2295 for (options.windows_lib_names) |windows_lib| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, windows_lib), {});
2296
2297 // Prevent some footguns by making the "any" fields of config reflect
2298 // the default Module settings.
2299 comp.config.any_unwind_tables = any_unwind_tables;
2300 comp.config.any_non_single_threaded = any_non_single_threaded;
2301 comp.config.any_sanitize_thread = any_sanitize_thread;
2302 comp.config.any_sanitize_c = any_sanitize_c;
2303 comp.config.any_fuzz = any_fuzz;
2304
2305 if (opt_zcu) |zcu| {
2306 // Populate `zcu.module_roots`.
2307 const pt: Zcu.PerThread = .activate(zcu, .main);
2308 defer pt.deactivate();
2309 pt.populateModuleRootTable() catch |err| switch (err) {
2310 error.OutOfMemory => |e| return e,
2311 error.IllegalZigImport => return diag.fail(.illegal_zig_import),
2312 };
2313 }
2314
2315 const lf_open_opts: link.File.OpenOptions = .{
2316 .linker_script = options.linker_script,
2317 .z_nodelete = options.linker_z_nodelete,
2318 .z_notext = options.linker_z_notext,
2319 .z_defs = options.linker_z_defs,
2320 .z_origin = options.linker_z_origin,
2321 .z_nocopyreloc = options.linker_z_nocopyreloc,
2322 .z_now = options.linker_z_now,
2323 .z_relro = options.linker_z_relro,
2324 .z_common_page_size = options.linker_z_common_page_size,
2325 .z_max_page_size = options.linker_z_max_page_size,
2326 .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
2327 .frameworks = options.frameworks,
2328 .lib_directories = options.lib_directories,
2329 .framework_dirs = options.framework_dirs,
2330 .rpath_list = options.rpath_list,
2331 .symbol_wrap_set = options.symbol_wrap_set,
2332 .repro = options.linker_repro orelse (options.root_mod.optimize_mode != .Debug),
2333 .allow_shlib_undefined = options.linker_allow_shlib_undefined,
2334 .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
2335 .compress_debug_sections = options.linker_compress_debug_sections orelse .none,
2336 .module_definition_file = options.linker_module_definition_file,
2337 .sort_section = options.linker_sort_section,
2338 .import_symbols = options.linker_import_symbols,
2339 .import_table = options.linker_import_table,
2340 .export_table = options.linker_export_table,
2341 .initial_memory = options.linker_initial_memory,
2342 .max_memory = options.linker_max_memory,
2343 .global_base = options.linker_global_base,
2344 .export_symbol_names = options.linker_export_symbol_names,
2345 .print_gc_sections = options.linker_print_gc_sections,
2346 .print_icf_sections = options.linker_print_icf_sections,
2347 .print_map = options.linker_print_map,
2348 .tsaware = options.linker_tsaware,
2349 .nxcompat = options.linker_nxcompat,
2350 .dynamicbase = options.linker_dynamicbase,
2351 .major_subsystem_version = options.major_subsystem_version,
2352 .minor_subsystem_version = options.minor_subsystem_version,
2353 .entry = options.entry,
2354 .stack_size = options.stack_size,
2355 .image_base = options.image_base,
2356 .version_script = options.version_script,
2357 .allow_undefined_version = options.linker_allow_undefined_version,
2358 .enable_new_dtags = options.linker_enable_new_dtags,
2359 .gc_sections = options.linker_gc_sections,
2360 .emit_relocs = options.link_emit_relocs,
2361 .soname = options.soname,
2362 .compatibility_version = options.compatibility_version,
2363 .build_id = build_id,
2364 .subsystem = options.subsystem,
2365 .hash_style = options.hash_style,
2366 .enable_link_snapshots = options.enable_link_snapshots,
2367 .install_name = options.install_name,
2368 .entitlements = options.entitlements,
2369 .pagezero_size = options.pagezero_size,
2370 .headerpad_size = options.headerpad_size,
2371 .headerpad_max_install_names = options.headerpad_max_install_names,
2372 .dead_strip_dylibs = options.dead_strip_dylibs,
2373 .force_load_objc = options.force_load_objc,
2374 .discard_local_symbols = options.discard_local_symbols,
2375 .pdb_source_path = options.pdb_source_path,
2376 .pdb_out_path = options.pdb_out_path,
2377 .entry_addr = null, // CLI does not expose this option (yet?)
2378 .object_host_name = "env",
2379 };
2380
2381 switch (options.cache_mode) {
2382 .none => {
2383 const none = try arena.create(CacheUse.None);
2384 none.* = .{ .tmp_artifact_directory = null };
2385 comp.cache_use = .{ .none = none };
2386 if (comp.emit_bin) |path| {
2387 comp.bin_file = link.File.open(arena, comp, .{
2388 .root_dir = .cwd(),
2389 .sub_path = path,
2390 }, lf_open_opts) catch |err| {
2391 return diag.fail(.{ .open_output_bin = err });
2392 };
2393 }
2394 },
2395 .incremental => {
2396 // Options that are specific to zig source files, that cannot be
2397 // modified between incremental updates.
2398 var hash = cache.hash;
2399
2400 // Synchronize with other matching comments: ZigOnlyHashStuff
2401 hash.add(use_llvm);
2402 hash.add(options.config.use_lib_llvm);
2403 hash.add(options.config.use_lld);
2404 hash.add(options.config.use_new_linker);
2405 hash.add(options.config.dll_export_fns);
2406 hash.add(options.config.is_test);
2407 hash.addListOfBytes(options.test_filters);
2408 hash.add(options.skip_linker_dependencies);
2409 hash.add(options.emit_h != .no);
2410 hash.add(error_limit);
2411
2412 // Here we put the root source file path name, but *not* with addFile.
2413 // We want the hash to be the same regardless of the contents of the
2414 // source file, because incremental compilation will handle it, but we
2415 // do want to namespace different source file names because they are
2416 // likely different compilations and therefore this would be likely to
2417 // cause cache hits.
2418 if (comp.zcu) |zcu| {
2419 try addModuleTableToCacheHash(zcu, arena, &hash, .path_bytes);
2420 } else {
2421 cache_helpers.addModule(&hash, options.root_mod);
2422 }
2423
2424 // In the case of incremental cache mode, this `artifact_directory`
2425 // is computed based on a hash of non-linker inputs, and it is where all
2426 // build artifacts are stored (even while in-progress).
2427 comp.digest = hash.peekBin();
2428 const digest = hash.final();
2429
2430 const artifact_sub_dir = "o" ++ fs.path.sep_str ++ digest;
2431 var artifact_dir = options.dirs.local_cache.handle.makeOpenPath(artifact_sub_dir, .{}) catch |err| {
2432 return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = artifact_sub_dir, .err = err } });
2433 };
2434 errdefer artifact_dir.close();
2435 const artifact_directory: Cache.Directory = .{
2436 .handle = artifact_dir,
2437 .path = try options.dirs.local_cache.join(arena, &.{artifact_sub_dir}),
2438 };
2439
2440 const incremental = try arena.create(CacheUse.Incremental);
2441 incremental.* = .{
2442 .artifact_directory = artifact_directory,
2443 };
2444 comp.cache_use = .{ .incremental = incremental };
2445
2446 if (comp.emit_bin) |cache_rel_path| {
2447 const emit: Cache.Path = .{
2448 .root_dir = artifact_directory,
2449 .sub_path = cache_rel_path,
2450 };
2451 comp.bin_file = link.File.open(arena, comp, emit, lf_open_opts) catch |err| {
2452 return diag.fail(.{ .open_output_bin = err });
2453 };
2454 }
2455 },
2456 .whole => {
2457 // For whole cache mode, we don't know where to put outputs from the linker until
2458 // the final cache hash, which is available after the compilation is complete.
2459 //
2460 // Therefore, `comp.bin_file` is left `null` (already done) until `update`, where
2461 // it may find a cache hit, or else will use a temporary directory to hold output
2462 // artifacts.
2463 const whole = try arena.create(CacheUse.Whole);
2464 whole.* = .{
2465 .lf_open_opts = lf_open_opts,
2466 .cache_manifest = null,
2467 .cache_manifest_mutex = .{},
2468 .tmp_artifact_directory = null,
2469 .lock = null,
2470 };
2471 comp.cache_use = .{ .whole = whole };
2472 },
2473 }
2474
2475 if (use_llvm) {
2476 if (opt_zcu) |zcu| {
2477 zcu.llvm_object = try LlvmObject.create(arena, comp);
2478 }
2479 }
2480
2481 break :comp comp;
2482 };
2483 errdefer comp.destroy();
2484
2485 // Add a `CObject` for each `c_source_files`.
2486 try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len);
2487 for (options.c_source_files) |c_source_file| {
2488 const c_object = try gpa.create(CObject);
2489 errdefer gpa.destroy(c_object);
2490
2491 c_object.* = .{
2492 .status = .{ .new = {} },
2493 .src = c_source_file,
2494 };
2495 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
2496 }
2497
2498 // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`.
2499 const win32_resource_count =
2500 options.rc_source_files.len + @intFromBool(options.manifest_file != null);
2501 if (win32_resource_count > 0) {
2502 dev.check(.win32_resource);
2503 try comp.win32_resource_table.ensureTotalCapacity(gpa, win32_resource_count);
2504 for (options.rc_source_files) |rc_source_file| {
2505 const win32_resource = try gpa.create(Win32Resource);
2506 errdefer gpa.destroy(win32_resource);
2507
2508 win32_resource.* = .{
2509 .status = .{ .new = {} },
2510 .src = .{ .rc = rc_source_file },
2511 };
2512 comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {});
2513 }
2514
2515 if (options.manifest_file) |manifest_path| {
2516 const win32_resource = try gpa.create(Win32Resource);
2517 errdefer gpa.destroy(win32_resource);
2518
2519 win32_resource.* = .{
2520 .status = .{ .new = {} },
2521 .src = .{ .manifest = manifest_path },
2522 };
2523 comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {});
2524 }
2525 }
2526
2527 if (comp.emit_bin != null and target.ofmt != .c) {
2528 if (!comp.skip_linker_dependencies) {
2529 // If we need to build libc for the target, add work items for it.
2530 // We go through the work queue so that building can be done in parallel.
2531 // If linking against host libc installation, instead queue up jobs
2532 // for loading those files in the linker.
2533 if (comp.config.link_libc and is_exe_or_dyn_lib) {
2534 // If the "is darwin" check is moved below the libc_installation check below,
2535 // error.LibCInstallationMissingCrtDir is returned from lci.resolveCrtPaths().
2536 if (target.isDarwinLibC()) {
2537 // TODO delete logic from MachO flush() and queue up tasks here instead.
2538 } else if (comp.libc_installation) |lci| {
2539 const basenames = LibCInstallation.CrtBasenames.get(.{
2540 .target = target,
2541 .link_libc = comp.config.link_libc,
2542 .output_mode = comp.config.output_mode,
2543 .link_mode = comp.config.link_mode,
2544 .pie = comp.config.pie,
2545 });
2546 const paths = lci.resolveCrtPaths(arena, basenames, target) catch |err| switch (err) {
2547 error.OutOfMemory => |e| return e,
2548 error.LibCInstallationMissingCrtDir => return diag.fail(.libc_installation_missing_crt_dir),
2549 };
2550
2551 const fields = @typeInfo(@TypeOf(paths)).@"struct".fields;
2552 try comp.link_task_queue.queued_prelink.ensureUnusedCapacity(gpa, fields.len + 1);
2553 inline for (fields) |field| {
2554 if (@field(paths, field.name)) |path| {
2555 comp.link_task_queue.queued_prelink.appendAssumeCapacity(.{ .load_object = path });
2556 }
2557 }
2558 // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.
2559 comp.link_task_queue.queued_prelink.appendAssumeCapacity(.load_host_libc);
2560 } else if (target.isMuslLibC()) {
2561 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2562
2563 if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| {
2564 comp.queued_jobs.musl_crt_file[@intFromEnum(f)] = true;
2565 }
2566 switch (comp.config.link_mode) {
2567 .static => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_a)] = true,
2568 .dynamic => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_so)] = true,
2569 }
2570 } else if (target.isGnuLibC()) {
2571 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2572
2573 if (glibc.needsCrt0(comp.config.output_mode)) |f| {
2574 comp.queued_jobs.glibc_crt_file[@intFromEnum(f)] = true;
2575 }
2576 comp.queued_jobs.glibc_shared_objects = true;
2577
2578 comp.queued_jobs.glibc_crt_file[@intFromEnum(glibc.CrtFile.libc_nonshared_a)] = true;
2579 } else if (target.isFreeBSDLibC()) {
2580 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2581
2582 if (freebsd.needsCrt0(comp.config.output_mode)) |f| {
2583 comp.queued_jobs.freebsd_crt_file[@intFromEnum(f)] = true;
2584 }
2585
2586 comp.queued_jobs.freebsd_shared_objects = true;
2587 } else if (target.isNetBSDLibC()) {
2588 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2589
2590 if (netbsd.needsCrt0(comp.config.output_mode)) |f| {
2591 comp.queued_jobs.netbsd_crt_file[@intFromEnum(f)] = true;
2592 }
2593
2594 comp.queued_jobs.netbsd_shared_objects = true;
2595 } else if (target.isWasiLibC()) {
2596 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2597
2598 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true;
2599 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true;
2600 } else if (target.isMinGW()) {
2601 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2602
2603 const main_crt_file: mingw.CrtFile = if (is_dyn_lib) .dllcrt2_o else .crt2_o;
2604 comp.queued_jobs.mingw_crt_file[@intFromEnum(main_crt_file)] = true;
2605 comp.queued_jobs.mingw_crt_file[@intFromEnum(mingw.CrtFile.libmingw32_lib)] = true;
2606
2607 // When linking mingw-w64 there are some import libs we always need.
2608 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
2609 for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, name), {});
2610 } else {
2611 return diag.fail(.cross_libc_unavailable);
2612 }
2613
2614 if ((target.isMuslLibC() and comp.config.link_mode == .static) or
2615 target.isWasiLibC() or
2616 target.isMinGW())
2617 {
2618 comp.queued_jobs.zigc_lib = true;
2619 }
2620 }
2621
2622 // Generate Windows import libs.
2623 if (target.os.tag == .windows) {
2624 const count = comp.windows_libs.count();
2625 for (0..count) |i| {
2626 try comp.queueJob(.{ .windows_import_lib = i });
2627 }
2628 // when integrating coff linker with prelink, the above
2629 // queueJob will need to change into something else since those
2630 // jobs are dispatched *after* the link_task_wait_group.wait()
2631 // that happens when separateCodegenThreadOk() is false.
2632 }
2633 if (comp.wantBuildLibUnwindFromSource()) {
2634 comp.queued_jobs.libunwind = true;
2635 }
2636 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
2637 comp.queued_jobs.libcxx = true;
2638 comp.queued_jobs.libcxxabi = true;
2639 }
2640 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) {
2641 comp.queued_jobs.libtsan = true;
2642 }
2643
2644 switch (comp.compiler_rt_strat) {
2645 .none, .zcu => {},
2646 .lib => {
2647 log.debug("queuing a job to build compiler_rt_lib", .{});
2648 comp.queued_jobs.compiler_rt_lib = true;
2649 },
2650 .obj => {
2651 log.debug("queuing a job to build compiler_rt_obj", .{});
2652 comp.queued_jobs.compiler_rt_obj = true;
2653 },
2654 .dyn_lib => {
2655 // hack for stage2_x86_64 + coff
2656 log.debug("queuing a job to build compiler_rt_dyn_lib", .{});
2657 comp.queued_jobs.compiler_rt_dyn_lib = true;
2658 },
2659 }
2660
2661 switch (comp.ubsan_rt_strat) {
2662 .none, .zcu => {},
2663 .lib => {
2664 log.debug("queuing a job to build ubsan_rt_lib", .{});
2665 comp.queued_jobs.ubsan_rt_lib = true;
2666 },
2667 .obj => {
2668 log.debug("queuing a job to build ubsan_rt_obj", .{});
2669 comp.queued_jobs.ubsan_rt_obj = true;
2670 },
2671 .dyn_lib => unreachable, // hack for compiler_rt only
2672 }
2673
2674 if (is_exe_or_dyn_lib and comp.config.any_fuzz) {
2675 log.debug("queuing a job to build libfuzzer", .{});
2676 comp.queued_jobs.fuzzer_lib = true;
2677 }
2678 }
2679
2680 try comp.link_task_queue.queued_prelink.append(gpa, .load_explicitly_provided);
2681 }
2682 log.debug("queued prelink tasks: {d}", .{comp.link_task_queue.queued_prelink.items.len});
2683 return comp;
2684}
2685
2686pub fn destroy(comp: *Compilation) void {
2687 const gpa = comp.gpa;
2688
2689 // This needs to be destroyed first, because it might contain MIR which we only know
2690 // how to interpret (which kind of MIR it is) from `comp.bin_file`.
2691 comp.link_task_queue.deinit(comp);
2692
2693 if (comp.bin_file) |lf| lf.destroy();
2694 if (comp.zcu) |zcu| zcu.deinit();
2695 comp.cache_use.deinit();
2696
2697 for (&comp.work_queues) |*work_queue| work_queue.deinit(gpa);
2698 comp.c_object_work_queue.deinit(gpa);
2699 comp.win32_resource_work_queue.deinit(gpa);
2700
2701 for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib);
2702 comp.windows_libs.deinit(gpa);
2703
2704 {
2705 var it = comp.crt_files.iterator();
2706 while (it.next()) |entry| {
2707 gpa.free(entry.key_ptr.*);
2708 entry.value_ptr.deinit(gpa);
2709 }
2710 comp.crt_files.deinit(gpa);
2711 }
2712 if (comp.libcxx_static_lib) |*crt_file| crt_file.deinit(gpa);
2713 if (comp.libcxxabi_static_lib) |*crt_file| crt_file.deinit(gpa);
2714 if (comp.libunwind_static_lib) |*crt_file| crt_file.deinit(gpa);
2715 if (comp.tsan_lib) |*crt_file| crt_file.deinit(gpa);
2716 if (comp.ubsan_rt_lib) |*crt_file| crt_file.deinit(gpa);
2717 if (comp.ubsan_rt_obj) |*crt_file| crt_file.deinit(gpa);
2718 if (comp.zigc_static_lib) |*crt_file| crt_file.deinit(gpa);
2719 if (comp.compiler_rt_lib) |*crt_file| crt_file.deinit(gpa);
2720 if (comp.compiler_rt_obj) |*crt_file| crt_file.deinit(gpa);
2721 if (comp.compiler_rt_dyn_lib) |*crt_file| crt_file.deinit(gpa);
2722 if (comp.fuzzer_lib) |*crt_file| crt_file.deinit(gpa);
2723
2724 if (comp.glibc_so_files) |*glibc_file| {
2725 glibc_file.deinit(gpa);
2726 }
2727
2728 if (comp.freebsd_so_files) |*freebsd_file| {
2729 freebsd_file.deinit(gpa);
2730 }
2731
2732 if (comp.netbsd_so_files) |*netbsd_file| {
2733 netbsd_file.deinit(gpa);
2734 }
2735
2736 for (comp.c_object_table.keys()) |key| {
2737 key.destroy(gpa);
2738 }
2739 comp.c_object_table.deinit(gpa);
2740
2741 for (comp.failed_c_objects.values()) |bundle| {
2742 bundle.destroy(gpa);
2743 }
2744 comp.failed_c_objects.deinit(gpa);
2745
2746 for (comp.win32_resource_table.keys()) |key| {
2747 key.destroy(gpa);
2748 }
2749 comp.win32_resource_table.deinit(gpa);
2750
2751 for (comp.failed_win32_resources.values()) |*value| {
2752 value.deinit(gpa);
2753 }
2754 comp.failed_win32_resources.deinit(gpa);
2755
2756 if (comp.time_report) |*tr| tr.deinit(gpa);
2757
2758 comp.link_diags.deinit();
2759
2760 comp.clearMiscFailures();
2761
2762 comp.cache_parent.manifest_dir.close();
2763}
2764
2765pub fn clearMiscFailures(comp: *Compilation) void {
2766 comp.alloc_failure_occurred = false;
2767 comp.link_diags.flags = .{};
2768 for (comp.misc_failures.values()) |*value| {
2769 value.deinit(comp.gpa);
2770 }
2771 comp.misc_failures.deinit(comp.gpa);
2772 comp.misc_failures = .{};
2773}
2774
2775pub fn getTarget(self: *const Compilation) *const Target {
2776 return &self.root_mod.resolved_target.result;
2777}
2778
2779/// Only legal to call when cache mode is incremental and a link file is present.
2780pub fn hotCodeSwap(
2781 comp: *Compilation,
2782 prog_node: std.Progress.Node,
2783 pid: std.process.Child.Id,
2784) !void {
2785 const lf = comp.bin_file.?;
2786 lf.child_pid = pid;
2787 try lf.makeWritable();
2788 try comp.update(prog_node);
2789 try lf.makeExecutable();
2790}
2791
2792fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
2793 switch (comp.cache_use) {
2794 .none => |none| {
2795 if (none.tmp_artifact_directory) |*tmp_dir| {
2796 tmp_dir.handle.close();
2797 none.tmp_artifact_directory = null;
2798 if (dev.env == .bootstrap) {
2799 // zig1 uses `CacheMode.none`, but it doesn't need to know how to delete
2800 // temporary directories; it doesn't have a real cache directory anyway.
2801 return;
2802 }
2803 // Usually, we want to delete the temporary directory. However, if we are emitting
2804 // an unstripped Mach-O binary with the LLVM backend, then the temporary directory
2805 // contains the ZCU object file emitted by LLVM, which contains debug symbols not
2806 // replicated in the output binary (the output instead contains a reference to that
2807 // file which debug tooling can look through). So, in that particular case, we need
2808 // to keep this directory around so that the output binary can be debugged.
2809 if (comp.bin_file != null and comp.getTarget().ofmt == .macho and comp.config.debug_format != .strip) {
2810 // We are emitting an unstripped Mach-O binary with the LLVM backend: the ZCU
2811 // object file must remain on-disk for its debug info.
2812 return;
2813 }
2814 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2815 comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| {
2816 log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{
2817 comp.dirs.local_cache.path orelse ".",
2818 fs.path.sep,
2819 tmp_dir_sub_path,
2820 @errorName(err),
2821 });
2822 };
2823 }
2824 },
2825 .incremental => return,
2826 .whole => |whole| {
2827 if (whole.cache_manifest) |man| {
2828 man.deinit();
2829 whole.cache_manifest = null;
2830 }
2831 if (comp.bin_file) |lf| {
2832 lf.destroy();
2833 comp.bin_file = null;
2834 }
2835 if (whole.tmp_artifact_directory) |*tmp_dir| {
2836 tmp_dir.handle.close();
2837 whole.tmp_artifact_directory = null;
2838 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2839 comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| {
2840 log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{
2841 comp.dirs.local_cache.path orelse ".",
2842 fs.path.sep,
2843 tmp_dir_sub_path,
2844 @errorName(err),
2845 });
2846 };
2847 }
2848 },
2849 }
2850}
2851
2852pub const UpdateError = error{
2853 OutOfMemory,
2854 Canceled,
2855 Unexpected,
2856 CurrentWorkingDirectoryUnlinked,
2857};
2858
2859/// Detect changes to source files, perform semantic analysis, and update the output files.
2860pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateError!void {
2861 const tracy_trace = trace(@src());
2862 defer tracy_trace.end();
2863
2864 // This arena is scoped to this one update.
2865 const gpa = comp.gpa;
2866 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
2867 defer arena_allocator.deinit();
2868 const arena = arena_allocator.allocator();
2869
2870 comp.clearMiscFailures();
2871 comp.last_update_was_cache_hit = false;
2872 if (comp.time_report) |*tr| {
2873 tr.deinit(gpa); // this is information about an old update
2874 tr.* = .init;
2875 }
2876
2877 var tmp_dir_rand_int: u64 = undefined;
2878 var man: Cache.Manifest = undefined;
2879 defer cleanupAfterUpdate(comp, tmp_dir_rand_int);
2880
2881 // If using the whole caching strategy, we check for *everything* up front, including
2882 // C source files.
2883 log.debug("Compilation.update for {s}, CacheMode.{s}", .{ comp.root_name, @tagName(comp.cache_use) });
2884 switch (comp.cache_use) {
2885 .none => |none| {
2886 assert(none.tmp_artifact_directory == null);
2887 none.tmp_artifact_directory = d: {
2888 tmp_dir_rand_int = std.crypto.random.int(u64);
2889 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2890 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
2891 const handle = comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}) catch |err| {
2892 return comp.setMiscFailure(.open_output, "failed to create output directory '{s}': {t}", .{ path, err });
2893 };
2894 break :d .{ .path = path, .handle = handle };
2895 };
2896 },
2897 .incremental => {},
2898 .whole => |whole| {
2899 assert(comp.bin_file == null);
2900 // We are about to obtain this lock, so here we give other processes a chance first.
2901 whole.releaseLock();
2902
2903 man = comp.cache_parent.obtain();
2904 whole.cache_manifest = &man;
2905 try addNonIncrementalStuffToCacheManifest(comp, arena, &man);
2906
2907 // Under `--time-report`, ignore cache hits; do the work anyway for those juicy numbers.
2908 const ignore_hit = comp.time_report != null;
2909
2910 if (ignore_hit) {
2911 // We're going to do the work regardless of whether this is a hit or a miss.
2912 man.want_shared_lock = false;
2913 }
2914
2915 const is_hit = man.hit() catch |err| switch (err) {
2916 error.CacheCheckFailed => switch (man.diagnostic) {
2917 .none => unreachable,
2918 .manifest_create, .manifest_read, .manifest_lock => |e| return comp.setMiscFailure(
2919 .check_whole_cache,
2920 "failed to check cache: {s} {s}",
2921 .{ @tagName(man.diagnostic), @errorName(e) },
2922 ),
2923 .file_open, .file_stat, .file_read, .file_hash => |op| {
2924 const pp = man.files.keys()[op.file_index].prefixed_path;
2925 const prefix = man.cache.prefixes()[pp.prefix];
2926 return comp.setMiscFailure(
2927 .check_whole_cache,
2928 "failed to check cache: '{f}{s}' {s} {s}",
2929 .{ prefix, pp.sub_path, @tagName(man.diagnostic), @errorName(op.err) },
2930 );
2931 },
2932 },
2933 error.OutOfMemory => return error.OutOfMemory,
2934 error.Canceled => return error.Canceled,
2935 error.InvalidFormat => return comp.setMiscFailure(
2936 .check_whole_cache,
2937 "failed to check cache: invalid manifest file format",
2938 .{},
2939 ),
2940 };
2941 if (is_hit and !ignore_hit) {
2942 // In this case the cache hit contains the full set of file system inputs. Nice!
2943 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
2944 if (comp.parent_whole_cache) |pwc| {
2945 pwc.mutex.lock();
2946 defer pwc.mutex.unlock();
2947 try man.populateOtherManifest(pwc.manifest, pwc.prefix_map);
2948 }
2949
2950 comp.last_update_was_cache_hit = true;
2951 log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name});
2952 const bin_digest = man.finalBin();
2953
2954 comp.digest = bin_digest;
2955
2956 assert(whole.lock == null);
2957 whole.lock = man.toOwnedLock();
2958 return;
2959 }
2960 log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name});
2961
2962 if (ignore_hit) {
2963 // Okay, now set this back so that `writeManifest` will downgrade our lock later.
2964 man.want_shared_lock = true;
2965 }
2966
2967 // Compile the artifacts to a temporary directory.
2968 whole.tmp_artifact_directory = d: {
2969 tmp_dir_rand_int = std.crypto.random.int(u64);
2970 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2971 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
2972 const handle = comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}) catch |err| {
2973 return comp.setMiscFailure(.open_output, "failed to create output directory '{s}': {t}", .{ path, err });
2974 };
2975 break :d .{ .path = path, .handle = handle };
2976 };
2977 if (comp.emit_bin) |sub_path| {
2978 const emit: Cache.Path = .{
2979 .root_dir = whole.tmp_artifact_directory.?,
2980 .sub_path = sub_path,
2981 };
2982 comp.bin_file = link.File.createEmpty(arena, comp, emit, whole.lf_open_opts) catch |err| {
2983 return comp.setMiscFailure(.open_output, "failed to open output file '{f}': {t}", .{ emit, err });
2984 };
2985 }
2986 },
2987 }
2988
2989 // From this point we add a preliminary set of file system inputs that
2990 // affects both incremental and whole cache mode. For incremental cache
2991 // mode, the long-lived compiler state will track additional file system
2992 // inputs discovered after this point. For whole cache mode, we rely on
2993 // these inputs to make it past AstGen, and once there, we can rely on
2994 // learning file system inputs from the Cache object.
2995
2996 // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
2997 // Add a Job for each C object.
2998 try comp.c_object_work_queue.ensureUnusedCapacity(gpa, comp.c_object_table.count());
2999 for (comp.c_object_table.keys()) |c_object| {
3000 comp.c_object_work_queue.pushBackAssumeCapacity(c_object);
3001 try comp.appendFileSystemInput(try .fromUnresolved(arena, comp.dirs, &.{c_object.src.src_path}));
3002 }
3003
3004 // For compiling Win32 resources, we rely on the cache hash system to avoid duplicating work.
3005 // Add a Job for each Win32 resource file.
3006 try comp.win32_resource_work_queue.ensureUnusedCapacity(gpa, comp.win32_resource_table.count());
3007 for (comp.win32_resource_table.keys()) |win32_resource| {
3008 comp.win32_resource_work_queue.pushBackAssumeCapacity(win32_resource);
3009 switch (win32_resource.src) {
3010 .rc => |f| {
3011 try comp.appendFileSystemInput(try .fromUnresolved(arena, comp.dirs, &.{f.src_path}));
3012 },
3013 .manifest => {},
3014 }
3015 }
3016
3017 if (comp.zcu) |zcu| {
3018 const pt: Zcu.PerThread = .activate(zcu, .main);
3019 defer pt.deactivate();
3020
3021 assert(zcu.cur_analysis_timer == null);
3022
3023 zcu.skip_analysis_this_update = false;
3024
3025 // TODO: doing this in `resolveReferences` later could avoid adding inputs for dead embedfiles. Investigate!
3026 for (zcu.embed_table.keys()) |embed_file| {
3027 try comp.appendFileSystemInput(embed_file.path);
3028 }
3029
3030 zcu.analysis_roots_len = 0;
3031
3032 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.std_mod;
3033 zcu.analysis_roots_len += 1;
3034
3035 // Normally we rely on importing std to in turn import the root source file in the start code.
3036 // However, the main module is distinct from the root module in tests, so that won't happen there.
3037 if (comp.config.is_test and zcu.main_mod != zcu.std_mod) {
3038 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.main_mod;
3039 zcu.analysis_roots_len += 1;
3040 }
3041
3042 if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
3043 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = compiler_rt_mod;
3044 zcu.analysis_roots_len += 1;
3045 }
3046
3047 if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| {
3048 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = ubsan_rt_mod;
3049 zcu.analysis_roots_len += 1;
3050 }
3051 }
3052
3053 // The linker progress node is set up here instead of in `performAllTheWork`, because
3054 // we also want it around during `flush`.
3055 if (comp.bin_file) |lf| {
3056 comp.link_prog_node = main_progress_node.start("Linking", 0);
3057 lf.startProgress(comp.link_prog_node);
3058 }
3059 defer if (comp.bin_file) |lf| {
3060 lf.endProgress();
3061 comp.link_prog_node.end();
3062 comp.link_prog_node = .none;
3063 };
3064
3065 try comp.performAllTheWork(main_progress_node);
3066
3067 if (comp.zcu) |zcu| {
3068 const pt: Zcu.PerThread = .activate(zcu, .main);
3069 defer pt.deactivate();
3070
3071 assert(zcu.cur_analysis_timer == null);
3072
3073 if (!zcu.skip_analysis_this_update) {
3074 if (comp.config.is_test) {
3075 // The `test_functions` decl has been intentionally postponed until now,
3076 // at which point we must populate it with the list of test functions that
3077 // have been discovered and not filtered out.
3078 try pt.populateTestFunctions();
3079 }
3080
3081 link.updateErrorData(pt);
3082
3083 try pt.processExports();
3084 }
3085
3086 if (build_options.enable_debug_extensions and comp.verbose_intern_pool) {
3087 std.debug.print("intern pool stats for '{s}':\n", .{
3088 comp.root_name,
3089 });
3090 zcu.intern_pool.dump();
3091 }
3092
3093 if (build_options.enable_debug_extensions and comp.verbose_generic_instances) {
3094 std.debug.print("generic instances for '{s}:0x{x}':\n", .{
3095 comp.root_name,
3096 @intFromPtr(zcu),
3097 });
3098 zcu.intern_pool.dumpGenericInstances(gpa);
3099 }
3100 }
3101
3102 if (anyErrors(comp)) {
3103 // Skip flushing and keep source files loaded for error reporting.
3104 return;
3105 }
3106
3107 if (comp.zcu == null and comp.config.output_mode == .Obj and comp.c_object_table.count() == 1) {
3108 // This is `zig build-obj foo.c`. We can emit asm and LLVM IR/bitcode.
3109 const c_obj_path = comp.c_object_table.keys()[0].status.success.object_path;
3110 if (comp.emit_asm) |path| try comp.emitFromCObject(arena, c_obj_path, ".s", path);
3111 if (comp.emit_llvm_ir) |path| try comp.emitFromCObject(arena, c_obj_path, ".ll", path);
3112 if (comp.emit_llvm_bc) |path| try comp.emitFromCObject(arena, c_obj_path, ".bc", path);
3113 }
3114
3115 switch (comp.cache_use) {
3116 .none, .incremental => {
3117 try flush(comp, arena, .main);
3118 },
3119 .whole => |whole| {
3120 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
3121 if (comp.parent_whole_cache) |pwc| {
3122 pwc.mutex.lock();
3123 defer pwc.mutex.unlock();
3124 try man.populateOtherManifest(pwc.manifest, pwc.prefix_map);
3125 }
3126
3127 const bin_digest = man.finalBin();
3128 const hex_digest = Cache.binToHex(bin_digest);
3129
3130 // Work around windows `AccessDenied` if any files within this
3131 // directory are open by closing and reopening the file handles.
3132 const need_writable_dance: enum { no, lf_only, lf_and_debug } = w: {
3133 if (builtin.os.tag == .windows) {
3134 if (comp.bin_file) |lf| {
3135 // We cannot just call `makeExecutable` as it makes a false
3136 // assumption that we have a file handle open only when linking
3137 // an executable file. This used to be true when our linkers
3138 // were incapable of emitting relocatables and static archive.
3139 // Now that they are capable, we need to unconditionally close
3140 // the file handle and re-open it in the follow up call to
3141 // `makeWritable`.
3142 if (lf.file) |f| {
3143 f.close();
3144 lf.file = null;
3145
3146 if (lf.closeDebugInfo()) break :w .lf_and_debug;
3147 break :w .lf_only;
3148 }
3149 }
3150 }
3151 break :w .no;
3152 };
3153
3154 // Rename the temporary directory into place.
3155 // Close tmp dir and link.File to avoid open handle during rename.
3156 whole.tmp_artifact_directory.?.handle.close();
3157 whole.tmp_artifact_directory = null;
3158 const s = fs.path.sep_str;
3159 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int);
3160 const o_sub_path = "o" ++ s ++ hex_digest;
3161 renameTmpIntoCache(comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| {
3162 return comp.setMiscFailure(
3163 .rename_results,
3164 "failed to rename compilation results ('{f}{s}') into local cache ('{f}{s}'): {t}",
3165 .{
3166 comp.dirs.local_cache, tmp_dir_sub_path,
3167 comp.dirs.local_cache, o_sub_path,
3168 err,
3169 },
3170 );
3171 };
3172 comp.digest = bin_digest;
3173
3174 // The linker flush functions need to know the final output path
3175 // for debug info purposes because executable debug info contains
3176 // references object file paths.
3177 if (comp.bin_file) |lf| {
3178 lf.emit = .{
3179 .root_dir = comp.dirs.local_cache,
3180 .sub_path = try fs.path.join(arena, &.{ o_sub_path, comp.emit_bin.? }),
3181 };
3182 const result: (link.File.OpenError || error{HotSwapUnavailableOnHostOperatingSystem})!void = switch (need_writable_dance) {
3183 .no => {},
3184 .lf_only => lf.makeWritable(),
3185 .lf_and_debug => res: {
3186 lf.makeWritable() catch |err| break :res err;
3187 lf.reopenDebugInfo() catch |err| break :res err;
3188 },
3189 };
3190 result catch |err| {
3191 return comp.setMiscFailure(
3192 .rename_results,
3193 "failed to re-open renamed compilation results ('{f}{s}'): {t}",
3194 .{ comp.dirs.local_cache, o_sub_path, err },
3195 );
3196 };
3197 }
3198
3199 try flush(comp, arena, .main);
3200
3201 // Calling `flush` may have produced errors, in which case the
3202 // cache manifest must not be written.
3203 if (anyErrors(comp)) return;
3204
3205 // Failure here only means an unnecessary cache miss.
3206 man.writeManifest() catch |err| {
3207 log.warn("failed to write cache manifest: {s}", .{@errorName(err)});
3208 };
3209
3210 if (comp.bin_file) |lf| {
3211 lf.destroy();
3212 comp.bin_file = null;
3213 }
3214
3215 assert(whole.lock == null);
3216 whole.lock = man.toOwnedLock();
3217 },
3218 }
3219}
3220
3221pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocator.Error!void {
3222 const gpa = comp.gpa;
3223 const fsi = comp.file_system_inputs orelse return;
3224 const prefixes = comp.cache_parent.prefixes();
3225
3226 const want_prefix_dir: Cache.Directory = switch (path.root) {
3227 .zig_lib => comp.dirs.zig_lib,
3228 .global_cache => comp.dirs.global_cache,
3229 .local_cache => comp.dirs.local_cache,
3230 .none => .cwd(),
3231 };
3232 const prefix: u8 = for (prefixes, 1..) |prefix_dir, i| {
3233 if (prefix_dir.eql(want_prefix_dir)) {
3234 break @intCast(i);
3235 }
3236 } else std.debug.panic(
3237 "missing prefix directory '{s}' ('{f}') for '{s}'",
3238 .{ @tagName(path.root), want_prefix_dir, path.sub_path },
3239 );
3240
3241 try fsi.ensureUnusedCapacity(gpa, path.sub_path.len + 3);
3242 if (fsi.items.len > 0) fsi.appendAssumeCapacity(0);
3243 fsi.appendAssumeCapacity(prefix);
3244 fsi.appendSliceAssumeCapacity(path.sub_path);
3245}
3246
3247fn resolveEmitPath(comp: *Compilation, path: []const u8) Cache.Path {
3248 return .{
3249 .root_dir = switch (comp.cache_use) {
3250 .none => .cwd(),
3251 .incremental => |i| i.artifact_directory,
3252 .whole => |w| w.tmp_artifact_directory.?,
3253 },
3254 .sub_path = path,
3255 };
3256}
3257/// Like `resolveEmitPath`, but for calling during `flush`. The returned `Cache.Path` may reference
3258/// memory from `arena`, and may reference `path` itself.
3259/// If `kind == .temp`, then the returned path will be in a temporary or cache directory. This is
3260/// useful for intermediate files, such as the ZCU object file emitted by the LLVM backend.
3261pub fn resolveEmitPathFlush(
3262 comp: *Compilation,
3263 arena: Allocator,
3264 kind: enum { temp, artifact },
3265 path: []const u8,
3266) Allocator.Error!Cache.Path {
3267 switch (comp.cache_use) {
3268 .none => |none| return .{
3269 .root_dir = switch (kind) {
3270 .temp => none.tmp_artifact_directory.?,
3271 .artifact => .cwd(),
3272 },
3273 .sub_path = path,
3274 },
3275 .incremental, .whole => return .{
3276 .root_dir = comp.dirs.local_cache,
3277 .sub_path = try fs.path.join(arena, &.{
3278 "o",
3279 &Cache.binToHex(comp.digest.?),
3280 path,
3281 }),
3282 },
3283 }
3284}
3285fn flush(
3286 comp: *Compilation,
3287 arena: Allocator,
3288 tid: Zcu.PerThread.Id,
3289) Allocator.Error!void {
3290 if (comp.zcu) |zcu| {
3291 if (zcu.llvm_object) |llvm_object| {
3292 const pt: Zcu.PerThread = .activate(zcu, tid);
3293 defer pt.deactivate();
3294
3295 // Emit the ZCU object from LLVM now; it's required to flush the output file.
3296 // If there's an output file, it wants to decide where the LLVM object goes!
3297 const sub_prog_node = comp.link_prog_node.start("LLVM Emit Object", 0);
3298 defer sub_prog_node.end();
3299
3300 var timer = comp.startTimer();
3301 defer if (timer.finish()) |ns| {
3302 comp.mutex.lock();
3303 defer comp.mutex.unlock();
3304 comp.time_report.?.stats.real_ns_llvm_emit = ns;
3305 };
3306
3307 llvm_object.emit(pt, .{
3308 .pre_ir_path = comp.verbose_llvm_ir,
3309 .pre_bc_path = comp.verbose_llvm_bc,
3310
3311 .bin_path = p: {
3312 const lf = comp.bin_file orelse break :p null;
3313 const p = try comp.resolveEmitPathFlush(arena, .temp, lf.zcu_object_basename.?);
3314 break :p try p.toStringZ(arena);
3315 },
3316 .asm_path = p: {
3317 const raw = comp.emit_asm orelse break :p null;
3318 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
3319 break :p try p.toStringZ(arena);
3320 },
3321 .post_ir_path = p: {
3322 const raw = comp.emit_llvm_ir orelse break :p null;
3323 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
3324 break :p try p.toStringZ(arena);
3325 },
3326 .post_bc_path = p: {
3327 const raw = comp.emit_llvm_bc orelse break :p null;
3328 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
3329 break :p try p.toStringZ(arena);
3330 },
3331
3332 .is_debug = comp.root_mod.optimize_mode == .Debug,
3333 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,
3334 .time_report = if (comp.time_report) |*p| p else null,
3335 .sanitize_thread = comp.config.any_sanitize_thread,
3336 .fuzz = comp.config.any_fuzz,
3337 .lto = comp.config.lto,
3338 }) catch |err| switch (err) {
3339 error.LinkFailure => {}, // Already reported.
3340 error.OutOfMemory => return error.OutOfMemory,
3341 };
3342 }
3343 }
3344 if (comp.bin_file) |lf| {
3345 var timer = comp.startTimer();
3346 defer if (timer.finish()) |ns| {
3347 comp.mutex.lock();
3348 defer comp.mutex.unlock();
3349 comp.time_report.?.stats.real_ns_link_flush = ns;
3350 };
3351 // This is needed before reading the error flags.
3352 lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) {
3353 error.LinkFailure => {}, // Already reported.
3354 error.OutOfMemory => return error.OutOfMemory,
3355 };
3356 }
3357 if (comp.zcu) |zcu| {
3358 try link.File.C.flushEmitH(zcu);
3359 }
3360}
3361
3362/// This function is called by the frontend before flush(). It communicates that
3363/// `options.bin_file.emit` directory needs to be renamed from
3364/// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`.
3365/// The frontend would like to simply perform a file system rename, however,
3366/// some linker backends care about the file paths of the objects they are linking.
3367/// So this function call tells linker backends to rename the paths of object files
3368/// to observe the new directory path.
3369/// Linker backends which do not have this requirement can fall back to the simple
3370/// implementation at the bottom of this function.
3371/// This function is only called when CacheMode is `whole`.
3372fn renameTmpIntoCache(
3373 cache_directory: Cache.Directory,
3374 tmp_dir_sub_path: []const u8,
3375 o_sub_path: []const u8,
3376) !void {
3377 var seen_eaccess = false;
3378 while (true) {
3379 fs.rename(
3380 cache_directory.handle,
3381 tmp_dir_sub_path,
3382 cache_directory.handle,
3383 o_sub_path,
3384 ) catch |err| switch (err) {
3385 // On Windows, rename fails with `AccessDenied` rather than `PathAlreadyExists`.
3386 // See https://github.com/ziglang/zig/issues/8362
3387 error.AccessDenied => switch (builtin.os.tag) {
3388 .windows => {
3389 if (seen_eaccess) return error.AccessDenied;
3390 seen_eaccess = true;
3391 try cache_directory.handle.deleteTree(o_sub_path);
3392 continue;
3393 },
3394 else => return error.AccessDenied,
3395 },
3396 error.PathAlreadyExists => {
3397 try cache_directory.handle.deleteTree(o_sub_path);
3398 continue;
3399 },
3400 error.FileNotFound => {
3401 try cache_directory.handle.makePath("o");
3402 continue;
3403 },
3404 else => |e| return e,
3405 };
3406 break;
3407 }
3408}
3409
3410/// This is only observed at compile-time and used to emit a compile error
3411/// to remind the programmer to update multiple related pieces of code that
3412/// are in different locations. Bump this number when adding or deleting
3413/// anything from the link cache manifest.
3414pub const link_hash_implementation_version = 14;
3415
3416fn addNonIncrementalStuffToCacheManifest(
3417 comp: *Compilation,
3418 arena: Allocator,
3419 man: *Cache.Manifest,
3420) !void {
3421 comptime assert(link_hash_implementation_version == 14);
3422
3423 if (comp.zcu) |zcu| {
3424 try addModuleTableToCacheHash(zcu, arena, &man.hash, .{ .files = man });
3425
3426 // Synchronize with other matching comments: ZigOnlyHashStuff
3427 man.hash.addListOfBytes(comp.test_filters);
3428 man.hash.add(comp.skip_linker_dependencies);
3429 //man.hash.add(zcu.emit_h != .no);
3430 man.hash.add(zcu.error_limit);
3431 } else {
3432 cache_helpers.addModule(&man.hash, comp.root_mod);
3433 }
3434
3435 try link.hashInputs(man, comp.link_inputs);
3436
3437 for (comp.c_object_table.keys()) |key| {
3438 _ = try man.addFile(key.src.src_path, null);
3439 man.hash.addOptional(key.src.ext);
3440 man.hash.addListOfBytes(key.src.extra_flags);
3441 }
3442
3443 for (comp.win32_resource_table.keys()) |key| {
3444 switch (key.src) {
3445 .rc => |rc_src| {
3446 _ = try man.addFile(rc_src.src_path, null);
3447 man.hash.addListOfBytes(rc_src.extra_flags);
3448 },
3449 .manifest => |manifest_path| {
3450 _ = try man.addFile(manifest_path, null);
3451 },
3452 }
3453 }
3454
3455 man.hash.add(comp.config.use_llvm);
3456 man.hash.add(comp.config.use_lib_llvm);
3457 man.hash.add(comp.config.use_lld);
3458 man.hash.add(comp.config.use_new_linker);
3459 man.hash.add(comp.config.is_test);
3460 man.hash.add(comp.config.import_memory);
3461 man.hash.add(comp.config.export_memory);
3462 man.hash.add(comp.config.shared_memory);
3463 man.hash.add(comp.config.dll_export_fns);
3464 man.hash.add(comp.config.rdynamic);
3465
3466 man.hash.addOptionalBytes(comp.sysroot);
3467 man.hash.addOptional(comp.version);
3468 man.hash.add(comp.link_eh_frame_hdr);
3469 man.hash.add(comp.skip_linker_dependencies);
3470 man.hash.add(comp.compiler_rt_strat);
3471 man.hash.add(comp.ubsan_rt_strat);
3472 man.hash.add(comp.rc_includes);
3473 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
3474 man.hash.addListOfBytes(comp.framework_dirs);
3475 man.hash.addListOfBytes(comp.windows_libs.keys());
3476
3477 man.hash.addListOfBytes(comp.global_cc_argv);
3478
3479 const opts = comp.cache_use.whole.lf_open_opts;
3480
3481 try man.addOptionalFile(opts.linker_script);
3482 try man.addOptionalFile(opts.version_script);
3483 man.hash.add(opts.allow_undefined_version);
3484 man.hash.addOptional(opts.enable_new_dtags);
3485
3486 man.hash.addOptional(opts.stack_size);
3487 man.hash.addOptional(opts.image_base);
3488 man.hash.addOptional(opts.gc_sections);
3489 man.hash.add(opts.emit_relocs);
3490 const target = &comp.root_mod.resolved_target.result;
3491 if (target.ofmt == .macho or target.ofmt == .coff) {
3492 // TODO remove this, libraries need to be resolved by the frontend. this is already
3493 // done by ELF.
3494 for (opts.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path);
3495 }
3496 man.hash.addListOfBytes(opts.rpath_list);
3497 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());
3498 if (comp.config.link_libc) {
3499 man.hash.add(comp.libc_installation != null);
3500 if (comp.libc_installation) |libc_installation| {
3501 man.hash.addOptionalBytes(libc_installation.crt_dir);
3502 if (target.abi == .msvc or target.abi == .itanium) {
3503 man.hash.addOptionalBytes(libc_installation.msvc_lib_dir);
3504 man.hash.addOptionalBytes(libc_installation.kernel32_lib_dir);
3505 }
3506 }
3507 man.hash.addOptionalBytes(target.dynamic_linker.get());
3508 }
3509 man.hash.add(opts.repro);
3510 man.hash.addOptional(opts.allow_shlib_undefined);
3511 man.hash.add(opts.bind_global_refs_locally);
3512
3513 const EntryTag = @typeInfo(link.File.OpenOptions.Entry).@"union".tag_type.?;
3514 man.hash.add(@as(EntryTag, opts.entry));
3515 switch (opts.entry) {
3516 .default, .disabled, .enabled => {},
3517 .named => |name| man.hash.addBytes(name),
3518 }
3519
3520 // ELF specific stuff
3521 man.hash.add(opts.z_nodelete);
3522 man.hash.add(opts.z_notext);
3523 man.hash.add(opts.z_defs);
3524 man.hash.add(opts.z_origin);
3525 man.hash.add(opts.z_nocopyreloc);
3526 man.hash.add(opts.z_now);
3527 man.hash.add(opts.z_relro);
3528 man.hash.add(opts.z_common_page_size orelse 0);
3529 man.hash.add(opts.z_max_page_size orelse 0);
3530 man.hash.add(opts.hash_style);
3531 man.hash.add(opts.compress_debug_sections);
3532 man.hash.addOptional(opts.sort_section);
3533 man.hash.addOptionalBytes(opts.soname);
3534 man.hash.add(opts.build_id);
3535
3536 // WASM specific stuff
3537 man.hash.addOptional(opts.initial_memory);
3538 man.hash.addOptional(opts.max_memory);
3539 man.hash.addOptional(opts.global_base);
3540 man.hash.addListOfBytes(opts.export_symbol_names);
3541 man.hash.add(opts.import_symbols);
3542 man.hash.add(opts.import_table);
3543 man.hash.add(opts.export_table);
3544
3545 // Mach-O specific stuff
3546 try link.File.MachO.hashAddFrameworks(man, opts.frameworks);
3547 try man.addOptionalFile(opts.entitlements);
3548 man.hash.addOptional(opts.pagezero_size);
3549 man.hash.addOptional(opts.headerpad_size);
3550 man.hash.add(opts.headerpad_max_install_names);
3551 man.hash.add(opts.dead_strip_dylibs);
3552 man.hash.add(opts.force_load_objc);
3553 man.hash.add(opts.discard_local_symbols);
3554 man.hash.addOptional(opts.compatibility_version);
3555 man.hash.addOptionalBytes(opts.install_name);
3556 man.hash.addOptional(opts.darwin_sdk_layout);
3557
3558 // COFF specific stuff
3559 man.hash.addOptional(opts.subsystem);
3560 man.hash.add(opts.tsaware);
3561 man.hash.add(opts.nxcompat);
3562 man.hash.add(opts.dynamicbase);
3563 man.hash.addOptional(opts.major_subsystem_version);
3564 man.hash.addOptional(opts.minor_subsystem_version);
3565 man.hash.addOptionalBytes(opts.pdb_source_path);
3566 man.hash.addOptionalBytes(opts.module_definition_file);
3567}
3568
3569fn emitFromCObject(
3570 comp: *Compilation,
3571 arena: Allocator,
3572 c_obj_path: Cache.Path,
3573 new_ext: []const u8,
3574 unresolved_emit_path: []const u8,
3575) Allocator.Error!void {
3576 // The dirname and stem (i.e. everything but the extension), of the sub path of the C object.
3577 // We'll append `new_ext` to it to get the path to the right thing (asm, LLVM IR, etc).
3578 const c_obj_dir_and_stem: []const u8 = p: {
3579 const p = c_obj_path.sub_path;
3580 const ext_len = fs.path.extension(p).len;
3581 break :p p[0 .. p.len - ext_len];
3582 };
3583 const src_path: Cache.Path = .{
3584 .root_dir = c_obj_path.root_dir,
3585 .sub_path = try std.fmt.allocPrint(arena, "{s}{s}", .{
3586 c_obj_dir_and_stem,
3587 new_ext,
3588 }),
3589 };
3590 const emit_path = comp.resolveEmitPath(unresolved_emit_path);
3591
3592 src_path.root_dir.handle.copyFile(
3593 src_path.sub_path,
3594 emit_path.root_dir.handle,
3595 emit_path.sub_path,
3596 .{},
3597 ) catch |err| log.err("unable to copy '{f}' to '{f}': {s}", .{
3598 src_path,
3599 emit_path,
3600 @errorName(err),
3601 });
3602}
3603
3604/// Having the file open for writing is problematic as far as executing the
3605/// binary is concerned. This will remove the write flag, or close the file,
3606/// or whatever is needed so that it can be executed.
3607/// After this, one must call` makeFileWritable` before calling `update`.
3608pub fn makeBinFileExecutable(comp: *Compilation) !void {
3609 if (!dev.env.supports(.make_executable)) return;
3610 const lf = comp.bin_file orelse return;
3611 return lf.makeExecutable();
3612}
3613
3614pub fn makeBinFileWritable(comp: *Compilation) !void {
3615 const lf = comp.bin_file orelse return;
3616 return lf.makeWritable();
3617}
3618
3619const Header = extern struct {
3620 intern_pool: extern struct {
3621 thread_count: u32,
3622 src_hash_deps_len: u32,
3623 nav_val_deps_len: u32,
3624 nav_ty_deps_len: u32,
3625 interned_deps_len: u32,
3626 zon_file_deps_len: u32,
3627 embed_file_deps_len: u32,
3628 namespace_deps_len: u32,
3629 namespace_name_deps_len: u32,
3630 first_dependency_len: u32,
3631 dep_entries_len: u32,
3632 free_dep_entries_len: u32,
3633 },
3634
3635 const PerThread = extern struct {
3636 intern_pool: extern struct {
3637 items_len: u32,
3638 extra_len: u32,
3639 limbs_len: u32,
3640 strings_len: u32,
3641 string_bytes_len: u32,
3642 tracked_insts_len: u32,
3643 files_len: u32,
3644 },
3645 };
3646};
3647
3648/// Note that all state that is included in the cache hash namespace is *not*
3649/// saved, such as the target and most CLI flags. A cache hit will only occur
3650/// when subsequent compiler invocations use the same set of flags.
3651pub fn saveState(comp: *Compilation) !void {
3652 dev.check(.incremental);
3653
3654 const lf = comp.bin_file orelse return;
3655
3656 const gpa = comp.gpa;
3657
3658 var bufs = std.array_list.Managed([]const u8).init(gpa);
3659 defer bufs.deinit();
3660
3661 var pt_headers = std.array_list.Managed(Header.PerThread).init(gpa);
3662 defer pt_headers.deinit();
3663
3664 if (comp.zcu) |zcu| {
3665 const ip = &zcu.intern_pool;
3666 const header: Header = .{
3667 .intern_pool = .{
3668 .thread_count = @intCast(ip.locals.len),
3669 .src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
3670 .nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
3671 .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
3672 .interned_deps_len = @intCast(ip.interned_deps.count()),
3673 .zon_file_deps_len = @intCast(ip.zon_file_deps.count()),
3674 .embed_file_deps_len = @intCast(ip.embed_file_deps.count()),
3675 .namespace_deps_len = @intCast(ip.namespace_deps.count()),
3676 .namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
3677 .first_dependency_len = @intCast(ip.first_dependency.count()),
3678 .dep_entries_len = @intCast(ip.dep_entries.items.len),
3679 .free_dep_entries_len = @intCast(ip.free_dep_entries.items.len),
3680 },
3681 };
3682
3683 try pt_headers.ensureTotalCapacityPrecise(header.intern_pool.thread_count);
3684 for (ip.locals) |*local| pt_headers.appendAssumeCapacity(.{
3685 .intern_pool = .{
3686 .items_len = @intCast(local.mutate.items.len),
3687 .extra_len = @intCast(local.mutate.extra.len),
3688 .limbs_len = @intCast(local.mutate.limbs.len),
3689 .strings_len = @intCast(local.mutate.strings.len),
3690 .string_bytes_len = @intCast(local.mutate.string_bytes.len),
3691 .tracked_insts_len = @intCast(local.mutate.tracked_insts.len),
3692 .files_len = @intCast(local.mutate.files.len),
3693 },
3694 });
3695
3696 try bufs.ensureTotalCapacityPrecise(14 + 8 * pt_headers.items.len);
3697 addBuf(&bufs, mem.asBytes(&header));
3698 addBuf(&bufs, @ptrCast(pt_headers.items));
3699
3700 addBuf(&bufs, @ptrCast(ip.src_hash_deps.keys()));
3701 addBuf(&bufs, @ptrCast(ip.src_hash_deps.values()));
3702 addBuf(&bufs, @ptrCast(ip.nav_val_deps.keys()));
3703 addBuf(&bufs, @ptrCast(ip.nav_val_deps.values()));
3704 addBuf(&bufs, @ptrCast(ip.nav_ty_deps.keys()));
3705 addBuf(&bufs, @ptrCast(ip.nav_ty_deps.values()));
3706 addBuf(&bufs, @ptrCast(ip.interned_deps.keys()));
3707 addBuf(&bufs, @ptrCast(ip.interned_deps.values()));
3708 addBuf(&bufs, @ptrCast(ip.zon_file_deps.keys()));
3709 addBuf(&bufs, @ptrCast(ip.zon_file_deps.values()));
3710 addBuf(&bufs, @ptrCast(ip.embed_file_deps.keys()));
3711 addBuf(&bufs, @ptrCast(ip.embed_file_deps.values()));
3712 addBuf(&bufs, @ptrCast(ip.namespace_deps.keys()));
3713 addBuf(&bufs, @ptrCast(ip.namespace_deps.values()));
3714 addBuf(&bufs, @ptrCast(ip.namespace_name_deps.keys()));
3715 addBuf(&bufs, @ptrCast(ip.namespace_name_deps.values()));
3716
3717 addBuf(&bufs, @ptrCast(ip.first_dependency.keys()));
3718 addBuf(&bufs, @ptrCast(ip.first_dependency.values()));
3719 addBuf(&bufs, @ptrCast(ip.dep_entries.items));
3720 addBuf(&bufs, @ptrCast(ip.free_dep_entries.items));
3721
3722 for (ip.locals, pt_headers.items) |*local, pt_header| {
3723 if (pt_header.intern_pool.limbs_len > 0) {
3724 addBuf(&bufs, @ptrCast(local.shared.limbs.view().items(.@"0")[0..pt_header.intern_pool.limbs_len]));
3725 }
3726 if (pt_header.intern_pool.extra_len > 0) {
3727 addBuf(&bufs, @ptrCast(local.shared.extra.view().items(.@"0")[0..pt_header.intern_pool.extra_len]));
3728 }
3729 if (pt_header.intern_pool.items_len > 0) {
3730 addBuf(&bufs, @ptrCast(local.shared.items.view().items(.data)[0..pt_header.intern_pool.items_len]));
3731 addBuf(&bufs, @ptrCast(local.shared.items.view().items(.tag)[0..pt_header.intern_pool.items_len]));
3732 }
3733 if (pt_header.intern_pool.strings_len > 0) {
3734 addBuf(&bufs, @ptrCast(local.shared.strings.view().items(.@"0")[0..pt_header.intern_pool.strings_len]));
3735 }
3736 if (pt_header.intern_pool.string_bytes_len > 0) {
3737 addBuf(&bufs, local.shared.string_bytes.view().items(.@"0")[0..pt_header.intern_pool.string_bytes_len]);
3738 }
3739 if (pt_header.intern_pool.tracked_insts_len > 0) {
3740 addBuf(&bufs, @ptrCast(local.shared.tracked_insts.view().items(.@"0")[0..pt_header.intern_pool.tracked_insts_len]));
3741 }
3742 if (pt_header.intern_pool.files_len > 0) {
3743 addBuf(&bufs, @ptrCast(local.shared.files.view().items(.bin_digest)[0..pt_header.intern_pool.files_len]));
3744 addBuf(&bufs, @ptrCast(local.shared.files.view().items(.root_type)[0..pt_header.intern_pool.files_len]));
3745 }
3746 }
3747
3748 //// TODO: compilation errors
3749 //// TODO: namespaces
3750 //// TODO: decls
3751 }
3752
3753 // linker state
3754 switch (lf.tag) {
3755 .wasm => {
3756 dev.check(link.File.Tag.wasm.devFeature());
3757 const wasm = lf.cast(.wasm).?;
3758 const is_obj = comp.config.output_mode == .Obj;
3759 try bufs.ensureUnusedCapacity(85);
3760 addBuf(&bufs, wasm.string_bytes.items);
3761 // TODO make it well-defined memory layout
3762 //addBuf(&bufs, @ptrCast(wasm.objects.items));
3763 addBuf(&bufs, @ptrCast(wasm.func_types.keys()));
3764 addBuf(&bufs, @ptrCast(wasm.object_function_imports.keys()));
3765 addBuf(&bufs, @ptrCast(wasm.object_function_imports.values()));
3766 addBuf(&bufs, @ptrCast(wasm.object_functions.items));
3767 addBuf(&bufs, @ptrCast(wasm.object_global_imports.keys()));
3768 addBuf(&bufs, @ptrCast(wasm.object_global_imports.values()));
3769 addBuf(&bufs, @ptrCast(wasm.object_globals.items));
3770 addBuf(&bufs, @ptrCast(wasm.object_table_imports.keys()));
3771 addBuf(&bufs, @ptrCast(wasm.object_table_imports.values()));
3772 addBuf(&bufs, @ptrCast(wasm.object_tables.items));
3773 addBuf(&bufs, @ptrCast(wasm.object_memory_imports.keys()));
3774 addBuf(&bufs, @ptrCast(wasm.object_memory_imports.values()));
3775 addBuf(&bufs, @ptrCast(wasm.object_memories.items));
3776 addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.tag)));
3777 addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.offset)));
3778 // TODO handle the union safety field
3779 //addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.pointee)));
3780 addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.addend)));
3781 addBuf(&bufs, @ptrCast(wasm.object_init_funcs.items));
3782 addBuf(&bufs, @ptrCast(wasm.object_data_segments.items));
3783 addBuf(&bufs, @ptrCast(wasm.object_datas.items));
3784 addBuf(&bufs, @ptrCast(wasm.object_data_imports.keys()));
3785 addBuf(&bufs, @ptrCast(wasm.object_data_imports.values()));
3786 addBuf(&bufs, @ptrCast(wasm.object_custom_segments.keys()));
3787 addBuf(&bufs, @ptrCast(wasm.object_custom_segments.values()));
3788 // TODO make it well-defined memory layout
3789 // addBuf(&bufs, @ptrCast(wasm.object_comdats.items));
3790 addBuf(&bufs, @ptrCast(wasm.object_relocations_table.keys()));
3791 addBuf(&bufs, @ptrCast(wasm.object_relocations_table.values()));
3792 addBuf(&bufs, @ptrCast(wasm.object_comdat_symbols.items(.kind)));
3793 addBuf(&bufs, @ptrCast(wasm.object_comdat_symbols.items(.index)));
3794 addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.tag)));
3795 addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.offset)));
3796 // TODO handle the union safety field
3797 //addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.pointee)));
3798 addBuf(&bufs, @ptrCast(wasm.out_relocs.items(.addend)));
3799 addBuf(&bufs, @ptrCast(wasm.uav_fixups.items));
3800 addBuf(&bufs, @ptrCast(wasm.nav_fixups.items));
3801 addBuf(&bufs, @ptrCast(wasm.func_table_fixups.items));
3802 if (is_obj) {
3803 addBuf(&bufs, @ptrCast(wasm.navs_obj.keys()));
3804 addBuf(&bufs, @ptrCast(wasm.navs_obj.values()));
3805 addBuf(&bufs, @ptrCast(wasm.uavs_obj.keys()));
3806 addBuf(&bufs, @ptrCast(wasm.uavs_obj.values()));
3807 } else {
3808 addBuf(&bufs, @ptrCast(wasm.navs_exe.keys()));
3809 addBuf(&bufs, @ptrCast(wasm.navs_exe.values()));
3810 addBuf(&bufs, @ptrCast(wasm.uavs_exe.keys()));
3811 addBuf(&bufs, @ptrCast(wasm.uavs_exe.values()));
3812 }
3813 addBuf(&bufs, @ptrCast(wasm.overaligned_uavs.keys()));
3814 addBuf(&bufs, @ptrCast(wasm.overaligned_uavs.values()));
3815 addBuf(&bufs, @ptrCast(wasm.zcu_funcs.keys()));
3816 // TODO handle the union safety field
3817 // addBuf(&bufs, @ptrCast(wasm.zcu_funcs.values()));
3818 addBuf(&bufs, @ptrCast(wasm.nav_exports.keys()));
3819 addBuf(&bufs, @ptrCast(wasm.nav_exports.values()));
3820 addBuf(&bufs, @ptrCast(wasm.uav_exports.keys()));
3821 addBuf(&bufs, @ptrCast(wasm.uav_exports.values()));
3822 addBuf(&bufs, @ptrCast(wasm.imports.keys()));
3823 addBuf(&bufs, @ptrCast(wasm.missing_exports.keys()));
3824 addBuf(&bufs, @ptrCast(wasm.function_exports.keys()));
3825 addBuf(&bufs, @ptrCast(wasm.function_exports.values()));
3826 addBuf(&bufs, @ptrCast(wasm.hidden_function_exports.keys()));
3827 addBuf(&bufs, @ptrCast(wasm.hidden_function_exports.values()));
3828 addBuf(&bufs, @ptrCast(wasm.global_exports.items));
3829 addBuf(&bufs, @ptrCast(wasm.functions.keys()));
3830 addBuf(&bufs, @ptrCast(wasm.function_imports.keys()));
3831 addBuf(&bufs, @ptrCast(wasm.function_imports.values()));
3832 addBuf(&bufs, @ptrCast(wasm.data_imports.keys()));
3833 addBuf(&bufs, @ptrCast(wasm.data_imports.values()));
3834 addBuf(&bufs, @ptrCast(wasm.data_segments.keys()));
3835 addBuf(&bufs, @ptrCast(wasm.globals.keys()));
3836 addBuf(&bufs, @ptrCast(wasm.global_imports.keys()));
3837 addBuf(&bufs, @ptrCast(wasm.global_imports.values()));
3838 addBuf(&bufs, @ptrCast(wasm.tables.keys()));
3839 addBuf(&bufs, @ptrCast(wasm.table_imports.keys()));
3840 addBuf(&bufs, @ptrCast(wasm.table_imports.values()));
3841 addBuf(&bufs, @ptrCast(wasm.zcu_indirect_function_set.keys()));
3842 addBuf(&bufs, @ptrCast(wasm.object_indirect_function_import_set.keys()));
3843 addBuf(&bufs, @ptrCast(wasm.object_indirect_function_set.keys()));
3844 addBuf(&bufs, @ptrCast(wasm.mir_instructions.items(.tag)));
3845 // TODO handle the union safety field
3846 //addBuf(&bufs, @ptrCast(wasm.mir_instructions.items(.data)));
3847 addBuf(&bufs, @ptrCast(wasm.mir_extra.items));
3848 addBuf(&bufs, @ptrCast(wasm.mir_locals.items));
3849 addBuf(&bufs, @ptrCast(wasm.tag_name_bytes.items));
3850 addBuf(&bufs, @ptrCast(wasm.tag_name_offs.items));
3851
3852 // TODO add as header fields
3853 // entry_resolution: FunctionImport.Resolution
3854 // function_exports_len: u32
3855 // global_exports_len: u32
3856 // functions_end_prelink: u32
3857 // globals_end_prelink: u32
3858 // error_name_table_ref_count: u32
3859 // tag_name_table_ref_count: u32
3860 // any_tls_relocs: bool
3861 // any_passive_inits: bool
3862 },
3863 else => log.err("TODO implement saving linker state for {s}", .{@tagName(lf.tag)}),
3864 }
3865
3866 var basename_buf: [255]u8 = undefined;
3867 const basename = std.fmt.bufPrint(&basename_buf, "{s}.zcs", .{
3868 comp.root_name,
3869 }) catch o: {
3870 basename_buf[basename_buf.len - 4 ..].* = ".zcs".*;
3871 break :o &basename_buf;
3872 };
3873
3874 // Using an atomic file prevents a crash or power failure from corrupting
3875 // the previous incremental compilation state.
3876 var write_buffer: [1024]u8 = undefined;
3877 var af = try lf.emit.root_dir.handle.atomicFile(basename, .{ .write_buffer = &write_buffer });
3878 defer af.deinit();
3879 try af.file_writer.interface.writeVecAll(bufs.items);
3880 try af.finish();
3881}
3882
3883fn addBuf(list: *std.array_list.Managed([]const u8), buf: []const u8) void {
3884 if (buf.len == 0) return;
3885 list.appendAssumeCapacity(buf);
3886}
3887
3888/// This function is temporally single-threaded.
3889pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
3890 const gpa = comp.gpa;
3891 const io = comp.io;
3892
3893 var bundle: ErrorBundle.Wip = undefined;
3894 try bundle.init(gpa);
3895 defer bundle.deinit();
3896
3897 for (comp.failed_c_objects.values()) |diag_bundle| {
3898 try diag_bundle.addToErrorBundle(io, &bundle);
3899 }
3900
3901 for (comp.failed_win32_resources.values()) |error_bundle| {
3902 try bundle.addBundleAsRoots(error_bundle);
3903 }
3904
3905 for (comp.link_diags.lld.items) |lld_error| {
3906 const notes_len = @as(u32, @intCast(lld_error.context_lines.len));
3907
3908 try bundle.addRootErrorMessage(.{
3909 .msg = try bundle.addString(lld_error.msg),
3910 .notes_len = notes_len,
3911 });
3912 const notes_start = try bundle.reserveNotes(notes_len);
3913 for (notes_start.., lld_error.context_lines) |note, context_line| {
3914 bundle.extra.items[note] = @intFromEnum(bundle.addErrorMessageAssumeCapacity(.{
3915 .msg = try bundle.addString(context_line),
3916 }));
3917 }
3918 }
3919 for (comp.misc_failures.values()) |*value| {
3920 try bundle.addRootErrorMessage(.{
3921 .msg = try bundle.addString(value.msg),
3922 .notes_len = if (value.children) |b| b.errorMessageCount() else 0,
3923 });
3924 if (value.children) |b| try bundle.addBundleAsNotes(b);
3925 }
3926 if (comp.alloc_failure_occurred or comp.link_diags.flags.alloc_failure_occurred) {
3927 try bundle.addRootErrorMessage(.{
3928 .msg = try bundle.addString("memory allocation failure"),
3929 });
3930 }
3931
3932 if (comp.zcu) |zcu| zcu_errors: {
3933 if (zcu.multi_module_err != null) {
3934 try zcu.addFileInMultipleModulesError(&bundle);
3935 break :zcu_errors;
3936 }
3937 for (zcu.failed_imports.items) |failed| {
3938 assert(zcu.alive_files.contains(failed.file_index)); // otherwise it wouldn't have been added
3939 const file = zcu.fileByIndex(failed.file_index);
3940 const tree = file.getTree(zcu) catch |err| {
3941 try unableToLoadZcuFile(zcu, &bundle, file, err);
3942 continue;
3943 };
3944 const start = tree.tokenStart(failed.import_token);
3945 const end = start + tree.tokenSlice(failed.import_token).len;
3946 const loc = std.zig.findLineColumn(tree.source, start);
3947 try bundle.addRootErrorMessage(.{
3948 .msg = switch (failed.kind) {
3949 .file_outside_module_root => try bundle.addString("import of file outside module path"),
3950 .illegal_zig_import => try bundle.addString("this compiler implementation does not allow importing files from this directory"),
3951 },
3952 .src_loc = try bundle.addSourceLocation(.{
3953 .src_path = try bundle.printString("{f}", .{file.path.fmt(comp)}),
3954 .span_start = start,
3955 .span_main = start,
3956 .span_end = @intCast(end),
3957 .line = @intCast(loc.line),
3958 .column = @intCast(loc.column),
3959 .source_line = try bundle.addString(loc.source_line),
3960 }),
3961 .notes_len = 0,
3962 });
3963 }
3964
3965 // Before iterating `failed_files`, we need to sort it into a consistent order so that error
3966 // messages appear consistently despite different ordering from the AstGen worker pool. File
3967 // paths are a great key for this sort! We are using sorting the `ArrayHashMap` itself to
3968 // make sure it reindexes; that's important because these entries need to be retained for
3969 // future updates.
3970 const FileSortCtx = struct {
3971 zcu: *Zcu,
3972 failed_files_keys: []const Zcu.File.Index,
3973 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
3974 const lhs_path = ctx.zcu.fileByIndex(ctx.failed_files_keys[lhs_index]).path;
3975 const rhs_path = ctx.zcu.fileByIndex(ctx.failed_files_keys[rhs_index]).path;
3976 if (lhs_path.root != rhs_path.root) return @intFromEnum(lhs_path.root) < @intFromEnum(rhs_path.root);
3977 return std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).compare(.lt);
3978 }
3979 };
3980 zcu.failed_files.sort(@as(FileSortCtx, .{
3981 .zcu = zcu,
3982 .failed_files_keys = zcu.failed_files.keys(),
3983 }));
3984
3985 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file_index, error_msg| {
3986 if (!zcu.alive_files.contains(file_index)) continue;
3987 const file = zcu.fileByIndex(file_index);
3988 const is_retryable = switch (file.status) {
3989 .retryable_failure => true,
3990 .success, .astgen_failure => false,
3991 .never_loaded => unreachable,
3992 };
3993 if (error_msg) |msg| {
3994 assert(is_retryable);
3995 try addWholeFileError(zcu, &bundle, file_index, msg);
3996 } else {
3997 assert(!is_retryable);
3998 // AstGen/ZoirGen succeeded with errors. Note that this may include AST errors.
3999 // Tree must be loaded.
4000 _ = file.getTree(zcu) catch |err| {
4001 try unableToLoadZcuFile(zcu, &bundle, file, err);
4002 continue;
4003 };
4004 const path = try std.fmt.allocPrint(gpa, "{f}", .{file.path.fmt(comp)});
4005 defer gpa.free(path);
4006 if (file.zir != null) {
4007 try bundle.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, path);
4008 } else if (file.zoir != null) {
4009 try bundle.addZoirErrorMessages(file.zoir.?, file.tree.?, file.source.?, path);
4010 } else {
4011 // Either Zir or Zoir must have been loaded.
4012 unreachable;
4013 }
4014 }
4015 }
4016 if (zcu.skip_analysis_this_update) break :zcu_errors;
4017 var sorted_failed_analysis: std.AutoArrayHashMapUnmanaged(InternPool.AnalUnit, *Zcu.ErrorMsg).DataList.Slice = s: {
4018 const SortOrder = struct {
4019 zcu: *Zcu,
4020 errors: []const *Zcu.ErrorMsg,
4021 read_err: *?ReadError,
4022 const ReadError = struct {
4023 file: *Zcu.File,
4024 err: Zcu.File.GetSourceError,
4025 };
4026 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
4027 if (ctx.read_err.* != null) return lhs_index < rhs_index;
4028 var bad_file: *Zcu.File = undefined;
4029 return ctx.errors[lhs_index].src_loc.lessThan(ctx.errors[rhs_index].src_loc, ctx.zcu, &bad_file) catch |err| {
4030 ctx.read_err.* = .{
4031 .file = bad_file,
4032 .err = err,
4033 };
4034 return lhs_index < rhs_index;
4035 };
4036 }
4037 };
4038
4039 // We can't directly sort `zcu.failed_analysis.entries`, because that would leave the map
4040 // in an invalid state, and we need it intact for future incremental updates. The amount
4041 // of data here is only as large as the number of analysis errors, so just dupe it all.
4042 var entries = try zcu.failed_analysis.entries.clone(gpa);
4043 errdefer entries.deinit(gpa);
4044
4045 var read_err: ?SortOrder.ReadError = null;
4046 entries.sort(SortOrder{
4047 .zcu = zcu,
4048 .errors = entries.items(.value),
4049 .read_err = &read_err,
4050 });
4051 if (read_err) |e| {
4052 try unableToLoadZcuFile(zcu, &bundle, e.file, e.err);
4053 break :zcu_errors;
4054 }
4055 break :s entries.slice();
4056 };
4057 defer sorted_failed_analysis.deinit(gpa);
4058 var added_any_analysis_error = false;
4059 for (sorted_failed_analysis.items(.key), sorted_failed_analysis.items(.value)) |anal_unit, error_msg| {
4060 if (comp.config.incremental) {
4061 const refs = try zcu.resolveReferences();
4062 if (!refs.contains(anal_unit)) continue;
4063 }
4064
4065 std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{f}'", .{
4066 error_msg.msg, zcu.fmtAnalUnit(anal_unit),
4067 });
4068
4069 try addModuleErrorMsg(zcu, &bundle, error_msg.*, added_any_analysis_error);
4070 added_any_analysis_error = true;
4071
4072 if (zcu.cimport_errors.get(anal_unit)) |errors| {
4073 for (errors.getMessages()) |err_msg_index| {
4074 const err_msg = errors.getErrorMessage(err_msg_index);
4075 try bundle.addRootErrorMessage(.{
4076 .msg = try bundle.addString(errors.nullTerminatedString(err_msg.msg)),
4077 .src_loc = if (err_msg.src_loc != .none) blk: {
4078 const src_loc = errors.getSourceLocation(err_msg.src_loc);
4079 break :blk try bundle.addSourceLocation(.{
4080 .src_path = try bundle.addString(errors.nullTerminatedString(src_loc.src_path)),
4081 .span_start = src_loc.span_start,
4082 .span_main = src_loc.span_main,
4083 .span_end = src_loc.span_end,
4084 .line = src_loc.line,
4085 .column = src_loc.column,
4086 .source_line = if (src_loc.source_line != 0) try bundle.addString(errors.nullTerminatedString(src_loc.source_line)) else 0,
4087 });
4088 } else .none,
4089 });
4090 }
4091 }
4092 }
4093 for (zcu.failed_codegen.values()) |error_msg| {
4094 try addModuleErrorMsg(zcu, &bundle, error_msg.*, false);
4095 }
4096 for (zcu.failed_types.values()) |error_msg| {
4097 try addModuleErrorMsg(zcu, &bundle, error_msg.*, false);
4098 }
4099 for (zcu.failed_exports.values()) |value| {
4100 try addModuleErrorMsg(zcu, &bundle, value.*, false);
4101 }
4102
4103 const actual_error_count = zcu.intern_pool.global_error_set.getNamesFromMainThread().len;
4104 if (actual_error_count > zcu.error_limit) {
4105 try bundle.addRootErrorMessage(.{
4106 .msg = try bundle.printString("ZCU used more errors than possible: used {d}, max {d}", .{
4107 actual_error_count, zcu.error_limit,
4108 }),
4109 .notes_len = 1,
4110 });
4111 const notes_start = try bundle.reserveNotes(1);
4112 bundle.extra.items[notes_start] = @intFromEnum(try bundle.addErrorMessage(.{
4113 .msg = try bundle.printString("use '--error-limit {d}' to increase limit", .{
4114 actual_error_count,
4115 }),
4116 }));
4117 }
4118 }
4119
4120 if (bundle.root_list.items.len == 0) {
4121 if (comp.link_diags.flags.no_entry_point_found) {
4122 try bundle.addRootErrorMessage(.{
4123 .msg = try bundle.addString("no entry point found"),
4124 });
4125 }
4126 }
4127
4128 if (comp.link_diags.flags.missing_libc) {
4129 try bundle.addRootErrorMessage(.{
4130 .msg = try bundle.addString("libc not available"),
4131 .notes_len = 2,
4132 });
4133 const notes_start = try bundle.reserveNotes(2);
4134 bundle.extra.items[notes_start + 0] = @intFromEnum(try bundle.addErrorMessage(.{
4135 .msg = try bundle.addString("run 'zig libc -h' to learn about libc installations"),
4136 }));
4137 bundle.extra.items[notes_start + 1] = @intFromEnum(try bundle.addErrorMessage(.{
4138 .msg = try bundle.addString("run 'zig targets' to see the targets for which zig can always provide libc"),
4139 }));
4140 }
4141
4142 try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file);
4143
4144 const compile_log_text: []const u8 = compile_log_text: {
4145 const zcu = comp.zcu orelse break :compile_log_text "";
4146 if (zcu.skip_analysis_this_update) break :compile_log_text "";
4147 if (zcu.compile_logs.count() == 0) break :compile_log_text "";
4148
4149 // If there are no other errors, we include a "found compile log statement" error.
4150 // Otherwise, we just show the compile log output, with no error.
4151 const include_compile_log_sources = bundle.root_list.items.len == 0;
4152
4153 const refs = try zcu.resolveReferences();
4154
4155 var messages: std.ArrayList(Zcu.ErrorMsg) = .empty;
4156 defer messages.deinit(gpa);
4157 for (zcu.compile_logs.keys(), zcu.compile_logs.values()) |logging_unit, compile_log| {
4158 if (!refs.contains(logging_unit)) continue;
4159 try messages.append(gpa, .{
4160 .src_loc = compile_log.src(),
4161 .msg = undefined, // populated later
4162 .notes = &.{},
4163 // We actually clear this later for most of these, but we populate
4164 // this field for now to avoid having to allocate more data to track
4165 // which compile log text this corresponds to.
4166 .reference_trace_root = logging_unit.toOptional(),
4167 });
4168 }
4169
4170 if (messages.items.len == 0) break :compile_log_text "";
4171
4172 // Okay, there *are* referenced compile logs. Sort them into a consistent order.
4173
4174 {
4175 const SortContext = struct {
4176 zcu: *Zcu,
4177 read_err: *?ReadError,
4178 const ReadError = struct {
4179 file: *Zcu.File,
4180 err: Zcu.File.GetSourceError,
4181 };
4182 fn lessThan(ctx: @This(), lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool {
4183 if (ctx.read_err.* != null) return false;
4184 var bad_file: *Zcu.File = undefined;
4185 return lhs.src_loc.lessThan(rhs.src_loc, ctx.zcu, &bad_file) catch |err| {
4186 ctx.read_err.* = .{
4187 .file = bad_file,
4188 .err = err,
4189 };
4190 return false;
4191 };
4192 }
4193 };
4194 var read_err: ?SortContext.ReadError = null;
4195 std.mem.sort(Zcu.ErrorMsg, messages.items, @as(SortContext, .{ .read_err = &read_err, .zcu = zcu }), SortContext.lessThan);
4196 if (read_err) |e| {
4197 try unableToLoadZcuFile(zcu, &bundle, e.file, e.err);
4198 break :compile_log_text "";
4199 }
4200 }
4201
4202 var log_text: std.ArrayList(u8) = .empty;
4203 defer log_text.deinit(gpa);
4204
4205 // Index 0 will be the root message; the rest will be notes.
4206 // Only the actual message, i.e. index 0, will retain its reference trace.
4207 try appendCompileLogLines(&log_text, zcu, messages.items[0].reference_trace_root.unwrap().?);
4208 messages.items[0].notes = messages.items[1..];
4209 messages.items[0].msg = "found compile log statement";
4210 for (messages.items[1..]) |*note| {
4211 try appendCompileLogLines(&log_text, zcu, note.reference_trace_root.unwrap().?);
4212 note.reference_trace_root = .none; // notes don't have reference traces
4213 note.msg = "also here";
4214 }
4215
4216 // We don't actually include the error here if `!include_compile_log_sources`.
4217 // The sorting above was still necessary, though, to get `log_text` in the right order.
4218 if (include_compile_log_sources) {
4219 try addModuleErrorMsg(zcu, &bundle, messages.items[0], false);
4220 }
4221
4222 break :compile_log_text try log_text.toOwnedSlice(gpa);
4223 };
4224
4225 // TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a
4226 // very common way for incremental compilation bugs to manifest, so let's always check it.
4227 if (comp.zcu) |zcu| if (comp.config.incremental and bundle.root_list.items.len == 0) {
4228 for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
4229 const refs = try zcu.resolveReferences();
4230 var ref = refs.get(failed_unit) orelse continue;
4231 // This AU is referenced and has a transitive compile error, meaning it referenced something with a compile error.
4232 // However, we haven't reported any such error.
4233 // This is a compiler bug.
4234 print_ctx: {
4235 var stderr_w, _ = std.debug.lockStderrWriter(&.{});
4236 defer std.debug.unlockStderrWriter();
4237 stderr_w.writeAll("referenced transitive analysis errors, but none actually emitted\n") catch break :print_ctx;
4238 stderr_w.print("{f} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)}) catch break :print_ctx;
4239 while (ref) |r| {
4240 stderr_w.print("referenced by: {f}{s}\n", .{
4241 zcu.fmtAnalUnit(r.referencer),
4242 if (zcu.transitive_failed_analysis.contains(r.referencer)) " [transitive failure]" else "",
4243 }) catch break :print_ctx;
4244 ref = refs.get(r.referencer).?;
4245 }
4246 }
4247 @panic("referenced transitive analysis errors, but none actually emitted");
4248 }
4249 };
4250
4251 return bundle.toOwnedBundle(compile_log_text);
4252}
4253
4254/// Writes all compile log lines belonging to `logging_unit` into `log_text` using `zcu.gpa`.
4255fn appendCompileLogLines(log_text: *std.ArrayList(u8), zcu: *Zcu, logging_unit: InternPool.AnalUnit) Allocator.Error!void {
4256 const gpa = zcu.gpa;
4257 const ip = &zcu.intern_pool;
4258 var opt_line_idx = zcu.compile_logs.get(logging_unit).?.first_line.toOptional();
4259 while (opt_line_idx.unwrap()) |line_idx| {
4260 const line = line_idx.get(zcu).*;
4261 opt_line_idx = line.next;
4262 const line_slice = line.data.toSlice(ip);
4263 try log_text.ensureUnusedCapacity(gpa, line_slice.len + 1);
4264 log_text.appendSliceAssumeCapacity(line_slice);
4265 log_text.appendAssumeCapacity('\n');
4266 }
4267}
4268
4269pub fn anyErrors(comp: *Compilation) bool {
4270 var errors = comp.getAllErrorsAlloc() catch return true;
4271 defer errors.deinit(comp.gpa);
4272 return errors.errorMessageCount() > 0;
4273}
4274
4275pub const ErrorNoteHashContext = struct {
4276 eb: *const ErrorBundle.Wip,
4277
4278 pub fn hash(ctx: ErrorNoteHashContext, key: ErrorBundle.ErrorMessage) u32 {
4279 var hasher = std.hash.Wyhash.init(0);
4280 const eb = ctx.eb.tmpBundle();
4281
4282 hasher.update(eb.nullTerminatedString(key.msg));
4283 if (key.src_loc != .none) {
4284 const src = eb.getSourceLocation(key.src_loc);
4285 hasher.update(eb.nullTerminatedString(src.src_path));
4286 std.hash.autoHash(&hasher, src.line);
4287 std.hash.autoHash(&hasher, src.column);
4288 std.hash.autoHash(&hasher, src.span_main);
4289 }
4290
4291 return @as(u32, @truncate(hasher.final()));
4292 }
4293
4294 pub fn eql(
4295 ctx: ErrorNoteHashContext,
4296 a: ErrorBundle.ErrorMessage,
4297 b: ErrorBundle.ErrorMessage,
4298 b_index: usize,
4299 ) bool {
4300 _ = b_index;
4301 const eb = ctx.eb.tmpBundle();
4302 const msg_a = eb.nullTerminatedString(a.msg);
4303 const msg_b = eb.nullTerminatedString(b.msg);
4304 if (!mem.eql(u8, msg_a, msg_b)) return false;
4305
4306 if (a.src_loc == .none and b.src_loc == .none) return true;
4307 if (a.src_loc == .none or b.src_loc == .none) return false;
4308 const src_a = eb.getSourceLocation(a.src_loc);
4309 const src_b = eb.getSourceLocation(b.src_loc);
4310
4311 const src_path_a = eb.nullTerminatedString(src_a.src_path);
4312 const src_path_b = eb.nullTerminatedString(src_b.src_path);
4313
4314 return mem.eql(u8, src_path_a, src_path_b) and
4315 src_a.line == src_b.line and
4316 src_a.column == src_b.column and
4317 src_a.span_main == src_b.span_main;
4318 }
4319};
4320
4321const default_reference_trace_len = 2;
4322pub fn addModuleErrorMsg(
4323 zcu: *Zcu,
4324 eb: *ErrorBundle.Wip,
4325 module_err_msg: Zcu.ErrorMsg,
4326 /// If `-freference-trace` is not specified, we only want to show the one reference trace.
4327 /// So, this is whether we have already emitted an error with a reference trace.
4328 already_added_error: bool,
4329) Allocator.Error!void {
4330 const gpa = eb.gpa;
4331 const ip = &zcu.intern_pool;
4332 const err_src_loc = module_err_msg.src_loc.upgrade(zcu);
4333 const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| {
4334 return unableToLoadZcuFile(zcu, eb, err_src_loc.file_scope, err);
4335 };
4336 const err_span = err_src_loc.span(zcu) catch |err| {
4337 return unableToLoadZcuFile(zcu, eb, err_src_loc.file_scope, err);
4338 };
4339 const err_loc = std.zig.findLineColumn(err_source, err_span.main);
4340
4341 var ref_traces: std.ArrayList(ErrorBundle.ReferenceTrace) = .empty;
4342 defer ref_traces.deinit(gpa);
4343
4344 rt: {
4345 const rt_root = module_err_msg.reference_trace_root.unwrap() orelse break :rt;
4346 const max_references = zcu.comp.reference_trace orelse refs: {
4347 if (already_added_error) break :rt;
4348 break :refs default_reference_trace_len;
4349 };
4350
4351 const all_references = try zcu.resolveReferences();
4352
4353 var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty;
4354 defer seen.deinit(gpa);
4355
4356 var referenced_by = rt_root;
4357 while (all_references.get(referenced_by)) |maybe_ref| {
4358 const ref = maybe_ref orelse break;
4359 const gop = try seen.getOrPut(gpa, ref.referencer);
4360 if (gop.found_existing) break;
4361 if (ref_traces.items.len < max_references) {
4362 var last_call_src = ref.src;
4363 var opt_inline_frame = ref.inline_frame;
4364 while (opt_inline_frame.unwrap()) |inline_frame| {
4365 const f = inline_frame.ptr(zcu).*;
4366 const func_nav = ip.indexToKey(f.callee).func.owner_nav;
4367 const func_name = ip.getNav(func_nav).name.toSlice(ip);
4368 addReferenceTraceFrame(zcu, eb, &ref_traces, func_name, last_call_src, true) catch |err| switch (err) {
4369 error.OutOfMemory => |e| return e,
4370 error.AlreadyReported => {
4371 // An incomplete reference trace isn't the end of the world; just cut it off.
4372 break :rt;
4373 },
4374 };
4375 last_call_src = f.call_src;
4376 opt_inline_frame = f.parent;
4377 }
4378 const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) {
4379 .@"comptime" => "comptime",
4380 .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
4381 .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
4382 .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
4383 .memoized_state => null,
4384 };
4385 if (root_name) |n| {
4386 addReferenceTraceFrame(zcu, eb, &ref_traces, n, last_call_src, false) catch |err| switch (err) {
4387 error.OutOfMemory => |e| return e,
4388 error.AlreadyReported => {
4389 // An incomplete reference trace isn't the end of the world; just cut it off.
4390 break :rt;
4391 },
4392 };
4393 }
4394 }
4395 referenced_by = ref.referencer;
4396 }
4397
4398 if (seen.count() > ref_traces.items.len) {
4399 try ref_traces.append(gpa, .{
4400 .decl_name = @intCast(seen.count() - ref_traces.items.len),
4401 .src_loc = .none,
4402 });
4403 }
4404 }
4405
4406 const src_loc = try eb.addSourceLocation(.{
4407 .src_path = try eb.printString("{f}", .{err_src_loc.file_scope.path.fmt(zcu.comp)}),
4408 .span_start = err_span.start,
4409 .span_main = err_span.main,
4410 .span_end = err_span.end,
4411 .line = @intCast(err_loc.line),
4412 .column = @intCast(err_loc.column),
4413 .source_line = try eb.addString(err_loc.source_line),
4414 .reference_trace_len = @intCast(ref_traces.items.len),
4415 });
4416
4417 for (ref_traces.items) |rt| {
4418 try eb.addReferenceTrace(rt);
4419 }
4420
4421 // De-duplicate error notes. The main use case in mind for this is
4422 // too many "note: called from here" notes when eval branch quota is reached.
4423 var notes: std.ArrayHashMapUnmanaged(ErrorBundle.ErrorMessage, void, ErrorNoteHashContext, true) = .empty;
4424 defer notes.deinit(gpa);
4425
4426 var last_note_loc: ?std.zig.Loc = null;
4427 for (module_err_msg.notes) |module_note| {
4428 const note_src_loc = module_note.src_loc.upgrade(zcu);
4429 const source = note_src_loc.file_scope.getSource(zcu) catch |err| {
4430 return unableToLoadZcuFile(zcu, eb, note_src_loc.file_scope, err);
4431 };
4432 const span = note_src_loc.span(zcu) catch |err| {
4433 return unableToLoadZcuFile(zcu, eb, note_src_loc.file_scope, err);
4434 };
4435 const loc = std.zig.findLineColumn(source, span.main);
4436
4437 const omit_source_line = loc.eql(err_loc) or (last_note_loc != null and loc.eql(last_note_loc.?));
4438 last_note_loc = loc;
4439
4440 const gop = try notes.getOrPutContext(gpa, .{
4441 .msg = try eb.addString(module_note.msg),
4442 .src_loc = try eb.addSourceLocation(.{
4443 .src_path = try eb.printString("{f}", .{note_src_loc.file_scope.path.fmt(zcu.comp)}),
4444 .span_start = span.start,
4445 .span_main = span.main,
4446 .span_end = span.end,
4447 .line = @intCast(loc.line),
4448 .column = @intCast(loc.column),
4449 .source_line = if (omit_source_line) 0 else try eb.addString(loc.source_line),
4450 }),
4451 }, .{ .eb = eb });
4452 if (gop.found_existing) {
4453 gop.key_ptr.count += 1;
4454 }
4455 }
4456
4457 const notes_len: u32 = @intCast(notes.entries.len);
4458
4459 try eb.addRootErrorMessage(.{
4460 .msg = try eb.addString(module_err_msg.msg),
4461 .src_loc = src_loc,
4462 .notes_len = notes_len,
4463 });
4464
4465 const notes_start = try eb.reserveNotes(notes_len);
4466
4467 for (notes_start.., notes.keys()) |i, note| {
4468 eb.extra.items[i] = @intFromEnum(try eb.addErrorMessage(note));
4469 }
4470}
4471
4472fn addReferenceTraceFrame(
4473 zcu: *Zcu,
4474 eb: *ErrorBundle.Wip,
4475 ref_traces: *std.ArrayList(ErrorBundle.ReferenceTrace),
4476 name: []const u8,
4477 lazy_src: Zcu.LazySrcLoc,
4478 inlined: bool,
4479) error{ OutOfMemory, AlreadyReported }!void {
4480 const gpa = zcu.gpa;
4481 const src = lazy_src.upgrade(zcu);
4482 const source = src.file_scope.getSource(zcu) catch |err| {
4483 try unableToLoadZcuFile(zcu, eb, src.file_scope, err);
4484 return error.AlreadyReported;
4485 };
4486 const span = src.span(zcu) catch |err| {
4487 try unableToLoadZcuFile(zcu, eb, src.file_scope, err);
4488 return error.AlreadyReported;
4489 };
4490 const loc = std.zig.findLineColumn(source, span.main);
4491 try ref_traces.append(gpa, .{
4492 .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }),
4493 .src_loc = try eb.addSourceLocation(.{
4494 .src_path = try eb.printString("{f}", .{src.file_scope.path.fmt(zcu.comp)}),
4495 .span_start = span.start,
4496 .span_main = span.main,
4497 .span_end = span.end,
4498 .line = @intCast(loc.line),
4499 .column = @intCast(loc.column),
4500 .source_line = 0,
4501 }),
4502 });
4503}
4504
4505fn addWholeFileError(
4506 zcu: *Zcu,
4507 eb: *ErrorBundle.Wip,
4508 file_index: Zcu.File.Index,
4509 msg: []const u8,
4510) Allocator.Error!void {
4511 // note: "file imported here" on the import reference token
4512 const imported_note: ?ErrorBundle.MessageIndex = switch (zcu.alive_files.get(file_index).?) {
4513 .analysis_root => null,
4514 .import => |import| note: {
4515 const file = zcu.fileByIndex(import.importer);
4516 // `errorBundleTokenSrc` expects the tree to be loaded
4517 _ = file.getTree(zcu) catch |err| {
4518 return unableToLoadZcuFile(zcu, eb, file, err);
4519 };
4520 break :note try eb.addErrorMessage(.{
4521 .msg = try eb.addString("file imported here"),
4522 .src_loc = try file.errorBundleTokenSrc(import.tok, zcu, eb),
4523 });
4524 },
4525 };
4526
4527 try eb.addRootErrorMessage(.{
4528 .msg = try eb.addString(msg),
4529 .src_loc = try zcu.fileByIndex(file_index).errorBundleWholeFileSrc(zcu, eb),
4530 .notes_len = if (imported_note != null) 1 else 0,
4531 });
4532 if (imported_note) |n| {
4533 const note_idx = try eb.reserveNotes(1);
4534 eb.extra.items[note_idx] = @intFromEnum(n);
4535 }
4536}
4537
4538/// Adds an error to `eb` that the contents of `file` could not be loaded due to `err`. This is
4539/// useful if `Zcu.File.getSource`/`Zcu.File.getTree` fails while lowering compile errors.
4540pub fn unableToLoadZcuFile(
4541 zcu: *const Zcu,
4542 eb: *ErrorBundle.Wip,
4543 file: *Zcu.File,
4544 err: Zcu.File.GetSourceError,
4545) Allocator.Error!void {
4546 const msg = switch (err) {
4547 error.OutOfMemory => |e| return e,
4548 error.FileChanged => try eb.addString("file contents changed during update"),
4549 else => |e| try eb.printString("unable to load: {t}", .{e}),
4550 };
4551 try eb.addRootErrorMessage(.{
4552 .msg = msg,
4553 .src_loc = try file.errorBundleWholeFileSrc(zcu, eb),
4554 });
4555}
4556
4557fn performAllTheWork(
4558 comp: *Compilation,
4559 main_progress_node: std.Progress.Node,
4560) JobError!void {
4561 // Regardless of errors, `comp.zcu` needs to update its generation number.
4562 defer if (comp.zcu) |zcu| {
4563 zcu.generation += 1;
4564 };
4565
4566 // This is awkward: we don't want to start the timer until later, but we won't want to stop it
4567 // until the wait groups finish. That means we need do do this.
4568 var decl_work_timer: ?Timer = null;
4569 defer commit_timer: {
4570 const t = &(decl_work_timer orelse break :commit_timer);
4571 const ns = t.finish() orelse break :commit_timer;
4572 comp.mutex.lock();
4573 defer comp.mutex.unlock();
4574 comp.time_report.?.stats.real_ns_decls = ns;
4575 }
4576
4577 // Here we queue up all the AstGen tasks first, followed by C object compilation.
4578 // We wait until the AstGen tasks are all completed before proceeding to the
4579 // (at least for now) single-threaded main work queue. However, C object compilation
4580 // only needs to be finished by the end of this function.
4581
4582 var work_queue_wait_group: WaitGroup = .{};
4583 defer work_queue_wait_group.wait();
4584
4585 comp.link_task_wait_group.reset();
4586 defer comp.link_task_wait_group.wait();
4587
4588 // Already-queued prelink tasks
4589 comp.link_prog_node.increaseEstimatedTotalItems(comp.link_task_queue.queued_prelink.items.len);
4590 comp.link_task_queue.start(comp);
4591
4592 if (comp.emit_docs != null) {
4593 dev.check(.docs_emit);
4594 comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp});
4595 work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });
4596 }
4597
4598 // In case it failed last time, try again. `clearMiscFailures` was already
4599 // called at the start of `update`.
4600 if (comp.queued_jobs.compiler_rt_lib and comp.compiler_rt_lib == null) {
4601 // LLVM disables LTO for its compiler-rt and we've had various issues with LTO of our
4602 // compiler-rt due to LLD bugs as well, e.g.:
4603 //
4604 // https://github.com/llvm/llvm-project/issues/43698#issuecomment-2542660611
4605 comp.link_task_queue.startPrelinkItem();
4606 comp.link_task_wait_group.spawnManager(buildRt, .{
4607 comp,
4608 "compiler_rt.zig",
4609 "compiler_rt",
4610 .Lib,
4611 .static,
4612 .compiler_rt,
4613 main_progress_node,
4614 RtOptions{
4615 .checks_valgrind = true,
4616 .allow_lto = false,
4617 },
4618 &comp.compiler_rt_lib,
4619 });
4620 }
4621
4622 if (comp.queued_jobs.compiler_rt_obj and comp.compiler_rt_obj == null) {
4623 comp.link_task_queue.startPrelinkItem();
4624 comp.link_task_wait_group.spawnManager(buildRt, .{
4625 comp,
4626 "compiler_rt.zig",
4627 "compiler_rt",
4628 .Obj,
4629 .static,
4630 .compiler_rt,
4631 main_progress_node,
4632 RtOptions{
4633 .checks_valgrind = true,
4634 .allow_lto = false,
4635 },
4636 &comp.compiler_rt_obj,
4637 });
4638 }
4639
4640 // hack for stage2_x86_64 + coff
4641 if (comp.queued_jobs.compiler_rt_dyn_lib and comp.compiler_rt_dyn_lib == null) {
4642 comp.link_task_queue.startPrelinkItem();
4643 comp.link_task_wait_group.spawnManager(buildRt, .{
4644 comp,
4645 "compiler_rt.zig",
4646 "compiler_rt",
4647 .Lib,
4648 .dynamic,
4649 .compiler_rt,
4650 main_progress_node,
4651 RtOptions{
4652 .checks_valgrind = true,
4653 .allow_lto = false,
4654 },
4655 &comp.compiler_rt_dyn_lib,
4656 });
4657 }
4658
4659 if (comp.queued_jobs.fuzzer_lib and comp.fuzzer_lib == null) {
4660 comp.link_task_queue.startPrelinkItem();
4661 comp.link_task_wait_group.spawnManager(buildRt, .{
4662 comp,
4663 "fuzzer.zig",
4664 "fuzzer",
4665 .Lib,
4666 .static,
4667 .libfuzzer,
4668 main_progress_node,
4669 RtOptions{},
4670 &comp.fuzzer_lib,
4671 });
4672 }
4673
4674 if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) {
4675 comp.link_task_queue.startPrelinkItem();
4676 comp.link_task_wait_group.spawnManager(buildRt, .{
4677 comp,
4678 "ubsan_rt.zig",
4679 "ubsan_rt",
4680 .Lib,
4681 .static,
4682 .libubsan,
4683 main_progress_node,
4684 RtOptions{
4685 .allow_lto = false,
4686 },
4687 &comp.ubsan_rt_lib,
4688 });
4689 }
4690
4691 if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) {
4692 comp.link_task_queue.startPrelinkItem();
4693 comp.link_task_wait_group.spawnManager(buildRt, .{
4694 comp,
4695 "ubsan_rt.zig",
4696 "ubsan_rt",
4697 .Obj,
4698 .static,
4699 .libubsan,
4700 main_progress_node,
4701 RtOptions{
4702 .allow_lto = false,
4703 },
4704 &comp.ubsan_rt_obj,
4705 });
4706 }
4707
4708 if (comp.queued_jobs.glibc_shared_objects) {
4709 comp.link_task_queue.startPrelinkItem();
4710 comp.link_task_wait_group.spawnManager(buildGlibcSharedObjects, .{ comp, main_progress_node });
4711 }
4712
4713 if (comp.queued_jobs.freebsd_shared_objects) {
4714 comp.link_task_queue.startPrelinkItem();
4715 comp.link_task_wait_group.spawnManager(buildFreeBSDSharedObjects, .{ comp, main_progress_node });
4716 }
4717
4718 if (comp.queued_jobs.netbsd_shared_objects) {
4719 comp.link_task_queue.startPrelinkItem();
4720 comp.link_task_wait_group.spawnManager(buildNetBSDSharedObjects, .{ comp, main_progress_node });
4721 }
4722
4723 if (comp.queued_jobs.libunwind) {
4724 comp.link_task_queue.startPrelinkItem();
4725 comp.link_task_wait_group.spawnManager(buildLibUnwind, .{ comp, main_progress_node });
4726 }
4727
4728 if (comp.queued_jobs.libcxx) {
4729 comp.link_task_queue.startPrelinkItem();
4730 comp.link_task_wait_group.spawnManager(buildLibCxx, .{ comp, main_progress_node });
4731 }
4732
4733 if (comp.queued_jobs.libcxxabi) {
4734 comp.link_task_queue.startPrelinkItem();
4735 comp.link_task_wait_group.spawnManager(buildLibCxxAbi, .{ comp, main_progress_node });
4736 }
4737
4738 if (comp.queued_jobs.libtsan) {
4739 comp.link_task_queue.startPrelinkItem();
4740 comp.link_task_wait_group.spawnManager(buildLibTsan, .{ comp, main_progress_node });
4741 }
4742
4743 if (comp.queued_jobs.zigc_lib and comp.zigc_static_lib == null) {
4744 comp.link_task_queue.startPrelinkItem();
4745 comp.link_task_wait_group.spawnManager(buildLibZigC, .{ comp, main_progress_node });
4746 }
4747
4748 for (0..@typeInfo(musl.CrtFile).@"enum".fields.len) |i| {
4749 if (comp.queued_jobs.musl_crt_file[i]) {
4750 const tag: musl.CrtFile = @enumFromInt(i);
4751 comp.link_task_queue.startPrelinkItem();
4752 comp.link_task_wait_group.spawnManager(buildMuslCrtFile, .{ comp, tag, main_progress_node });
4753 }
4754 }
4755
4756 for (0..@typeInfo(glibc.CrtFile).@"enum".fields.len) |i| {
4757 if (comp.queued_jobs.glibc_crt_file[i]) {
4758 const tag: glibc.CrtFile = @enumFromInt(i);
4759 comp.link_task_queue.startPrelinkItem();
4760 comp.link_task_wait_group.spawnManager(buildGlibcCrtFile, .{ comp, tag, main_progress_node });
4761 }
4762 }
4763
4764 for (0..@typeInfo(freebsd.CrtFile).@"enum".fields.len) |i| {
4765 if (comp.queued_jobs.freebsd_crt_file[i]) {
4766 const tag: freebsd.CrtFile = @enumFromInt(i);
4767 comp.link_task_queue.startPrelinkItem();
4768 comp.link_task_wait_group.spawnManager(buildFreeBSDCrtFile, .{ comp, tag, main_progress_node });
4769 }
4770 }
4771
4772 for (0..@typeInfo(netbsd.CrtFile).@"enum".fields.len) |i| {
4773 if (comp.queued_jobs.netbsd_crt_file[i]) {
4774 const tag: netbsd.CrtFile = @enumFromInt(i);
4775 comp.link_task_queue.startPrelinkItem();
4776 comp.link_task_wait_group.spawnManager(buildNetBSDCrtFile, .{ comp, tag, main_progress_node });
4777 }
4778 }
4779
4780 for (0..@typeInfo(wasi_libc.CrtFile).@"enum".fields.len) |i| {
4781 if (comp.queued_jobs.wasi_libc_crt_file[i]) {
4782 const tag: wasi_libc.CrtFile = @enumFromInt(i);
4783 comp.link_task_queue.startPrelinkItem();
4784 comp.link_task_wait_group.spawnManager(buildWasiLibcCrtFile, .{ comp, tag, main_progress_node });
4785 }
4786 }
4787
4788 for (0..@typeInfo(mingw.CrtFile).@"enum".fields.len) |i| {
4789 if (comp.queued_jobs.mingw_crt_file[i]) {
4790 const tag: mingw.CrtFile = @enumFromInt(i);
4791 comp.link_task_queue.startPrelinkItem();
4792 comp.link_task_wait_group.spawnManager(buildMingwCrtFile, .{ comp, tag, main_progress_node });
4793 }
4794 }
4795
4796 {
4797 const astgen_frame = tracy.namedFrame("astgen");
4798 defer astgen_frame.end();
4799
4800 const zir_prog_node = main_progress_node.start("AST Lowering", 0);
4801 defer zir_prog_node.end();
4802
4803 var timer = comp.startTimer();
4804 defer if (timer.finish()) |ns| {
4805 comp.mutex.lock();
4806 defer comp.mutex.unlock();
4807 comp.time_report.?.stats.real_ns_files = ns;
4808 };
4809
4810 var astgen_wait_group: WaitGroup = .{};
4811 defer astgen_wait_group.wait();
4812
4813 if (comp.zcu) |zcu| {
4814 const gpa = zcu.gpa;
4815
4816 // We cannot reference `zcu.import_table` after we spawn any `workerUpdateFile` jobs,
4817 // because on single-threaded targets the worker will be run eagerly, meaning the
4818 // `import_table` could be mutated, and not even holding `comp.mutex` will save us. So,
4819 // build up a list of the files to update *before* we spawn any jobs.
4820 var astgen_work_items: std.MultiArrayList(struct {
4821 file_index: Zcu.File.Index,
4822 file: *Zcu.File,
4823 }) = .empty;
4824 defer astgen_work_items.deinit(gpa);
4825 // Not every item in `import_table` will need updating, because some are builtin.zig
4826 // files. However, most will, so let's just reserve sufficient capacity upfront.
4827 try astgen_work_items.ensureTotalCapacity(gpa, zcu.import_table.count());
4828 for (zcu.import_table.keys()) |file_index| {
4829 const file = zcu.fileByIndex(file_index);
4830 if (file.is_builtin) {
4831 // This is a `builtin.zig`, so updating is redundant. However, we want to make
4832 // sure the file contents are still correct on disk, since it can improve the
4833 // debugging experience better. That job only needs `file`, so we can kick it
4834 // off right now.
4835 comp.thread_pool.spawnWg(&astgen_wait_group, workerUpdateBuiltinFile, .{ comp, file });
4836 continue;
4837 }
4838 astgen_work_items.appendAssumeCapacity(.{
4839 .file_index = file_index,
4840 .file = file,
4841 });
4842 }
4843
4844 // Now that we're not going to touch `zcu.import_table` again, we can spawn `workerUpdateFile` jobs.
4845 for (astgen_work_items.items(.file_index), astgen_work_items.items(.file)) |file_index, file| {
4846 comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateFile, .{
4847 comp, file, file_index, zir_prog_node, &astgen_wait_group,
4848 });
4849 }
4850
4851 // On the other hand, it's fine to directly iterate `zcu.embed_table.keys()` here
4852 // because `workerUpdateEmbedFile` can't invalidate it. The different here is that one
4853 // `@embedFile` can't trigger analysis of a new `@embedFile`!
4854 for (0.., zcu.embed_table.keys()) |ef_index_usize, ef| {
4855 const ef_index: Zcu.EmbedFile.Index = @enumFromInt(ef_index_usize);
4856 comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateEmbedFile, .{
4857 comp, ef_index, ef,
4858 });
4859 }
4860 }
4861
4862 while (comp.c_object_work_queue.popFront()) |c_object| {
4863 comp.link_task_queue.startPrelinkItem();
4864 comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateCObject, .{
4865 comp, c_object, main_progress_node,
4866 });
4867 }
4868
4869 while (comp.win32_resource_work_queue.popFront()) |win32_resource| {
4870 comp.link_task_queue.startPrelinkItem();
4871 comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateWin32Resource, .{
4872 comp, win32_resource, main_progress_node,
4873 });
4874 }
4875 }
4876
4877 if (comp.zcu) |zcu| {
4878 const pt: Zcu.PerThread = .activate(zcu, .main);
4879 defer pt.deactivate();
4880
4881 const gpa = zcu.gpa;
4882
4883 // On an incremental update, a source file might become "dead", in that all imports of
4884 // the file were removed. This could even change what module the file belongs to! As such,
4885 // we do a traversal over the files, to figure out which ones are alive and the modules
4886 // they belong to.
4887 const any_fatal_files = try pt.computeAliveFiles();
4888
4889 // If the cache mode is `whole`, add every alive source file to the manifest.
4890 switch (comp.cache_use) {
4891 .whole => |whole| if (whole.cache_manifest) |man| {
4892 for (zcu.alive_files.keys()) |file_index| {
4893 const file = zcu.fileByIndex(file_index);
4894
4895 switch (file.status) {
4896 .never_loaded => unreachable, // AstGen tried to load it
4897 .retryable_failure => continue, // the file cannot be read; this is a guaranteed error
4898 .astgen_failure, .success => {}, // the file was read successfully
4899 }
4900
4901 const path = try file.path.toAbsolute(comp.dirs, gpa);
4902 defer gpa.free(path);
4903
4904 const result = res: {
4905 whole.cache_manifest_mutex.lock();
4906 defer whole.cache_manifest_mutex.unlock();
4907 if (file.source) |source| {
4908 break :res man.addFilePostContents(path, source, file.stat);
4909 } else {
4910 break :res man.addFilePost(path);
4911 }
4912 };
4913 result catch |err| switch (err) {
4914 error.OutOfMemory => |e| return e,
4915 else => {
4916 try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)});
4917 continue;
4918 },
4919 };
4920 }
4921 },
4922 .none, .incremental => {},
4923 }
4924
4925 if (any_fatal_files or
4926 zcu.multi_module_err != null or
4927 zcu.failed_imports.items.len > 0 or
4928 comp.alloc_failure_occurred)
4929 {
4930 // We give up right now! No updating of ZIR refs, no nothing. The idea is that this prevents
4931 // us from invalidating lots of incremental dependencies due to files with e.g. parse errors.
4932 // However, this means our analysis data is invalid, so we want to omit all analysis errors.
4933 zcu.skip_analysis_this_update = true;
4934 return;
4935 }
4936
4937 if (comp.time_report) |*tr| {
4938 tr.stats.n_reachable_files = @intCast(zcu.alive_files.count());
4939 }
4940
4941 if (comp.config.incremental) {
4942 const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);
4943 defer update_zir_refs_node.end();
4944 try pt.updateZirRefs();
4945 }
4946 try zcu.flushRetryableFailures();
4947
4948 // It's analysis time! Queue up our initial analysis.
4949 for (zcu.analysisRoots()) |mod| {
4950 try comp.queueJob(.{ .analyze_mod = mod });
4951 }
4952
4953 zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
4954 if (comp.bin_file != null) {
4955 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);
4956 }
4957 // We increment `pending_codegen_jobs` so that it doesn't reach 0 until after analysis finishes.
4958 // That prevents the "Code Generation" node from constantly disappearing and reappearing when
4959 // we're probably going to analyze more functions at some point.
4960 assert(zcu.pending_codegen_jobs.swap(1, .monotonic) == 0); // don't let this become 0 until analysis finishes
4961 }
4962 // When analysis ends, delete the progress nodes for "Semantic Analysis" and possibly "Code Generation".
4963 defer if (comp.zcu) |zcu| {
4964 zcu.sema_prog_node.end();
4965 zcu.sema_prog_node = .none;
4966 if (zcu.pending_codegen_jobs.rmw(.Sub, 1, .monotonic) == 1) {
4967 // Decremented to 0, so all done.
4968 zcu.codegen_prog_node.end();
4969 zcu.codegen_prog_node = .none;
4970 }
4971 };
4972
4973 // We aren't going to queue any more prelink tasks.
4974 comp.link_task_queue.finishPrelinkItem(comp);
4975
4976 if (!comp.separateCodegenThreadOk()) {
4977 // Waits until all input files have been parsed.
4978 comp.link_task_wait_group.wait();
4979 comp.link_task_wait_group.reset();
4980 std.log.scoped(.link).debug("finished waiting for link_task_wait_group", .{});
4981 }
4982
4983 if (comp.zcu != null) {
4984 // Start the timer for the "decls" part of the pipeline (Sema, CodeGen, link).
4985 decl_work_timer = comp.startTimer();
4986 }
4987
4988 work: while (true) {
4989 for (&comp.work_queues) |*work_queue| if (work_queue.popFront()) |job| {
4990 try processOneJob(@intFromEnum(Zcu.PerThread.Id.main), comp, job);
4991 continue :work;
4992 };
4993 if (comp.zcu) |zcu| {
4994 // If there's no work queued, check if there's anything outdated
4995 // which we need to work on, and queue it if so.
4996 if (try zcu.findOutdatedToAnalyze()) |outdated| {
4997 try comp.queueJob(switch (outdated.unwrap()) {
4998 .func => |f| .{ .analyze_func = f },
4999 .memoized_state,
5000 .@"comptime",
5001 .nav_ty,
5002 .nav_val,
5003 .type,
5004 => .{ .analyze_comptime_unit = outdated },
5005 });
5006 continue;
5007 }
5008 zcu.sema_prog_node.end();
5009 zcu.sema_prog_node = .none;
5010 }
5011 break;
5012 }
5013}
5014
5015const JobError = Allocator.Error || Io.Cancelable;
5016
5017pub fn queueJob(comp: *Compilation, job: Job) !void {
5018 try comp.work_queues[Job.stage(job)].pushBack(comp.gpa, job);
5019}
5020
5021pub fn queueJobs(comp: *Compilation, jobs: []const Job) !void {
5022 for (jobs) |job| try comp.queueJob(job);
5023}
5024
5025fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
5026 switch (job) {
5027 .codegen_func => |func| {
5028 const zcu = comp.zcu.?;
5029 const gpa = zcu.gpa;
5030 var air = func.air;
5031 errdefer {
5032 zcu.codegen_prog_node.completeOne();
5033 comp.link_prog_node.completeOne();
5034 air.deinit(gpa);
5035 }
5036 if (!air.typesFullyResolved(zcu)) {
5037 // Type resolution failed in a way which affects this function. This is a transitive
5038 // failure, but it doesn't need recording, because this function semantically depends
5039 // on the failed type, so when it is changed the function is updated.
5040 zcu.codegen_prog_node.completeOne();
5041 comp.link_prog_node.completeOne();
5042 air.deinit(gpa);
5043 return;
5044 }
5045 const shared_mir = try gpa.create(link.ZcuTask.LinkFunc.SharedMir);
5046 shared_mir.* = .{
5047 .status = .init(.pending),
5048 .value = undefined,
5049 };
5050 assert(zcu.pending_codegen_jobs.rmw(.Add, 1, .monotonic) > 0); // the "Code Generation" node hasn't been ended
5051 // This value is used as a heuristic to avoid queueing too much AIR/MIR at once (hence
5052 // using a lot of memory). If this would cause too many AIR bytes to be in-flight, we
5053 // will block on the `dispatchZcuLinkTask` call below.
5054 const air_bytes: u32 = @intCast(air.instructions.len * 5 + air.extra.items.len * 4);
5055 if (comp.separateCodegenThreadOk()) {
5056 // `workerZcuCodegen` takes ownership of `air`.
5057 comp.thread_pool.spawnWgId(&comp.link_task_wait_group, workerZcuCodegen, .{ comp, func.func, air, shared_mir });
5058 comp.dispatchZcuLinkTask(tid, .{ .link_func = .{
5059 .func = func.func,
5060 .mir = shared_mir,
5061 .air_bytes = air_bytes,
5062 } });
5063 } else {
5064 {
5065 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
5066 defer pt.deactivate();
5067 pt.runCodegen(func.func, &air, shared_mir);
5068 }
5069 assert(shared_mir.status.load(.monotonic) != .pending);
5070 comp.dispatchZcuLinkTask(tid, .{ .link_func = .{
5071 .func = func.func,
5072 .mir = shared_mir,
5073 .air_bytes = air_bytes,
5074 } });
5075 air.deinit(gpa);
5076 }
5077 },
5078 .link_nav => |nav_index| {
5079 const zcu = comp.zcu.?;
5080 const nav = zcu.intern_pool.getNav(nav_index);
5081 if (nav.analysis != null) {
5082 const unit: InternPool.AnalUnit = .wrap(.{ .nav_val = nav_index });
5083 if (zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit)) {
5084 comp.link_prog_node.completeOne();
5085 return;
5086 }
5087 }
5088 assert(nav.status == .fully_resolved);
5089 if (!Air.valFullyResolved(zcu.navValue(nav_index), zcu)) {
5090 // Type resolution failed in a way which affects this `Nav`. This is a transitive
5091 // failure, but it doesn't need recording, because this `Nav` semantically depends
5092 // on the failed type, so when it is changed the `Nav` will be updated.
5093 comp.link_prog_node.completeOne();
5094 return;
5095 }
5096 comp.dispatchZcuLinkTask(tid, .{ .link_nav = nav_index });
5097 },
5098 .link_type => |ty| {
5099 const zcu = comp.zcu.?;
5100 if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(zcu.gpa);
5101 if (!Air.typeFullyResolved(.fromInterned(ty), zcu)) {
5102 // Type resolution failed in a way which affects this type. This is a transitive
5103 // failure, but it doesn't need recording, because this type semantically depends
5104 // on the failed type, so when that is changed, this type will be updated.
5105 comp.link_prog_node.completeOne();
5106 return;
5107 }
5108 comp.dispatchZcuLinkTask(tid, .{ .link_type = ty });
5109 },
5110 .update_line_number => |ti| {
5111 comp.dispatchZcuLinkTask(tid, .{ .update_line_number = ti });
5112 },
5113 .analyze_func => |func| {
5114 const named_frame = tracy.namedFrame("analyze_func");
5115 defer named_frame.end();
5116
5117 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
5118 defer pt.deactivate();
5119
5120 pt.ensureFuncBodyUpToDate(func) catch |err| switch (err) {
5121 error.OutOfMemory => |e| return e,
5122 error.Canceled => |e| return e,
5123 error.AnalysisFail => return,
5124 };
5125 },
5126 .analyze_comptime_unit => |unit| {
5127 const named_frame = tracy.namedFrame("analyze_comptime_unit");
5128 defer named_frame.end();
5129
5130 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
5131 defer pt.deactivate();
5132
5133 const maybe_err: Zcu.SemaError!void = switch (unit.unwrap()) {
5134 .@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu),
5135 .nav_ty => |nav| pt.ensureNavTypeUpToDate(nav),
5136 .nav_val => |nav| pt.ensureNavValUpToDate(nav),
5137 .type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err,
5138 .memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage),
5139 .func => unreachable,
5140 };
5141 maybe_err catch |err| switch (err) {
5142 error.OutOfMemory => |e| return e,
5143 error.Canceled => |e| return e,
5144 error.AnalysisFail => return,
5145 };
5146
5147 queue_test_analysis: {
5148 if (!comp.config.is_test) break :queue_test_analysis;
5149 const nav = switch (unit.unwrap()) {
5150 .nav_val => |nav| nav,
5151 else => break :queue_test_analysis,
5152 };
5153
5154 // Check if this is a test function.
5155 const ip = &pt.zcu.intern_pool;
5156 if (!pt.zcu.test_functions.contains(nav)) {
5157 break :queue_test_analysis;
5158 }
5159
5160 // Tests are always emitted in test binaries. The decl_refs are created by
5161 // Zcu.populateTestFunctions, but this will not queue body analysis, so do
5162 // that now.
5163 try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.fully_resolved.val);
5164 }
5165 },
5166 .resolve_type_fully => |ty| {
5167 const named_frame = tracy.namedFrame("resolve_type_fully");
5168 defer named_frame.end();
5169
5170 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
5171 defer pt.deactivate();
5172 Type.fromInterned(ty).resolveFully(pt) catch |err| switch (err) {
5173 error.OutOfMemory, error.Canceled => |e| return e,
5174 error.AnalysisFail => return,
5175 };
5176 },
5177 .analyze_mod => |mod| {
5178 const named_frame = tracy.namedFrame("analyze_mod");
5179 defer named_frame.end();
5180
5181 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
5182 defer pt.deactivate();
5183 pt.semaMod(mod) catch |err| switch (err) {
5184 error.OutOfMemory, error.Canceled => |e| return e,
5185 error.AnalysisFail => return,
5186 };
5187 },
5188 .windows_import_lib => |index| {
5189 const named_frame = tracy.namedFrame("windows_import_lib");
5190 defer named_frame.end();
5191
5192 const link_lib = comp.windows_libs.keys()[index];
5193 mingw.buildImportLib(comp, link_lib) catch |err| {
5194 // TODO Surface more error details.
5195 comp.lockAndSetMiscFailure(
5196 .windows_import_lib,
5197 "unable to generate DLL import .lib file for {s}: {t}",
5198 .{ link_lib, err },
5199 );
5200 };
5201 },
5202 }
5203}
5204
5205pub fn separateCodegenThreadOk(comp: *const Compilation) bool {
5206 if (InternPool.single_threaded) return false;
5207 const zcu = comp.zcu orelse return true;
5208 return zcu.backendSupportsFeature(.separate_thread);
5209}
5210
5211fn workerDocsCopy(comp: *Compilation) void {
5212 docsCopyFallible(comp) catch |err| return comp.lockAndSetMiscFailure(
5213 .docs_copy,
5214 "unable to copy autodocs artifacts: {s}",
5215 .{@errorName(err)},
5216 );
5217}
5218
5219fn docsCopyFallible(comp: *Compilation) anyerror!void {
5220 const zcu = comp.zcu orelse return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{});
5221
5222 const docs_path = comp.resolveEmitPath(comp.emit_docs.?);
5223 var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| {
5224 return comp.lockAndSetMiscFailure(
5225 .docs_copy,
5226 "unable to create output directory '{f}': {s}",
5227 .{ docs_path, @errorName(err) },
5228 );
5229 };
5230 defer out_dir.close();
5231
5232 for (&[_][]const u8{ "docs/main.js", "docs/index.html" }) |sub_path| {
5233 const basename = fs.path.basename(sub_path);
5234 comp.dirs.zig_lib.handle.copyFile(sub_path, out_dir, basename, .{}) catch |err| {
5235 comp.lockAndSetMiscFailure(.docs_copy, "unable to copy {s}: {s}", .{
5236 sub_path,
5237 @errorName(err),
5238 });
5239 return;
5240 };
5241 }
5242
5243 var tar_file = out_dir.createFile("sources.tar", .{}) catch |err| {
5244 return comp.lockAndSetMiscFailure(
5245 .docs_copy,
5246 "unable to create '{f}/sources.tar': {s}",
5247 .{ docs_path, @errorName(err) },
5248 );
5249 };
5250 defer tar_file.close();
5251
5252 var buffer: [1024]u8 = undefined;
5253 var tar_file_writer = tar_file.writer(&buffer);
5254
5255 var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, []const u8) = .empty;
5256 defer seen_table.deinit(comp.gpa);
5257
5258 try seen_table.put(comp.gpa, zcu.main_mod, comp.root_name);
5259 try seen_table.put(comp.gpa, zcu.std_mod, zcu.std_mod.fully_qualified_name);
5260
5261 var i: usize = 0;
5262 while (i < seen_table.count()) : (i += 1) {
5263 const mod = seen_table.keys()[i];
5264 try comp.docsCopyModule(mod, seen_table.values()[i], &tar_file_writer);
5265
5266 const deps = mod.deps.values();
5267 try seen_table.ensureUnusedCapacity(comp.gpa, deps.len);
5268 for (deps) |dep| seen_table.putAssumeCapacity(dep, dep.fully_qualified_name);
5269 }
5270
5271 tar_file_writer.end() catch |err| {
5272 return comp.lockAndSetMiscFailure(
5273 .docs_copy,
5274 "unable to write '{f}/sources.tar': {t}",
5275 .{ docs_path, err },
5276 );
5277 };
5278}
5279
5280fn docsCopyModule(
5281 comp: *Compilation,
5282 module: *Package.Module,
5283 name: []const u8,
5284 tar_file_writer: *fs.File.Writer,
5285) !void {
5286 const io = comp.io;
5287 const root = module.root;
5288 var mod_dir = d: {
5289 const root_dir, const sub_path = root.openInfo(comp.dirs);
5290 break :d root_dir.openDir(sub_path, .{ .iterate = true });
5291 } catch |err| {
5292 return comp.lockAndSetMiscFailure(.docs_copy, "unable to open directory '{f}': {t}", .{ root.fmt(comp), err });
5293 };
5294 defer mod_dir.close();
5295
5296 var walker = try mod_dir.walk(comp.gpa);
5297 defer walker.deinit();
5298
5299 var archiver: std.tar.Writer = .{ .underlying_writer = &tar_file_writer.interface };
5300 archiver.prefix = name;
5301
5302 var buffer: [1024]u8 = undefined;
5303
5304 while (try walker.next()) |entry| {
5305 switch (entry.kind) {
5306 .file => {
5307 if (!std.mem.endsWith(u8, entry.basename, ".zig")) continue;
5308 if (std.mem.eql(u8, entry.basename, "test.zig")) continue;
5309 if (std.mem.endsWith(u8, entry.basename, "_test.zig")) continue;
5310 },
5311 else => continue,
5312 }
5313 var file = mod_dir.openFile(entry.path, .{}) catch |err| {
5314 return comp.lockAndSetMiscFailure(.docs_copy, "unable to open {f}{s}: {t}", .{
5315 root.fmt(comp), entry.path, err,
5316 });
5317 };
5318 defer file.close();
5319 const stat = try file.stat();
5320 var file_reader: fs.File.Reader = .initSize(file.adaptToNewApi(), io, &buffer, stat.size);
5321
5322 archiver.writeFileTimestamp(entry.path, &file_reader, stat.mtime) catch |err| {
5323 return comp.lockAndSetMiscFailure(.docs_copy, "unable to archive {f}{s}: {t}", .{
5324 root.fmt(comp), entry.path, err,
5325 });
5326 };
5327 }
5328}
5329
5330fn workerDocsWasm(comp: *Compilation, parent_prog_node: std.Progress.Node) void {
5331 const prog_node = parent_prog_node.start("Compile Autodocs", 0);
5332 defer prog_node.end();
5333
5334 workerDocsWasmFallible(comp, prog_node) catch |err| switch (err) {
5335 error.AlreadyReported => return,
5336 else => comp.lockAndSetMiscFailure(.docs_wasm, "unable to build autodocs: {t}", .{err}),
5337 };
5338}
5339
5340fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubUpdateError!void {
5341 const gpa = comp.gpa;
5342 const io = comp.io;
5343
5344 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
5345 defer arena_allocator.deinit();
5346 const arena = arena_allocator.allocator();
5347
5348 const optimize_mode = std.builtin.OptimizeMode.ReleaseSmall;
5349 const output_mode = std.builtin.OutputMode.Exe;
5350 const resolved_target: Package.Module.ResolvedTarget = .{
5351 .result = std.zig.system.resolveTargetQuery(io, .{
5352 .cpu_arch = .wasm32,
5353 .os_tag = .freestanding,
5354 .cpu_features_add = std.Target.wasm.featureSet(&.{
5355 .atomics,
5356 // .extended_const, not supported by Safari
5357 .reference_types,
5358 //.relaxed_simd, not supported by Firefox or Safari
5359 // observed to cause Error occured during wast conversion :
5360 // Unknown operator: 0xfd058 in Firefox 117
5361 //.simd128,
5362 // .tail_call, not supported by Safari
5363 }),
5364 }) catch unreachable,
5365
5366 .is_native_os = false,
5367 .is_native_abi = false,
5368 .is_explicit_dynamic_linker = false,
5369 };
5370
5371 const config = Config.resolve(.{
5372 .output_mode = output_mode,
5373 .resolved_target = resolved_target,
5374 .is_test = false,
5375 .have_zcu = true,
5376 .emit_bin = true,
5377 .root_optimize_mode = optimize_mode,
5378 .link_libc = false,
5379 .rdynamic = true,
5380 }) catch |err| {
5381 comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: failed to resolve compilation config: {t}", .{err});
5382 return error.AlreadyReported;
5383 };
5384
5385 const src_basename = "main.zig";
5386 const root_name = fs.path.stem(src_basename);
5387
5388 const dirs = comp.dirs.withoutLocalCache();
5389
5390 const root_mod = Package.Module.create(arena, .{
5391 .paths = .{
5392 .root = try .fromRoot(arena, dirs, .zig_lib, "docs/wasm"),
5393 .root_src_path = src_basename,
5394 },
5395 .fully_qualified_name = root_name,
5396 .inherited = .{
5397 .resolved_target = resolved_target,
5398 .optimize_mode = optimize_mode,
5399 },
5400 .global = config,
5401 .cc_argv = &.{},
5402 .parent = null,
5403 }) catch |err| {
5404 comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: failed to create root module: {t}", .{err});
5405 return error.AlreadyReported;
5406 };
5407 const walk_mod = Package.Module.create(arena, .{
5408 .paths = .{
5409 .root = try .fromRoot(arena, dirs, .zig_lib, "docs/wasm"),
5410 .root_src_path = "Walk.zig",
5411 },
5412 .fully_qualified_name = "Walk",
5413 .inherited = .{
5414 .resolved_target = resolved_target,
5415 .optimize_mode = optimize_mode,
5416 },
5417 .global = config,
5418 .cc_argv = &.{},
5419 .parent = root_mod,
5420 }) catch |err| {
5421 comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: failed to create 'Walk' module: {t}", .{err});
5422 return error.AlreadyReported;
5423 };
5424 try root_mod.deps.put(arena, "Walk", walk_mod);
5425
5426 var sub_create_diag: CreateDiagnostic = undefined;
5427 const sub_compilation = Compilation.create(gpa, arena, io, &sub_create_diag, .{
5428 .dirs = dirs,
5429 .self_exe_path = comp.self_exe_path,
5430 .config = config,
5431 .root_mod = root_mod,
5432 .entry = .disabled,
5433 .cache_mode = .whole,
5434 .root_name = root_name,
5435 .thread_pool = comp.thread_pool,
5436 .libc_installation = comp.libc_installation,
5437 .emit_bin = .yes_cache,
5438 .verbose_cc = comp.verbose_cc,
5439 .verbose_link = comp.verbose_link,
5440 .verbose_air = comp.verbose_air,
5441 .verbose_intern_pool = comp.verbose_intern_pool,
5442 .verbose_generic_instances = comp.verbose_intern_pool,
5443 .verbose_llvm_ir = comp.verbose_llvm_ir,
5444 .verbose_llvm_bc = comp.verbose_llvm_bc,
5445 .verbose_cimport = comp.verbose_cimport,
5446 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
5447 }) catch |err| switch (err) {
5448 error.CreateFail => {
5449 comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: {f}", .{sub_create_diag});
5450 return error.AlreadyReported;
5451 },
5452 else => |e| return e,
5453 };
5454 defer sub_compilation.destroy();
5455
5456 try comp.updateSubCompilation(sub_compilation, .docs_wasm, prog_node);
5457
5458 var crt_file = try sub_compilation.toCrtFile();
5459 defer crt_file.deinit(gpa);
5460
5461 const docs_bin_file = crt_file.full_object_path;
5462 assert(docs_bin_file.sub_path.len > 0); // emitted binary is not a directory
5463
5464 const docs_path = comp.resolveEmitPath(comp.emit_docs.?);
5465 var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| {
5466 comp.lockAndSetMiscFailure(
5467 .docs_copy,
5468 "unable to create output directory '{f}': {t}",
5469 .{ docs_path, err },
5470 );
5471 return error.AlreadyReported;
5472 };
5473 defer out_dir.close();
5474
5475 crt_file.full_object_path.root_dir.handle.copyFile(
5476 crt_file.full_object_path.sub_path,
5477 out_dir,
5478 "main.wasm",
5479 .{},
5480 ) catch |err| {
5481 comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{f}' to '{f}': {t}", .{
5482 crt_file.full_object_path, docs_path, err,
5483 });
5484 return error.AlreadyReported;
5485 };
5486}
5487
5488fn workerUpdateFile(
5489 tid: usize,
5490 comp: *Compilation,
5491 file: *Zcu.File,
5492 file_index: Zcu.File.Index,
5493 prog_node: std.Progress.Node,
5494 wg: *WaitGroup,
5495) void {
5496 const child_prog_node = prog_node.start(fs.path.basename(file.path.sub_path), 0);
5497 defer child_prog_node.end();
5498
5499 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
5500 defer pt.deactivate();
5501 pt.updateFile(file_index, file) catch |err| {
5502 pt.reportRetryableFileError(file_index, "unable to load '{s}': {s}", .{ fs.path.basename(file.path.sub_path), @errorName(err) }) catch |oom| switch (oom) {
5503 error.OutOfMemory => {
5504 comp.mutex.lock();
5505 defer comp.mutex.unlock();
5506 comp.setAllocFailure();
5507 },
5508 };
5509 return;
5510 };
5511
5512 switch (file.getMode()) {
5513 .zig => {}, // continue to logic below
5514 .zon => return, // ZON can't import anything so we're done
5515 }
5516
5517 // Discover all imports in the file. Imports of modules we ignore for now since we don't
5518 // know which module we're in, but imports of file paths might need us to queue up other
5519 // AstGen jobs.
5520 const imports_index = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)];
5521 if (imports_index != 0) {
5522 const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_index);
5523 var import_i: u32 = 0;
5524 var extra_index = extra.end;
5525
5526 while (import_i < extra.data.imports_len) : (import_i += 1) {
5527 const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index);
5528 extra_index = item.end;
5529
5530 const import_path = file.zir.?.nullTerminatedString(item.data.name);
5531
5532 if (pt.discoverImport(file.path, import_path)) |res| switch (res) {
5533 .module, .existing_file => {},
5534 .new_file => |new| {
5535 comp.thread_pool.spawnWgId(wg, workerUpdateFile, .{
5536 comp, new.file, new.index, prog_node, wg,
5537 });
5538 },
5539 } else |err| switch (err) {
5540 error.OutOfMemory => {
5541 comp.mutex.lock();
5542 defer comp.mutex.unlock();
5543 comp.setAllocFailure();
5544 },
5545 }
5546 }
5547 }
5548}
5549
5550fn workerUpdateBuiltinFile(comp: *Compilation, file: *Zcu.File) void {
5551 Builtin.updateFileOnDisk(file, comp) catch |err| comp.lockAndSetMiscFailure(
5552 .write_builtin_zig,
5553 "unable to write '{f}': {s}",
5554 .{ file.path.fmt(comp), @errorName(err) },
5555 );
5556}
5557
5558fn workerUpdateEmbedFile(tid: usize, comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void {
5559 comp.detectEmbedFileUpdate(@enumFromInt(tid), ef_index, ef) catch |err| switch (err) {
5560 error.OutOfMemory => {
5561 comp.mutex.lock();
5562 defer comp.mutex.unlock();
5563 comp.setAllocFailure();
5564 },
5565 };
5566}
5567
5568fn detectEmbedFileUpdate(comp: *Compilation, tid: Zcu.PerThread.Id, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) !void {
5569 const zcu = comp.zcu.?;
5570 const pt: Zcu.PerThread = .activate(zcu, tid);
5571 defer pt.deactivate();
5572
5573 const old_val = ef.val;
5574 const old_err = ef.err;
5575
5576 try pt.updateEmbedFile(ef, null);
5577
5578 if (ef.val != .none and ef.val == old_val) return; // success, value unchanged
5579 if (ef.val == .none and old_val == .none and ef.err == old_err) return; // failure, error unchanged
5580
5581 comp.mutex.lock();
5582 defer comp.mutex.unlock();
5583
5584 try zcu.markDependeeOutdated(.not_marked_po, .{ .embed_file = ef_index });
5585}
5586
5587pub fn obtainCObjectCacheManifest(
5588 comp: *const Compilation,
5589 owner_mod: *Package.Module,
5590) Cache.Manifest {
5591 var man = comp.cache_parent.obtain();
5592
5593 // Only things that need to be added on top of the base hash, and only things
5594 // that apply both to @cImport and compiling C objects. No linking stuff here!
5595 // Also nothing that applies only to compiling .zig code.
5596 cache_helpers.addModule(&man.hash, owner_mod);
5597 man.hash.addListOfBytes(comp.global_cc_argv);
5598 man.hash.add(comp.config.link_libcpp);
5599
5600 // When libc_installation is null it means that Zig generated this dir list
5601 // based on the zig library directory alone. The zig lib directory file
5602 // path is purposefully either in the cache or not in the cache. The
5603 // decision should not be overridden here.
5604 if (comp.libc_installation != null) {
5605 man.hash.addListOfBytes(comp.libc_include_dir_list);
5606 }
5607
5608 return man;
5609}
5610
5611pub fn obtainWin32ResourceCacheManifest(comp: *const Compilation) Cache.Manifest {
5612 var man = comp.cache_parent.obtain();
5613
5614 man.hash.add(comp.rc_includes);
5615
5616 return man;
5617}
5618
5619pub const CImportResult = struct {
5620 // Only valid if `errors` is not empty
5621 digest: [Cache.bin_digest_len]u8,
5622 cache_hit: bool,
5623 errors: std.zig.ErrorBundle,
5624
5625 pub fn deinit(result: *CImportResult, gpa: mem.Allocator) void {
5626 result.errors.deinit(gpa);
5627 }
5628};
5629
5630pub fn translateC(
5631 comp: *Compilation,
5632 arena: Allocator,
5633 man: *Cache.Manifest,
5634 ext: FileExt,
5635 source: union(enum) {
5636 path: []const u8,
5637 c_src: []const u8,
5638 },
5639 translated_basename: []const u8,
5640 owner_mod: *Package.Module,
5641 prog_node: std.Progress.Node,
5642) !CImportResult {
5643 dev.check(.translate_c_command);
5644
5645 const gpa = comp.gpa;
5646 const io = comp.io;
5647 const tmp_basename = std.fmt.hex(std.crypto.random.int(u64));
5648 const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename;
5649 const cache_dir = comp.dirs.local_cache.handle;
5650 var cache_tmp_dir = try cache_dir.makeOpenPath(tmp_sub_path, .{});
5651 defer cache_tmp_dir.close();
5652
5653 const translated_path = try comp.dirs.local_cache.join(arena, &.{ tmp_sub_path, translated_basename });
5654 const source_path = switch (source) {
5655 .c_src => |c_src| path: {
5656 const cimport_basename = "cimport.h";
5657 const out_h_sub_path = tmp_sub_path ++ fs.path.sep_str ++ cimport_basename;
5658 const out_h_path = try comp.dirs.local_cache.join(arena, &.{out_h_sub_path});
5659 if (comp.verbose_cimport) log.info("writing C import source to {s}", .{out_h_path});
5660 try cache_dir.writeFile(.{ .sub_path = out_h_sub_path, .data = c_src });
5661 break :path out_h_path;
5662 },
5663 .path => |p| p,
5664 };
5665
5666 const out_dep_path: ?[]const u8 = blk: {
5667 if (comp.disable_c_depfile) break :blk null;
5668 const c_src_basename = fs.path.basename(source_path);
5669 const dep_basename = try std.fmt.allocPrint(arena, "{s}.d", .{c_src_basename});
5670 const out_dep_path = try comp.dirs.local_cache.join(arena, &.{ tmp_sub_path, dep_basename });
5671 break :blk out_dep_path;
5672 };
5673
5674 var argv = std.array_list.Managed([]const u8).init(arena);
5675 {
5676 const target = &owner_mod.resolved_target.result;
5677 try argv.appendSlice(&.{ "--zig-integration", "-x", "c" });
5678
5679 const resource_path = try comp.dirs.zig_lib.join(arena, &.{ "compiler", "aro", "include" });
5680 try argv.appendSlice(&.{ "-isystem", resource_path });
5681 try comp.addCommonCCArgs(arena, &argv, ext, out_dep_path, owner_mod, .aro);
5682 try argv.appendSlice(&[_][]const u8{ "-target", try target.zigTriple(arena) });
5683
5684 const mcpu = mcpu: {
5685 var buf: std.ArrayList(u8) = .empty;
5686 defer buf.deinit(gpa);
5687
5688 try buf.print(gpa, "-mcpu={s}", .{target.cpu.model.name});
5689
5690 // TODO better serialization https://github.com/ziglang/zig/issues/4584
5691 const all_features_list = target.cpu.arch.allFeaturesList();
5692 try argv.ensureUnusedCapacity(all_features_list.len * 4);
5693 for (all_features_list, 0..) |feature, index_usize| {
5694 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
5695 const is_enabled = target.cpu.features.isEnabled(index);
5696
5697 const plus_or_minus = "-+"[@intFromBool(is_enabled)];
5698 try buf.print(gpa, "{c}{s}", .{ plus_or_minus, feature.name });
5699 }
5700 break :mcpu try arena.dupe(u8, buf.items);
5701 };
5702 try argv.append(mcpu);
5703
5704 try argv.appendSlice(comp.global_cc_argv);
5705 try argv.appendSlice(owner_mod.cc_argv);
5706 try argv.appendSlice(&.{ source_path, "-o", translated_path });
5707 if (comp.verbose_cimport) dump_argv(argv.items);
5708 }
5709
5710 var stdout: []u8 = undefined;
5711 try @import("main.zig").translateC(gpa, arena, io, argv.items, prog_node, &stdout);
5712
5713 if (out_dep_path) |dep_file_path| add_deps: {
5714 if (comp.verbose_cimport) log.info("processing dep file at {s}", .{dep_file_path});
5715
5716 const dep_basename = fs.path.basename(dep_file_path);
5717 // Add the files depended on to the cache system, if a dep file was emitted
5718 man.addDepFilePost(cache_tmp_dir, dep_basename) catch |err| switch (err) {
5719 error.FileNotFound => break :add_deps,
5720 else => |e| return e,
5721 };
5722
5723 switch (comp.cache_use) {
5724 .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| {
5725 whole.cache_manifest_mutex.lock();
5726 defer whole.cache_manifest_mutex.unlock();
5727 try whole_cache_manifest.addDepFilePost(cache_tmp_dir, dep_basename);
5728 },
5729 .incremental, .none => {},
5730 }
5731
5732 // Just to save disk space, we delete the file because it is never needed again.
5733 cache_tmp_dir.deleteFile(dep_basename) catch |err| {
5734 log.warn("failed to delete '{s}': {t}", .{ dep_file_path, err });
5735 };
5736 }
5737
5738 if (stdout.len > 0) {
5739 var reader: std.Io.Reader = .fixed(stdout);
5740 const MessageHeader = std.zig.Server.Message.Header;
5741 const header = reader.takeStruct(MessageHeader, .little) catch |err|
5742 fatal("unable to read translate-c MessageHeader: {s}", .{@errorName(err)});
5743 const body = reader.take(header.bytes_len) catch |err|
5744 fatal("unable to read {}-byte translate-c message body: {s}", .{ header.bytes_len, @errorName(err) });
5745 switch (header.tag) {
5746 .error_bundle => {
5747 const error_bundle = try std.zig.Server.allocErrorBundle(gpa, body);
5748 return .{
5749 .digest = undefined,
5750 .cache_hit = false,
5751 .errors = error_bundle,
5752 };
5753 },
5754 else => fatal("unexpected message type received from translate-c: {s}", .{@tagName(header.tag)}),
5755 }
5756 }
5757
5758 const bin_digest = man.finalBin();
5759 const hex_digest = Cache.binToHex(bin_digest);
5760 const o_sub_path = "o" ++ fs.path.sep_str ++ hex_digest;
5761
5762 if (comp.verbose_cimport) log.info("renaming {s} to {s}", .{ tmp_sub_path, o_sub_path });
5763 try renameTmpIntoCache(comp.dirs.local_cache, tmp_sub_path, o_sub_path);
5764
5765 return .{
5766 .digest = bin_digest,
5767 .cache_hit = false,
5768 .errors = ErrorBundle.empty,
5769 };
5770}
5771
5772/// Caller owns returned memory.
5773pub fn cImport(
5774 comp: *Compilation,
5775 c_src: []const u8,
5776 owner_mod: *Package.Module,
5777 prog_node: std.Progress.Node,
5778) !CImportResult {
5779 dev.check(.translate_c_command);
5780
5781 const translated_basename = "cimport.zig";
5782
5783 var man = comp.obtainCObjectCacheManifest(owner_mod);
5784 defer man.deinit();
5785
5786 man.hash.add(@as(u16, 0x7dd9)); // Random number to distinguish c-import from compiling C objects
5787 man.hash.addBytes(c_src);
5788
5789 const result: CImportResult = if (try man.hit()) .{
5790 .digest = man.finalBin(),
5791 .cache_hit = true,
5792 .errors = ErrorBundle.empty,
5793 } else result: {
5794 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
5795 defer arena_allocator.deinit();
5796 const arena = arena_allocator.allocator();
5797
5798 break :result try comp.translateC(
5799 arena,
5800 &man,
5801 .c,
5802 .{ .c_src = c_src },
5803 translated_basename,
5804 owner_mod,
5805 prog_node,
5806 );
5807 };
5808
5809 if (result.errors.errorMessageCount() == 0 and man.have_exclusive_lock) {
5810 // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
5811 // possible we had a hit and the manifest is dirty, for example if the file mtime changed but
5812 // the contents were the same, we hit the cache but the manifest is dirty and we need to update
5813 // it to prevent doing a full file content comparison the next time around.
5814 man.writeManifest() catch |err| {
5815 log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)});
5816 };
5817 }
5818
5819 return result;
5820}
5821
5822fn workerUpdateCObject(
5823 comp: *Compilation,
5824 c_object: *CObject,
5825 progress_node: std.Progress.Node,
5826) void {
5827 defer comp.link_task_queue.finishPrelinkItem(comp);
5828 comp.updateCObject(c_object, progress_node) catch |err| switch (err) {
5829 error.AnalysisFail => return,
5830 else => {
5831 comp.reportRetryableCObjectError(c_object, err) catch |oom| switch (oom) {
5832 // Swallowing this error is OK because it's implied to be OOM when
5833 // there is a missing failed_c_objects error message.
5834 error.OutOfMemory => {},
5835 };
5836 },
5837 };
5838}
5839
5840fn workerUpdateWin32Resource(
5841 comp: *Compilation,
5842 win32_resource: *Win32Resource,
5843 progress_node: std.Progress.Node,
5844) void {
5845 defer comp.link_task_queue.finishPrelinkItem(comp);
5846 comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) {
5847 error.AnalysisFail => return,
5848 else => {
5849 comp.reportRetryableWin32ResourceError(win32_resource, err) catch |oom| switch (oom) {
5850 // Swallowing this error is OK because it's implied to be OOM when
5851 // there is a missing failed_win32_resources error message.
5852 error.OutOfMemory => {},
5853 };
5854 },
5855 };
5856}
5857
5858pub const RtOptions = struct {
5859 checks_valgrind: bool = false,
5860 allow_lto: bool = true,
5861};
5862
5863fn workerZcuCodegen(
5864 tid: usize,
5865 comp: *Compilation,
5866 func_index: InternPool.Index,
5867 orig_air: Air,
5868 out: *link.ZcuTask.LinkFunc.SharedMir,
5869) void {
5870 var air = orig_air;
5871 // We own `air` now, so we are responsbile for freeing it.
5872 defer air.deinit(comp.gpa);
5873 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
5874 defer pt.deactivate();
5875 pt.runCodegen(func_index, &air, out);
5876}
5877
5878fn buildRt(
5879 comp: *Compilation,
5880 root_source_name: []const u8,
5881 root_name: []const u8,
5882 output_mode: std.builtin.OutputMode,
5883 link_mode: std.builtin.LinkMode,
5884 misc_task: MiscTask,
5885 prog_node: std.Progress.Node,
5886 options: RtOptions,
5887 out: *?CrtFile,
5888) void {
5889 defer comp.link_task_queue.finishPrelinkItem(comp);
5890 comp.buildOutputFromZig(
5891 root_source_name,
5892 root_name,
5893 output_mode,
5894 link_mode,
5895 misc_task,
5896 prog_node,
5897 options,
5898 out,
5899 ) catch |err| switch (err) {
5900 error.AlreadyReported => return,
5901 else => comp.lockAndSetMiscFailure(misc_task, "unable to build {s}: {s}", .{
5902 @tagName(misc_task), @errorName(err),
5903 }),
5904 };
5905}
5906
5907fn buildMuslCrtFile(comp: *Compilation, crt_file: musl.CrtFile, prog_node: std.Progress.Node) void {
5908 defer comp.link_task_queue.finishPrelinkItem(comp);
5909 if (musl.buildCrtFile(comp, crt_file, prog_node)) |_| {
5910 comp.queued_jobs.musl_crt_file[@intFromEnum(crt_file)] = false;
5911 } else |err| switch (err) {
5912 error.AlreadyReported => return,
5913 else => comp.lockAndSetMiscFailure(.musl_crt_file, "unable to build musl {s}: {s}", .{
5914 @tagName(crt_file), @errorName(err),
5915 }),
5916 }
5917}
5918
5919fn buildGlibcCrtFile(comp: *Compilation, crt_file: glibc.CrtFile, prog_node: std.Progress.Node) void {
5920 defer comp.link_task_queue.finishPrelinkItem(comp);
5921 if (glibc.buildCrtFile(comp, crt_file, prog_node)) |_| {
5922 comp.queued_jobs.glibc_crt_file[@intFromEnum(crt_file)] = false;
5923 } else |err| switch (err) {
5924 error.AlreadyReported => return,
5925 else => comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc {s}: {s}", .{
5926 @tagName(crt_file), @errorName(err),
5927 }),
5928 }
5929}
5930
5931fn buildGlibcSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
5932 defer comp.link_task_queue.finishPrelinkItem(comp);
5933 if (glibc.buildSharedObjects(comp, prog_node)) |_| {
5934 // The job should no longer be queued up since it succeeded.
5935 comp.queued_jobs.glibc_shared_objects = false;
5936 } else |err| switch (err) {
5937 error.AlreadyReported => return,
5938 else => comp.lockAndSetMiscFailure(.glibc_shared_objects, "unable to build glibc shared objects: {t}", .{err}),
5939 }
5940}
5941
5942fn buildFreeBSDCrtFile(comp: *Compilation, crt_file: freebsd.CrtFile, prog_node: std.Progress.Node) void {
5943 defer comp.link_task_queue.finishPrelinkItem(comp);
5944 if (freebsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
5945 comp.queued_jobs.freebsd_crt_file[@intFromEnum(crt_file)] = false;
5946 } else |err| switch (err) {
5947 error.AlreadyReported => return,
5948 else => comp.lockAndSetMiscFailure(.freebsd_crt_file, "unable to build FreeBSD {s}: {s}", .{
5949 @tagName(crt_file), @errorName(err),
5950 }),
5951 }
5952}
5953
5954fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
5955 defer comp.link_task_queue.finishPrelinkItem(comp);
5956 if (freebsd.buildSharedObjects(comp, prog_node)) |_| {
5957 // The job should no longer be queued up since it succeeded.
5958 comp.queued_jobs.freebsd_shared_objects = false;
5959 } else |err| switch (err) {
5960 error.AlreadyReported => return,
5961 else => comp.lockAndSetMiscFailure(.freebsd_shared_objects, "unable to build FreeBSD libc shared objects: {s}", .{
5962 @errorName(err),
5963 }),
5964 }
5965}
5966
5967fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: std.Progress.Node) void {
5968 defer comp.link_task_queue.finishPrelinkItem(comp);
5969 if (netbsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
5970 comp.queued_jobs.netbsd_crt_file[@intFromEnum(crt_file)] = false;
5971 } else |err| switch (err) {
5972 error.AlreadyReported => return,
5973 else => comp.lockAndSetMiscFailure(.netbsd_crt_file, "unable to build NetBSD {s}: {s}", .{
5974 @tagName(crt_file), @errorName(err),
5975 }),
5976 }
5977}
5978
5979fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
5980 defer comp.link_task_queue.finishPrelinkItem(comp);
5981 if (netbsd.buildSharedObjects(comp, prog_node)) |_| {
5982 // The job should no longer be queued up since it succeeded.
5983 comp.queued_jobs.netbsd_shared_objects = false;
5984 } else |err| switch (err) {
5985 error.AlreadyReported => return,
5986 else => comp.lockAndSetMiscFailure(.netbsd_shared_objects, "unable to build NetBSD libc shared objects: {s}", .{
5987 @errorName(err),
5988 }),
5989 }
5990}
5991
5992fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std.Progress.Node) void {
5993 defer comp.link_task_queue.finishPrelinkItem(comp);
5994 if (mingw.buildCrtFile(comp, crt_file, prog_node)) |_| {
5995 comp.queued_jobs.mingw_crt_file[@intFromEnum(crt_file)] = false;
5996 } else |err| switch (err) {
5997 error.AlreadyReported => return,
5998 else => comp.lockAndSetMiscFailure(.mingw_crt_file, "unable to build mingw-w64 {s}: {s}", .{
5999 @tagName(crt_file), @errorName(err),
6000 }),
6001 }
6002}
6003
6004fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_node: std.Progress.Node) void {
6005 defer comp.link_task_queue.finishPrelinkItem(comp);
6006 if (wasi_libc.buildCrtFile(comp, crt_file, prog_node)) |_| {
6007 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = false;
6008 } else |err| switch (err) {
6009 error.AlreadyReported => return,
6010 else => comp.lockAndSetMiscFailure(.wasi_libc_crt_file, "unable to build WASI libc {s}: {s}", .{
6011 @tagName(crt_file), @errorName(err),
6012 }),
6013 }
6014}
6015
6016fn buildLibUnwind(comp: *Compilation, prog_node: std.Progress.Node) void {
6017 defer comp.link_task_queue.finishPrelinkItem(comp);
6018 if (libunwind.buildStaticLib(comp, prog_node)) |_| {
6019 comp.queued_jobs.libunwind = false;
6020 } else |err| switch (err) {
6021 error.AlreadyReported => return,
6022 else => comp.lockAndSetMiscFailure(.libunwind, "unable to build libunwind: {s}", .{@errorName(err)}),
6023 }
6024}
6025
6026fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) void {
6027 defer comp.link_task_queue.finishPrelinkItem(comp);
6028 if (libcxx.buildLibCxx(comp, prog_node)) |_| {
6029 comp.queued_jobs.libcxx = false;
6030 } else |err| switch (err) {
6031 error.AlreadyReported => return,
6032 else => comp.lockAndSetMiscFailure(.libcxx, "unable to build libcxx: {s}", .{@errorName(err)}),
6033 }
6034}
6035
6036fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) void {
6037 defer comp.link_task_queue.finishPrelinkItem(comp);
6038 if (libcxx.buildLibCxxAbi(comp, prog_node)) |_| {
6039 comp.queued_jobs.libcxxabi = false;
6040 } else |err| switch (err) {
6041 error.AlreadyReported => return,
6042 else => comp.lockAndSetMiscFailure(.libcxxabi, "unable to build libcxxabi: {s}", .{@errorName(err)}),
6043 }
6044}
6045
6046fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void {
6047 defer comp.link_task_queue.finishPrelinkItem(comp);
6048 if (libtsan.buildTsan(comp, prog_node)) |_| {
6049 comp.queued_jobs.libtsan = false;
6050 } else |err| switch (err) {
6051 error.AlreadyReported => return,
6052 else => comp.lockAndSetMiscFailure(.libtsan, "unable to build TSAN library: {s}", .{@errorName(err)}),
6053 }
6054}
6055
6056fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void {
6057 defer comp.link_task_queue.finishPrelinkItem(comp);
6058 comp.buildOutputFromZig(
6059 "c.zig",
6060 "zigc",
6061 .Lib,
6062 .static,
6063 .libzigc,
6064 prog_node,
6065 .{},
6066 &comp.zigc_static_lib,
6067 ) catch |err| switch (err) {
6068 error.AlreadyReported => return,
6069 else => comp.lockAndSetMiscFailure(.libzigc, "unable to build libzigc: {s}", .{@errorName(err)}),
6070 };
6071}
6072
6073fn reportRetryableCObjectError(comp: *Compilation, c_object: *CObject, err: anyerror) error{OutOfMemory}!void {
6074 c_object.status = .failure_retryable;
6075
6076 switch (comp.failCObj(c_object, "{t}", .{err})) {
6077 error.AnalysisFail => return,
6078 else => |e| return e,
6079 }
6080}
6081
6082fn reportRetryableWin32ResourceError(
6083 comp: *Compilation,
6084 win32_resource: *Win32Resource,
6085 err: anyerror,
6086) error{OutOfMemory}!void {
6087 win32_resource.status = .failure_retryable;
6088
6089 var bundle: ErrorBundle.Wip = undefined;
6090 try bundle.init(comp.gpa);
6091 errdefer bundle.deinit();
6092 try bundle.addRootErrorMessage(.{
6093 .msg = try bundle.printString("{s}", .{@errorName(err)}),
6094 .src_loc = try bundle.addSourceLocation(.{
6095 .src_path = try bundle.addString(switch (win32_resource.src) {
6096 .rc => |rc_src| rc_src.src_path,
6097 .manifest => |manifest_src| manifest_src,
6098 }),
6099 .line = 0,
6100 .column = 0,
6101 .span_start = 0,
6102 .span_main = 0,
6103 .span_end = 0,
6104 }),
6105 });
6106 const finished_bundle = try bundle.toOwnedBundle("");
6107 {
6108 comp.mutex.lock();
6109 defer comp.mutex.unlock();
6110 try comp.failed_win32_resources.putNoClobber(comp.gpa, win32_resource, finished_bundle);
6111 }
6112}
6113
6114fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Progress.Node) !void {
6115 if (comp.config.c_frontend == .aro) {
6116 return comp.failCObj(c_object, "aro does not support compiling C objects yet", .{});
6117 }
6118 if (!build_options.have_llvm) {
6119 return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{});
6120 }
6121 const self_exe_path = comp.self_exe_path orelse
6122 return comp.failCObj(c_object, "clang compilation disabled", .{});
6123
6124 const tracy_trace = trace(@src());
6125 defer tracy_trace.end();
6126
6127 log.debug("updating C object: {s}", .{c_object.src.src_path});
6128
6129 const gpa = comp.gpa;
6130 const io = comp.io;
6131
6132 if (c_object.clearStatus(gpa)) {
6133 // There was previous failure.
6134 comp.mutex.lock();
6135 defer comp.mutex.unlock();
6136 // If the failure was OOM, there will not be an entry here, so we do
6137 // not assert discard.
6138 _ = comp.failed_c_objects.swapRemove(c_object);
6139 }
6140
6141 var man = comp.obtainCObjectCacheManifest(c_object.src.owner);
6142 defer man.deinit();
6143
6144 man.hash.add(comp.clang_preprocessor_mode);
6145 man.hash.addOptionalBytes(comp.emit_asm);
6146 man.hash.addOptionalBytes(comp.emit_llvm_ir);
6147 man.hash.addOptionalBytes(comp.emit_llvm_bc);
6148
6149 try cache_helpers.hashCSource(&man, c_object.src);
6150
6151 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
6152 defer arena_allocator.deinit();
6153 const arena = arena_allocator.allocator();
6154
6155 const c_source_basename = fs.path.basename(c_object.src.src_path);
6156
6157 const child_progress_node = c_obj_prog_node.start(c_source_basename, 0);
6158 defer child_progress_node.end();
6159
6160 // Special case when doing build-obj for just one C file. When there are more than one object
6161 // file and building an object we need to link them together, but with just one it should go
6162 // directly to the output file.
6163 const direct_o = comp.c_source_files.len == 1 and comp.zcu == null and
6164 comp.config.output_mode == .Obj and !link.anyObjectInputs(comp.link_inputs);
6165 const o_basename_noext = if (direct_o)
6166 comp.root_name
6167 else
6168 c_source_basename[0 .. c_source_basename.len - fs.path.extension(c_source_basename).len];
6169
6170 const target = comp.getTarget();
6171 const o_ext = target.ofmt.fileExt(target.cpu.arch);
6172 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
6173 var argv = std.array_list.Managed([]const u8).init(gpa);
6174 defer argv.deinit();
6175
6176 // In case we are doing passthrough mode, we need to detect -S and -emit-llvm.
6177 const out_ext = e: {
6178 if (!comp.clang_passthrough_mode)
6179 break :e o_ext;
6180 if (comp.emit_asm != null)
6181 break :e ".s";
6182 if (comp.emit_llvm_ir != null)
6183 break :e ".ll";
6184 if (comp.emit_llvm_bc != null)
6185 break :e ".bc";
6186
6187 break :e o_ext;
6188 };
6189 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext });
6190 const ext = c_object.src.ext orelse classifyFileExt(c_object.src.src_path);
6191
6192 try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" });
6193 // if "ext" is explicit, add "-x <lang>". Otherwise let clang do its thing.
6194 if (c_object.src.ext != null or ext.clangNeedsLanguageOverride()) {
6195 try argv.appendSlice(&[_][]const u8{ "-x", switch (ext) {
6196 .assembly => "assembler",
6197 .assembly_with_cpp => "assembler-with-cpp",
6198 .c => "c",
6199 .h => "c-header",
6200 .cpp => "c++",
6201 .hpp => "c++-header",
6202 .m => "objective-c",
6203 .hm => "objective-c-header",
6204 .mm => "objective-c++",
6205 .hmm => "objective-c++-header",
6206 else => fatal("language '{s}' is unsupported in this context", .{@tagName(ext)}),
6207 } });
6208 }
6209 try argv.append(c_object.src.src_path);
6210
6211 // When all these flags are true, it means that the entire purpose of
6212 // this compilation is to perform a single zig cc operation. This means
6213 // that we could "tail call" clang by doing an execve, and any use of
6214 // the caching system would actually be problematic since the user is
6215 // presumably doing their own caching by using dep file flags.
6216 if (std.process.can_execv and direct_o and
6217 comp.disable_c_depfile and comp.clang_passthrough_mode)
6218 {
6219 try comp.addCCArgs(arena, &argv, ext, null, c_object.src.owner);
6220 try argv.appendSlice(c_object.src.extra_flags);
6221 try argv.appendSlice(c_object.src.cache_exempt_flags);
6222
6223 const out_obj_path = if (comp.bin_file) |lf|
6224 try lf.emit.root_dir.join(arena, &.{lf.emit.sub_path})
6225 else
6226 "/dev/null";
6227
6228 try argv.ensureUnusedCapacity(6);
6229 switch (comp.clang_preprocessor_mode) {
6230 .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
6231 .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
6232 .pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
6233 .stdout => argv.appendAssumeCapacity("-E"),
6234 }
6235
6236 if (comp.emit_asm != null) {
6237 argv.appendAssumeCapacity("-S");
6238 } else if (comp.emit_llvm_ir != null) {
6239 argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" });
6240 } else if (comp.emit_llvm_bc != null) {
6241 argv.appendAssumeCapacity("-emit-llvm");
6242 }
6243
6244 if (comp.verbose_cc) {
6245 dump_argv(argv.items);
6246 }
6247
6248 const err = std.process.execv(arena, argv.items);
6249 fatal("unable to execv clang: {s}", .{@errorName(err)});
6250 }
6251
6252 // We can't know the digest until we do the C compiler invocation,
6253 // so we need a temporary filename.
6254 const out_obj_path = try comp.tmpFilePath(arena, o_basename);
6255 var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.makeOpenPath("tmp", .{});
6256 defer zig_cache_tmp_dir.close();
6257
6258 const out_diag_path = if (comp.clang_passthrough_mode or !ext.clangSupportsDiagnostics())
6259 null
6260 else
6261 try std.fmt.allocPrint(arena, "{s}.diag", .{out_obj_path});
6262 const out_dep_path = if (comp.disable_c_depfile or !ext.clangSupportsDepFile())
6263 null
6264 else
6265 try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});
6266
6267 try comp.addCCArgs(arena, &argv, ext, out_dep_path, c_object.src.owner);
6268 try argv.appendSlice(c_object.src.extra_flags);
6269 try argv.appendSlice(c_object.src.cache_exempt_flags);
6270
6271 try argv.ensureUnusedCapacity(6);
6272 switch (comp.clang_preprocessor_mode) {
6273 .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
6274 .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
6275 .pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
6276 .stdout => argv.appendAssumeCapacity("-E"),
6277 }
6278 if (out_diag_path) |diag_file_path| {
6279 argv.appendSliceAssumeCapacity(&.{ "--serialize-diagnostics", diag_file_path });
6280 } else if (comp.clang_passthrough_mode) {
6281 if (comp.emit_asm != null) {
6282 argv.appendAssumeCapacity("-S");
6283 } else if (comp.emit_llvm_ir != null) {
6284 argv.appendSliceAssumeCapacity(&.{ "-emit-llvm", "-S" });
6285 } else if (comp.emit_llvm_bc != null) {
6286 argv.appendAssumeCapacity("-emit-llvm");
6287 }
6288 }
6289
6290 if (comp.verbose_cc) {
6291 dump_argv(argv.items);
6292 }
6293
6294 // Just to save disk space, we delete the files that are never needed again.
6295 defer if (out_diag_path) |diag_file_path| zig_cache_tmp_dir.deleteFile(fs.path.basename(diag_file_path)) catch |err| switch (err) {
6296 error.FileNotFound => {}, // the file wasn't created due to an error we reported
6297 else => log.warn("failed to delete '{s}': {s}", .{ diag_file_path, @errorName(err) }),
6298 };
6299 defer if (out_dep_path) |dep_file_path| zig_cache_tmp_dir.deleteFile(fs.path.basename(dep_file_path)) catch |err| switch (err) {
6300 error.FileNotFound => {}, // the file wasn't created due to an error we reported
6301 else => log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }),
6302 };
6303 if (std.process.can_spawn) {
6304 var child = std.process.Child.init(argv.items, arena);
6305 if (comp.clang_passthrough_mode) {
6306 child.stdin_behavior = .Inherit;
6307 child.stdout_behavior = .Inherit;
6308 child.stderr_behavior = .Inherit;
6309
6310 const term = child.spawnAndWait() catch |err| {
6311 return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {s}", .{ argv.items[0], @errorName(err) });
6312 };
6313 switch (term) {
6314 .Exited => |code| {
6315 if (code != 0) {
6316 std.process.exit(code);
6317 }
6318 if (comp.clang_preprocessor_mode == .stdout)
6319 std.process.exit(0);
6320 },
6321 else => std.process.abort(),
6322 }
6323 } else {
6324 child.stdin_behavior = .Ignore;
6325 child.stdout_behavior = .Ignore;
6326 child.stderr_behavior = .Pipe;
6327
6328 try child.spawn();
6329
6330 var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
6331 const stderr = try stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32)));
6332
6333 const term = child.wait() catch |err| {
6334 return comp.failCObj(c_object, "failed to spawn zig clang {s}: {s}", .{ argv.items[0], @errorName(err) });
6335 };
6336
6337 switch (term) {
6338 .Exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| {
6339 const bundle = CObject.Diag.Bundle.parse(gpa, io, diag_file_path) catch |err| {
6340 log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr });
6341 return comp.failCObj(c_object, "clang exited with code {d}", .{code});
6342 };
6343 return comp.failCObjWithOwnedDiagBundle(c_object, bundle);
6344 } else {
6345 log.err("clang failed with stderr: {s}", .{stderr});
6346 return comp.failCObj(c_object, "clang exited with code {d}", .{code});
6347 },
6348 else => {
6349 log.err("clang terminated with stderr: {s}", .{stderr});
6350 return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
6351 },
6352 }
6353 }
6354 } else {
6355 const exit_code = try clangMain(arena, argv.items);
6356 if (exit_code != 0) {
6357 if (comp.clang_passthrough_mode) {
6358 std.process.exit(exit_code);
6359 } else {
6360 return comp.failCObj(c_object, "clang exited with code {d}", .{exit_code});
6361 }
6362 }
6363 if (comp.clang_passthrough_mode and
6364 comp.clang_preprocessor_mode == .stdout)
6365 {
6366 std.process.exit(0);
6367 }
6368 }
6369
6370 if (out_dep_path) |dep_file_path| {
6371 const dep_basename = fs.path.basename(dep_file_path);
6372 // Add the files depended on to the cache system.
6373 try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
6374 switch (comp.cache_use) {
6375 .whole => |whole| {
6376 if (whole.cache_manifest) |whole_cache_manifest| {
6377 whole.cache_manifest_mutex.lock();
6378 defer whole.cache_manifest_mutex.unlock();
6379 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
6380 }
6381 },
6382 .incremental, .none => {},
6383 }
6384 }
6385
6386 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
6387 if (comp.disable_c_depfile) _ = try man.hit();
6388
6389 // Rename into place.
6390 const digest = man.final();
6391 const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });
6392 var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{});
6393 defer o_dir.close();
6394 const tmp_basename = fs.path.basename(out_obj_path);
6395 try fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename);
6396 break :blk digest;
6397 };
6398
6399 if (man.have_exclusive_lock) {
6400 // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
6401 // possible we had a hit and the manifest is dirty, for example if the file mtime changed but
6402 // the contents were the same, we hit the cache but the manifest is dirty and we need to update
6403 // it to prevent doing a full file content comparison the next time around.
6404 man.writeManifest() catch |err| {
6405 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{
6406 c_object.src.src_path, @errorName(err),
6407 });
6408 };
6409 }
6410
6411 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext });
6412
6413 c_object.status = .{
6414 .success = .{
6415 .object_path = .{
6416 .root_dir = comp.dirs.local_cache,
6417 .sub_path = try fs.path.join(gpa, &.{ "o", &digest, o_basename }),
6418 },
6419 .lock = man.toOwnedLock(),
6420 },
6421 };
6422
6423 comp.queuePrelinkTasks(&.{.{ .load_object = c_object.status.success.object_path }});
6424}
6425
6426fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void {
6427 if (!std.process.can_spawn) {
6428 return comp.failWin32Resource(win32_resource, "{s} does not support spawning a child process", .{@tagName(builtin.os.tag)});
6429 }
6430
6431 const self_exe_path = comp.self_exe_path orelse
6432 return comp.failWin32Resource(win32_resource, "unable to find self exe path", .{});
6433
6434 const tracy_trace = trace(@src());
6435 defer tracy_trace.end();
6436
6437 const src_path = switch (win32_resource.src) {
6438 .rc => |rc_src| rc_src.src_path,
6439 .manifest => |src_path| src_path,
6440 };
6441 const src_basename = fs.path.basename(src_path);
6442
6443 log.debug("updating win32 resource: {s}", .{src_path});
6444
6445 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
6446 defer arena_allocator.deinit();
6447 const arena = arena_allocator.allocator();
6448
6449 if (win32_resource.clearStatus(comp.gpa)) {
6450 // There was previous failure.
6451 comp.mutex.lock();
6452 defer comp.mutex.unlock();
6453 // If the failure was OOM, there will not be an entry here, so we do
6454 // not assert discard.
6455 _ = comp.failed_win32_resources.swapRemove(win32_resource);
6456 }
6457
6458 const child_progress_node = win32_resource_prog_node.start(src_basename, 0);
6459 defer child_progress_node.end();
6460
6461 var man = comp.obtainWin32ResourceCacheManifest();
6462 defer man.deinit();
6463
6464 // For .manifest files, we ultimately just want to generate a .res with
6465 // the XML data as a RT_MANIFEST resource. This means we can skip preprocessing,
6466 // include paths, CLI options, etc.
6467 if (win32_resource.src == .manifest) {
6468 _ = try man.addFile(src_path, null);
6469
6470 const rc_basename = try std.fmt.allocPrint(arena, "{s}.rc", .{src_basename});
6471 const res_basename = try std.fmt.allocPrint(arena, "{s}.res", .{src_basename});
6472
6473 const digest = if (try man.hit()) man.final() else blk: {
6474 // The digest only depends on the .manifest file, so we can
6475 // get the digest now and write the .res directly to the cache
6476 const digest = man.final();
6477
6478 const o_sub_path = try fs.path.join(arena, &.{ "o", &digest });
6479 var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{});
6480 defer o_dir.close();
6481
6482 const in_rc_path = try comp.dirs.local_cache.join(comp.gpa, &.{
6483 o_sub_path, rc_basename,
6484 });
6485 const out_res_path = try comp.dirs.local_cache.join(comp.gpa, &.{
6486 o_sub_path, res_basename,
6487 });
6488
6489 // In .rc files, a " within a quoted string is escaped as ""
6490 const fmtRcEscape = struct {
6491 fn formatRcEscape(bytes: []const u8, writer: *Writer) Writer.Error!void {
6492 for (bytes) |byte| switch (byte) {
6493 '"' => try writer.writeAll("\"\""),
6494 '\\' => try writer.writeAll("\\\\"),
6495 else => try writer.writeByte(byte),
6496 };
6497 }
6498
6499 pub fn fmtRcEscape(bytes: []const u8) std.fmt.Alt([]const u8, formatRcEscape) {
6500 return .{ .data = bytes };
6501 }
6502 }.fmtRcEscape;
6503
6504 // https://learn.microsoft.com/en-us/windows/win32/sbscs/using-side-by-side-assemblies-as-a-resource
6505 // WinUser.h defines:
6506 // CREATEPROCESS_MANIFEST_RESOURCE_ID to 1, which is the default
6507 // ISOLATIONAWARE_MANIFEST_RESOURCE_ID to 2, which must be used for .dlls
6508 const resource_id: u32 = if (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic) 2 else 1;
6509
6510 // 24 is RT_MANIFEST
6511 const resource_type = 24;
6512
6513 const input = try std.fmt.allocPrint(arena, "{d} {d} \"{f}\"", .{
6514 resource_id, resource_type, fmtRcEscape(src_path),
6515 });
6516
6517 try o_dir.writeFile(.{ .sub_path = rc_basename, .data = input });
6518
6519 var argv = std.array_list.Managed([]const u8).init(comp.gpa);
6520 defer argv.deinit();
6521
6522 try argv.appendSlice(&.{
6523 self_exe_path,
6524 "rc",
6525 "--zig-integration",
6526 "/:target",
6527 @tagName(comp.getTarget().cpu.arch),
6528 "/:no-preprocess",
6529 "/x", // ignore INCLUDE environment variable
6530 "/c65001", // UTF-8 codepage
6531 "/:auto-includes",
6532 "none",
6533 });
6534 try argv.appendSlice(&.{ "--", in_rc_path, out_res_path });
6535
6536 try spawnZigRc(comp, win32_resource, arena, argv.items, child_progress_node);
6537
6538 break :blk digest;
6539 };
6540
6541 if (man.have_exclusive_lock) {
6542 man.writeManifest() catch |err| {
6543 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ src_path, @errorName(err) });
6544 };
6545 }
6546
6547 win32_resource.status = .{
6548 .success = .{
6549 .res_path = try comp.dirs.local_cache.join(comp.gpa, &[_][]const u8{
6550 "o", &digest, res_basename,
6551 }),
6552 .lock = man.toOwnedLock(),
6553 },
6554 };
6555 return;
6556 }
6557
6558 // We now know that we're compiling an .rc file
6559 const rc_src = win32_resource.src.rc;
6560
6561 _ = try man.addFile(rc_src.src_path, null);
6562 man.hash.addListOfBytes(rc_src.extra_flags);
6563
6564 const rc_basename_noext = src_basename[0 .. src_basename.len - fs.path.extension(src_basename).len];
6565
6566 const digest = if (try man.hit()) man.final() else blk: {
6567 var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.makeOpenPath("tmp", .{});
6568 defer zig_cache_tmp_dir.close();
6569
6570 const res_filename = try std.fmt.allocPrint(arena, "{s}.res", .{rc_basename_noext});
6571
6572 // We can't know the digest until we do the compilation,
6573 // so we need a temporary filename.
6574 const out_res_path = try comp.tmpFilePath(arena, res_filename);
6575
6576 var argv = std.array_list.Managed([]const u8).init(comp.gpa);
6577 defer argv.deinit();
6578
6579 const depfile_filename = try std.fmt.allocPrint(arena, "{s}.d.json", .{rc_basename_noext});
6580 const out_dep_path = try comp.tmpFilePath(arena, depfile_filename);
6581 try argv.appendSlice(&.{
6582 self_exe_path,
6583 "rc",
6584 "--zig-integration",
6585 "/:target",
6586 @tagName(comp.getTarget().cpu.arch),
6587 "/:depfile",
6588 out_dep_path,
6589 "/:depfile-fmt",
6590 "json",
6591 "/x", // ignore INCLUDE environment variable
6592 "/:auto-includes",
6593 @tagName(comp.rc_includes),
6594 });
6595 // While these defines are not normally present when calling rc.exe directly,
6596 // them being defined matches the behavior of how MSVC calls rc.exe which is the more
6597 // relevant behavior in this case.
6598 switch (rc_src.owner.optimize_mode) {
6599 .Debug, .ReleaseSafe => {},
6600 .ReleaseFast, .ReleaseSmall => try argv.append("-DNDEBUG"),
6601 }
6602 try argv.appendSlice(rc_src.extra_flags);
6603 try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path });
6604
6605 try spawnZigRc(comp, win32_resource, arena, argv.items, child_progress_node);
6606
6607 // Read depfile and update cache manifest
6608 {
6609 const dep_basename = fs.path.basename(out_dep_path);
6610 const dep_file_contents = try zig_cache_tmp_dir.readFileAlloc(dep_basename, arena, .limited(50 * 1024 * 1024));
6611 defer arena.free(dep_file_contents);
6612
6613 const value = try std.json.parseFromSliceLeaky(std.json.Value, arena, dep_file_contents, .{});
6614 if (value != .array) {
6615 return comp.failWin32Resource(win32_resource, "depfile from zig rc has unexpected format", .{});
6616 }
6617
6618 for (value.array.items) |element| {
6619 if (element != .string) {
6620 return comp.failWin32Resource(win32_resource, "depfile from zig rc has unexpected format", .{});
6621 }
6622 const dep_file_path = element.string;
6623 try man.addFilePost(dep_file_path);
6624 switch (comp.cache_use) {
6625 .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| {
6626 whole.cache_manifest_mutex.lock();
6627 defer whole.cache_manifest_mutex.unlock();
6628 try whole_cache_manifest.addFilePost(dep_file_path);
6629 },
6630 .incremental, .none => {},
6631 }
6632 }
6633 }
6634
6635 // Rename into place.
6636 const digest = man.final();
6637 const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });
6638 var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{});
6639 defer o_dir.close();
6640 const tmp_basename = fs.path.basename(out_res_path);
6641 try fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, res_filename);
6642 break :blk digest;
6643 };
6644
6645 if (man.have_exclusive_lock) {
6646 // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
6647 // possible we had a hit and the manifest is dirty, for example if the file mtime changed but
6648 // the contents were the same, we hit the cache but the manifest is dirty and we need to update
6649 // it to prevent doing a full file content comparison the next time around.
6650 man.writeManifest() catch |err| {
6651 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ rc_src.src_path, @errorName(err) });
6652 };
6653 }
6654
6655 const res_basename = try std.fmt.allocPrint(arena, "{s}.res", .{rc_basename_noext});
6656
6657 win32_resource.status = .{
6658 .success = .{
6659 .res_path = try comp.dirs.local_cache.join(comp.gpa, &[_][]const u8{
6660 "o", &digest, res_basename,
6661 }),
6662 .lock = man.toOwnedLock(),
6663 },
6664 };
6665}
6666
6667fn spawnZigRc(
6668 comp: *Compilation,
6669 win32_resource: *Win32Resource,
6670 arena: Allocator,
6671 argv: []const []const u8,
6672 child_progress_node: std.Progress.Node,
6673) !void {
6674 var node_name: std.ArrayList(u8) = .empty;
6675 defer node_name.deinit(arena);
6676
6677 var child = std.process.Child.init(argv, arena);
6678 child.stdin_behavior = .Ignore;
6679 child.stdout_behavior = .Pipe;
6680 child.stderr_behavior = .Pipe;
6681 child.progress_node = child_progress_node;
6682
6683 child.spawn() catch |err| {
6684 return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) });
6685 };
6686
6687 var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{
6688 .stdout = child.stdout.?,
6689 .stderr = child.stderr.?,
6690 });
6691 defer poller.deinit();
6692
6693 const stdout = poller.reader(.stdout);
6694
6695 poll: while (true) {
6696 const MessageHeader = std.zig.Server.Message.Header;
6697 while (stdout.buffered().len < @sizeOf(MessageHeader)) if (!try poller.poll()) break :poll;
6698 const header = stdout.takeStruct(MessageHeader, .little) catch unreachable;
6699 while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll;
6700 const body = stdout.take(header.bytes_len) catch unreachable;
6701
6702 switch (header.tag) {
6703 // We expect exactly one ErrorBundle, and if any error_bundle header is
6704 // sent then it's a fatal error.
6705 .error_bundle => {
6706 const error_bundle = try std.zig.Server.allocErrorBundle(comp.gpa, body);
6707 return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);
6708 },
6709 else => {}, // ignore other messages
6710 }
6711 }
6712
6713 // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)
6714 const stderr = poller.reader(.stderr);
6715
6716 const term = child.wait() catch |err| {
6717 return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) });
6718 };
6719
6720 switch (term) {
6721 .Exited => |code| {
6722 if (code != 0) {
6723 log.err("zig rc failed with stderr:\n{s}", .{stderr.buffered()});
6724 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
6725 }
6726 },
6727 else => {
6728 log.err("zig rc terminated with stderr:\n{s}", .{stderr.buffered()});
6729 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
6730 },
6731 }
6732}
6733
6734pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
6735 const s = fs.path.sep_str;
6736 const rand_int = std.crypto.random.int(u64);
6737 if (comp.dirs.local_cache.path) |p| {
6738 return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
6739 } else {
6740 return std.fmt.allocPrint(ally, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
6741 }
6742}
6743
6744/// Add common C compiler args between translate-c and C object compilation.
6745fn addCommonCCArgs(
6746 comp: *const Compilation,
6747 arena: Allocator,
6748 argv: *std.array_list.Managed([]const u8),
6749 ext: FileExt,
6750 out_dep_path: ?[]const u8,
6751 mod: *Package.Module,
6752 c_frontend: Config.CFrontend,
6753) !void {
6754 const target = &mod.resolved_target.result;
6755 const is_clang = c_frontend == .clang;
6756
6757 if (target_util.supports_fpic(target)) {
6758 // PIE needs to go before PIC because Clang interprets `-fno-PIE` to imply `-fno-PIC`, which
6759 // we don't necessarily want.
6760 try argv.append(if (comp.config.pie) "-fPIE" else "-fno-PIE");
6761 try argv.append(if (mod.pic) "-fPIC" else "-fno-PIC");
6762 }
6763
6764 switch (target.os.tag) {
6765 .ios, .maccatalyst, .macos, .tvos, .watchos => |os| if (is_clang) {
6766 try argv.ensureUnusedCapacity(2);
6767 // Pass the proper -m<os>-version-min argument for darwin.
6768 const ver = target.os.version_range.semver.min;
6769 argv.appendAssumeCapacity(try std.fmt.allocPrint(arena, "-m{s}{s}-version-min={d}.{d}.{d}", .{
6770 switch (os) {
6771 .maccatalyst => "ios",
6772 else => @tagName(os),
6773 },
6774 switch (target.abi) {
6775 .simulator => "-simulator",
6776 else => "",
6777 },
6778 ver.major,
6779 ver.minor,
6780 ver.patch,
6781 }));
6782 // This avoids a warning that sometimes occurs when
6783 // providing both a -target argument that contains a
6784 // version as well as the -mmacosx-version-min argument.
6785 // Zig provides the correct value in both places, so it
6786 // doesn't matter which one gets overridden.
6787 argv.appendAssumeCapacity("-Wno-overriding-option");
6788 },
6789 else => {},
6790 }
6791
6792 if (comp.mingw_unicode_entry_point) {
6793 try argv.append("-municode");
6794 }
6795
6796 try argv.ensureUnusedCapacity(2);
6797 switch (comp.config.debug_format) {
6798 .strip => {},
6799 .code_view => {
6800 // -g is required here because -gcodeview doesn't trigger debug info
6801 // generation, it only changes the type of information generated.
6802 argv.appendSliceAssumeCapacity(&.{ "-g", "-gcodeview" });
6803 },
6804 .dwarf => |f| {
6805 argv.appendAssumeCapacity("-gdwarf-4");
6806 switch (f) {
6807 .@"32" => argv.appendAssumeCapacity("-gdwarf32"),
6808 .@"64" => argv.appendAssumeCapacity("-gdwarf64"),
6809 }
6810 },
6811 }
6812
6813 switch (comp.config.lto) {
6814 .none => try argv.append("-fno-lto"),
6815 .full => try argv.append("-flto=full"),
6816 .thin => try argv.append("-flto=thin"),
6817 }
6818
6819 // This only works for preprocessed files. Guarded by `FileExt.clangSupportsDepFile`.
6820 if (out_dep_path) |p| {
6821 try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p });
6822 }
6823
6824 // Non-preprocessed assembly files don't support these flags.
6825 if (ext != .assembly) {
6826 try argv.append(if (target.os.tag == .freestanding) "-ffreestanding" else "-fhosted");
6827
6828 try argv.append("-nostdinc");
6829
6830 if (ext == .cpp or ext == .hpp) {
6831 try argv.append("-nostdinc++");
6832 }
6833
6834 // LLVM IR files don't support these flags.
6835 if (ext != .ll and ext != .bc) {
6836 switch (mod.optimize_mode) {
6837 .Debug => {},
6838 .ReleaseSafe => {
6839 try argv.append("-D_FORTIFY_SOURCE=2");
6840 },
6841 .ReleaseFast, .ReleaseSmall => {
6842 try argv.append("-DNDEBUG");
6843 },
6844 }
6845
6846 switch (target.os.tag) {
6847 // LLVM doesn't distinguish between Solaris and illumos, but the illumos GCC fork
6848 // defines this macro.
6849 .illumos => try argv.append("__illumos__"),
6850 // Homebrew targets without LLVM support; use communities's preferred macros.
6851 .@"3ds" => try argv.append("-D__3DS__"),
6852 .vita => try argv.append("-D__vita__"),
6853 else => {},
6854 }
6855
6856 if (comp.config.link_libc) {
6857 if (target.isGnuLibC()) {
6858 const target_version = target.os.versionRange().gnuLibCVersion().?;
6859 const glibc_minor_define = try std.fmt.allocPrint(arena, "-D__GLIBC_MINOR__={d}", .{
6860 target_version.minor,
6861 });
6862 try argv.append(glibc_minor_define);
6863 } else if (target.isMinGW()) {
6864 try argv.append("-D__MSVCRT_VERSION__=0xE00"); // use ucrt
6865
6866 const minver: u16 = @truncate(@intFromEnum(target.os.versionRange().windows.min) >> 16);
6867 try argv.append(
6868 try std.fmt.allocPrint(arena, "-D_WIN32_WINNT=0x{x:0>4}", .{minver}),
6869 );
6870 } else if (target.isFreeBSDLibC()) {
6871 // https://docs.freebsd.org/en/books/porters-handbook/versions
6872 const min_ver = target.os.version_range.semver.min;
6873 try argv.append(try std.fmt.allocPrint(arena, "-D__FreeBSD_version={d}", .{
6874 // We don't currently respect the minor and patch components. This wouldn't be particularly
6875 // helpful because our abilists file only tracks major FreeBSD releases, so the link-time stub
6876 // symbols would be inconsistent with header declarations.
6877 min_ver.major * 100_000 + 500,
6878 }));
6879 } else if (target.isNetBSDLibC()) {
6880 const min_ver = target.os.version_range.semver.min;
6881 try argv.append(try std.fmt.allocPrint(arena, "-D__NetBSD_Version__={d}", .{
6882 // We don't currently respect the patch component. This wouldn't be particularly helpful because
6883 // our abilists file only tracks major and minor NetBSD releases, so the link-time stub symbols
6884 // would be inconsistent with header declarations.
6885 (min_ver.major * 100_000_000) + (min_ver.minor * 1_000_000),
6886 }));
6887 }
6888 }
6889
6890 if (comp.config.link_libcpp) {
6891 try argv.append("-isystem");
6892 try argv.append(try fs.path.join(arena, &[_][]const u8{
6893 comp.dirs.zig_lib.path.?, "libcxx", "include",
6894 }));
6895
6896 try argv.append("-isystem");
6897 try argv.append(try fs.path.join(arena, &[_][]const u8{
6898 comp.dirs.zig_lib.path.?, "libcxxabi", "include",
6899 }));
6900
6901 try libcxx.addCxxArgs(comp, arena, argv);
6902 }
6903
6904 // According to Rich Felker libc headers are supposed to go before C language headers.
6905 // However as noted by @dimenus, appending libc headers before compiler headers breaks
6906 // intrinsics and other compiler specific items.
6907 try argv.append("-isystem");
6908 try argv.append(try fs.path.join(arena, &.{ comp.dirs.zig_lib.path.?, "include" }));
6909
6910 try argv.ensureUnusedCapacity(comp.libc_include_dir_list.len * 2);
6911 for (comp.libc_include_dir_list) |include_dir| {
6912 try argv.append("-isystem");
6913 try argv.append(include_dir);
6914 }
6915
6916 if (mod.resolved_target.is_native_os and mod.resolved_target.is_native_abi) {
6917 try argv.ensureUnusedCapacity(comp.native_system_include_paths.len * 2);
6918 for (comp.native_system_include_paths) |include_path| {
6919 argv.appendAssumeCapacity("-isystem");
6920 argv.appendAssumeCapacity(include_path);
6921 }
6922 }
6923
6924 if (comp.config.link_libunwind) {
6925 try argv.append("-isystem");
6926 try argv.append(try fs.path.join(arena, &[_][]const u8{
6927 comp.dirs.zig_lib.path.?, "libunwind", "include",
6928 }));
6929 }
6930
6931 try argv.ensureUnusedCapacity(comp.libc_framework_dir_list.len * 2);
6932 for (comp.libc_framework_dir_list) |framework_dir| {
6933 try argv.appendSlice(&.{ "-iframework", framework_dir });
6934 }
6935
6936 try argv.ensureUnusedCapacity(comp.framework_dirs.len * 2);
6937 for (comp.framework_dirs) |framework_dir| {
6938 try argv.appendSlice(&.{ "-F", framework_dir });
6939 }
6940 }
6941 }
6942
6943 // Only C-family files support these flags.
6944 switch (ext) {
6945 .c,
6946 .h,
6947 .cpp,
6948 .hpp,
6949 .m,
6950 .hm,
6951 .mm,
6952 .hmm,
6953 => {
6954 if (is_clang) {
6955 try argv.append("-fno-spell-checking");
6956
6957 if (target.os.tag == .windows and target.abi.isGnu()) {
6958 // windows.h has files such as pshpack1.h which do #pragma packing,
6959 // triggering a clang warning. So for this target, we disable this warning.
6960 try argv.append("-Wno-pragma-pack");
6961 }
6962 }
6963
6964 if (mod.optimize_mode != .Debug) {
6965 try argv.append("-Werror=date-time");
6966 }
6967 },
6968 else => {},
6969 }
6970
6971 // Only compiled files support these flags.
6972 switch (ext) {
6973 .c,
6974 .h,
6975 .cpp,
6976 .hpp,
6977 .m,
6978 .hm,
6979 .mm,
6980 .hmm,
6981 .ll,
6982 .bc,
6983 => {
6984 if (mod.code_model != .default) {
6985 try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mod.code_model)}));
6986 }
6987
6988 if (is_clang) {
6989 var san_arg: std.ArrayList(u8) = .empty;
6990 const prefix = "-fsanitize=";
6991 if (mod.sanitize_c != .off) {
6992 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
6993 try san_arg.appendSlice(arena, "undefined,");
6994 }
6995 if (mod.sanitize_thread) {
6996 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
6997 try san_arg.appendSlice(arena, "thread,");
6998 }
6999 if (mod.fuzz) {
7000 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
7001 try san_arg.appendSlice(arena, "fuzzer-no-link,");
7002 }
7003 // Chop off the trailing comma and append to argv.
7004 if (san_arg.pop()) |_| {
7005 try argv.append(san_arg.items);
7006
7007 switch (mod.sanitize_c) {
7008 .off => {},
7009 .trap => {
7010 try argv.append("-fsanitize-trap=undefined");
7011 },
7012 .full => {
7013 // This check requires implementing the Itanium C++ ABI.
7014 // We would make it `-fsanitize-trap=vptr`, however this check requires
7015 // a full runtime due to the type hashing involved.
7016 try argv.append("-fno-sanitize=vptr");
7017
7018 // It is very common, and well-defined, for a pointer on one side of a C ABI
7019 // to have a different but compatible element type. Examples include:
7020 // `char*` vs `uint8_t*` on a system with 8-bit bytes
7021 // `const char*` vs `char*`
7022 // `char*` vs `unsigned char*`
7023 // Without this flag, Clang would invoke UBSAN when such an extern
7024 // function was called.
7025 try argv.append("-fno-sanitize=function");
7026
7027 // This is necessary because, by default, Clang instructs LLVM to embed
7028 // a COFF link dependency on `libclang_rt.ubsan_standalone.a` when the
7029 // UBSan runtime is used.
7030 if (target.os.tag == .windows) {
7031 try argv.append("-fno-rtlib-defaultlib");
7032 }
7033 },
7034 }
7035 }
7036
7037 if (comp.config.san_cov_trace_pc_guard) {
7038 try argv.append("-fsanitize-coverage=trace-pc-guard");
7039 }
7040 }
7041
7042 switch (mod.optimize_mode) {
7043 .Debug => {
7044 // Clang has -Og for compatibility with GCC, but currently it is just equivalent
7045 // to -O1. Besides potentially impairing debugging, -O1/-Og significantly
7046 // increases compile times.
7047 try argv.append("-O0");
7048 },
7049 .ReleaseSafe => {
7050 // See the comment in the BuildModeFastRelease case for why we pass -O2 rather
7051 // than -O3 here.
7052 try argv.append("-O2");
7053 },
7054 .ReleaseFast => {
7055 // Here we pass -O2 rather than -O3 because, although we do the equivalent of
7056 // -O3 in Zig code, the justification for the difference here is that Zig
7057 // has better detection and prevention of undefined behavior, so -O3 is safer for
7058 // Zig code than it is for C code. Also, C programmers are used to their code
7059 // running in -O2 and thus the -O3 path has been tested less.
7060 try argv.append("-O2");
7061 },
7062 .ReleaseSmall => {
7063 try argv.append("-Os");
7064 },
7065 }
7066 },
7067 else => {},
7068 }
7069}
7070
7071/// Add common C compiler args and Clang specific args.
7072pub fn addCCArgs(
7073 comp: *const Compilation,
7074 arena: Allocator,
7075 argv: *std.array_list.Managed([]const u8),
7076 ext: FileExt,
7077 out_dep_path: ?[]const u8,
7078 mod: *Package.Module,
7079) !void {
7080 const target = &mod.resolved_target.result;
7081
7082 // As of Clang 16.x, it will by default read extra flags from /etc/clang.
7083 // I'm sure the person who implemented this means well, but they have a lot
7084 // to learn about abstractions and where the appropriate boundaries between
7085 // them are. The road to hell is paved with good intentions. Fortunately it
7086 // can be disabled.
7087 try argv.append("--no-default-config");
7088
7089 // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode
7090 // we want Clang to infer it, and in normal mode we always want it off, which will be true since
7091 // clang will detect stderr as a pipe rather than a terminal.
7092 if (!comp.clang_passthrough_mode and ext.clangSupportsDiagnostics()) {
7093 // Make stderr more easily parseable.
7094 try argv.append("-fno-caret-diagnostics");
7095 }
7096
7097 // We never want clang to invoke the system assembler for anything. So we would want
7098 // this option always enabled. However, it only matters for some targets. To avoid
7099 // "unused parameter" warnings, and to keep CLI spam to a minimum, we only put this
7100 // flag on the command line if it is necessary.
7101 if (target_util.clangMightShellOutForAssembly(target)) {
7102 try argv.append("-integrated-as");
7103 }
7104
7105 const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target);
7106 try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
7107
7108 if (target.cpu.arch.isThumb()) {
7109 try argv.append(switch (ext) {
7110 .assembly, .assembly_with_cpp => "-Wa,-mthumb",
7111 else => "-mthumb",
7112 });
7113 }
7114
7115 if (target_util.llvmMachineAbi(target)) |mabi| {
7116 // Clang's integrated Arm assembler doesn't support `-mabi` yet...
7117 // Clang's FreeBSD driver doesn't support `-mabi` on PPC64 (ELFv2 is used anyway).
7118 if (!(target.cpu.arch.isArm() and (ext == .assembly or ext == .assembly_with_cpp)) and
7119 !(target.cpu.arch.isPowerPC64() and target.os.tag == .freebsd))
7120 {
7121 try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{mabi}));
7122 }
7123 }
7124
7125 // We might want to support -mfloat-abi=softfp for Arm and CSKY here in the future.
7126 if (target_util.clangSupportsFloatAbiArg(target)) {
7127 const fabi = @tagName(target.abi.float());
7128
7129 try argv.append(switch (target.cpu.arch) {
7130 // For whatever reason, Clang doesn't support `-mfloat-abi` for s390x.
7131 .s390x => try std.fmt.allocPrint(arena, "-m{s}-float", .{fabi}),
7132 else => try std.fmt.allocPrint(arena, "-mfloat-abi={s}", .{fabi}),
7133 });
7134 }
7135
7136 try comp.addCommonCCArgs(arena, argv, ext, out_dep_path, mod, comp.config.c_frontend);
7137
7138 // Only assembly files support these flags.
7139 switch (ext) {
7140 .assembly,
7141 .assembly_with_cpp,
7142 => {
7143 // The Clang assembler does not accept the list of CPU features like the
7144 // compiler frontend does. Therefore we must hard-code the -m flags for
7145 // all CPU features here.
7146 switch (target.cpu.arch) {
7147 .riscv32, .riscv32be, .riscv64, .riscv64be => {
7148 const RvArchFeat = struct { char: u8, feat: std.Target.riscv.Feature };
7149 const letters = [_]RvArchFeat{
7150 .{ .char = 'm', .feat = .m },
7151 .{ .char = 'a', .feat = .a },
7152 .{ .char = 'f', .feat = .f },
7153 .{ .char = 'd', .feat = .d },
7154 .{ .char = 'c', .feat = .c },
7155 };
7156 const prefix: []const u8 = if (target.cpu.arch == .riscv64) "rv64" else "rv32";
7157 const prefix_len = 4;
7158 assert(prefix.len == prefix_len);
7159 var march_buf: [prefix_len + letters.len + 1]u8 = undefined;
7160 var march_index: usize = prefix_len;
7161 @memcpy(march_buf[0..prefix.len], prefix);
7162
7163 if (target.cpu.has(.riscv, .e)) {
7164 march_buf[march_index] = 'e';
7165 } else {
7166 march_buf[march_index] = 'i';
7167 }
7168 march_index += 1;
7169
7170 for (letters) |letter| {
7171 if (target.cpu.has(.riscv, letter.feat)) {
7172 march_buf[march_index] = letter.char;
7173 march_index += 1;
7174 }
7175 }
7176
7177 const march_arg = try std.fmt.allocPrint(arena, "-march={s}", .{
7178 march_buf[0..march_index],
7179 });
7180 try argv.append(march_arg);
7181
7182 if (target.cpu.has(.riscv, .relax)) {
7183 try argv.append("-mrelax");
7184 } else {
7185 try argv.append("-mno-relax");
7186 }
7187 if (target.cpu.has(.riscv, .save_restore)) {
7188 try argv.append("-msave-restore");
7189 } else {
7190 try argv.append("-mno-save-restore");
7191 }
7192 },
7193 .mips, .mipsel, .mips64, .mips64el => {
7194 if (target.cpu.model.llvm_name) |llvm_name| {
7195 try argv.append(try std.fmt.allocPrint(arena, "-march={s}", .{llvm_name}));
7196 }
7197 },
7198 else => {
7199 // TODO
7200 },
7201 }
7202
7203 if (target_util.clangAssemblerSupportsMcpuArg(target)) {
7204 if (target.cpu.model.llvm_name) |llvm_name| {
7205 try argv.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{llvm_name}));
7206 }
7207 }
7208 },
7209 else => {},
7210 }
7211
7212 // Non-preprocessed assembly files don't support these flags.
7213 if (ext != .assembly) {
7214 if (target_util.clangSupportsNoImplicitFloatArg(target) and target.abi.float() == .soft) {
7215 try argv.append("-mno-implicit-float");
7216 }
7217
7218 if (target_util.hasRedZone(target)) {
7219 try argv.append(if (mod.red_zone) "-mred-zone" else "-mno-red-zone");
7220 }
7221
7222 try argv.append(if (mod.omit_frame_pointer) "-fomit-frame-pointer" else "-fno-omit-frame-pointer");
7223 if (target.cpu.arch == .s390x) {
7224 try argv.append(if (mod.omit_frame_pointer) "-mbackchain" else "-mno-backchain");
7225 }
7226
7227 const ssp_buf_size = mod.stack_protector;
7228 if (ssp_buf_size != 0) {
7229 try argv.appendSlice(&[_][]const u8{
7230 "-fstack-protector-strong",
7231 "--param",
7232 try std.fmt.allocPrint(arena, "ssp-buffer-size={d}", .{ssp_buf_size}),
7233 });
7234 } else {
7235 try argv.append("-fno-stack-protector");
7236 }
7237
7238 try argv.append(if (mod.no_builtin) "-fno-builtin" else "-fbuiltin");
7239
7240 try argv.append(if (comp.function_sections) "-ffunction-sections" else "-fno-function-sections");
7241 try argv.append(if (comp.data_sections) "-fdata-sections" else "-fno-data-sections");
7242
7243 switch (mod.unwind_tables) {
7244 .none => {
7245 try argv.append("-fno-unwind-tables");
7246 try argv.append("-fno-asynchronous-unwind-tables");
7247 },
7248 .sync => {
7249 // Need to override Clang's convoluted default logic.
7250 try argv.append("-fno-asynchronous-unwind-tables");
7251 try argv.append("-funwind-tables");
7252 },
7253 .async => try argv.append("-fasynchronous-unwind-tables"),
7254 }
7255 }
7256
7257 // Only compiled files support these flags.
7258 switch (ext) {
7259 .c,
7260 .h,
7261 .cpp,
7262 .hpp,
7263 .m,
7264 .hm,
7265 .mm,
7266 .hmm,
7267 .ll,
7268 .bc,
7269 => {
7270 const xclang_flag = switch (ext) {
7271 .assembly, .assembly_with_cpp => "-Xclangas",
7272 else => "-Xclang",
7273 };
7274
7275 if (target_util.clangSupportsTargetCpuArg(target)) {
7276 if (target.cpu.model.llvm_name) |llvm_name| {
7277 try argv.appendSlice(&[_][]const u8{
7278 xclang_flag, "-target-cpu", xclang_flag, llvm_name,
7279 });
7280 }
7281 }
7282
7283 // It would be really nice if there was a more compact way to communicate this info to Clang.
7284 const all_features_list = target.cpu.arch.allFeaturesList();
7285 try argv.ensureUnusedCapacity(all_features_list.len * 4);
7286 for (all_features_list, 0..) |feature, index_usize| {
7287 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
7288 const is_enabled = target.cpu.features.isEnabled(index);
7289
7290 if (feature.llvm_name) |llvm_name| {
7291 // We communicate these to Clang through the dedicated options.
7292 if (std.mem.startsWith(u8, llvm_name, "soft-float") or
7293 std.mem.startsWith(u8, llvm_name, "hard-float") or
7294 (target.cpu.arch == .s390x and std.mem.eql(u8, llvm_name, "backchain")))
7295 continue;
7296
7297 // Ignore these until we figure out how to handle the concept of omitting features.
7298 // See https://github.com/ziglang/zig/issues/23539
7299 if (target_util.isDynamicAMDGCNFeature(target, feature)) continue;
7300
7301 argv.appendSliceAssumeCapacity(&[_][]const u8{ xclang_flag, "-target-feature", xclang_flag });
7302 const plus_or_minus = "-+"[@intFromBool(is_enabled)];
7303 const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name });
7304 argv.appendAssumeCapacity(arg);
7305 }
7306 }
7307 },
7308 else => {},
7309 }
7310
7311 try argv.appendSlice(comp.global_cc_argv);
7312 try argv.appendSlice(mod.cc_argv);
7313}
7314
7315fn failCObj(
7316 comp: *Compilation,
7317 c_object: *CObject,
7318 comptime format: []const u8,
7319 args: anytype,
7320) error{ OutOfMemory, AnalysisFail } {
7321 @branchHint(.cold);
7322 const diag_bundle = blk: {
7323 const diag_bundle = try comp.gpa.create(CObject.Diag.Bundle);
7324 diag_bundle.* = .{};
7325 errdefer diag_bundle.destroy(comp.gpa);
7326
7327 try diag_bundle.file_names.ensureTotalCapacity(comp.gpa, 1);
7328 diag_bundle.file_names.putAssumeCapacity(1, try comp.gpa.dupe(u8, c_object.src.src_path));
7329
7330 diag_bundle.diags = try comp.gpa.alloc(CObject.Diag, 1);
7331 diag_bundle.diags[0] = .{};
7332 diag_bundle.diags[0].level = 3;
7333 diag_bundle.diags[0].msg = try std.fmt.allocPrint(comp.gpa, format, args);
7334 diag_bundle.diags[0].src_loc.file = 1;
7335 break :blk diag_bundle;
7336 };
7337 return comp.failCObjWithOwnedDiagBundle(c_object, diag_bundle);
7338}
7339
7340fn failCObjWithOwnedDiagBundle(
7341 comp: *Compilation,
7342 c_object: *CObject,
7343 diag_bundle: *CObject.Diag.Bundle,
7344) error{ OutOfMemory, AnalysisFail } {
7345 @branchHint(.cold);
7346 assert(diag_bundle.diags.len > 0);
7347 {
7348 comp.mutex.lock();
7349 defer comp.mutex.unlock();
7350 {
7351 errdefer diag_bundle.destroy(comp.gpa);
7352 try comp.failed_c_objects.ensureUnusedCapacity(comp.gpa, 1);
7353 }
7354 comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, diag_bundle);
7355 }
7356 c_object.status = .failure;
7357 return error.AnalysisFail;
7358}
7359
7360fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) error{ OutOfMemory, AnalysisFail } {
7361 @branchHint(.cold);
7362 var bundle: ErrorBundle.Wip = undefined;
7363 try bundle.init(comp.gpa);
7364 errdefer bundle.deinit();
7365 try bundle.addRootErrorMessage(.{
7366 .msg = try bundle.printString(format, args),
7367 .src_loc = try bundle.addSourceLocation(.{
7368 .src_path = try bundle.addString(switch (win32_resource.src) {
7369 .rc => |rc_src| rc_src.src_path,
7370 .manifest => |manifest_src| manifest_src,
7371 }),
7372 .line = 0,
7373 .column = 0,
7374 .span_start = 0,
7375 .span_main = 0,
7376 .span_end = 0,
7377 }),
7378 });
7379 const finished_bundle = try bundle.toOwnedBundle("");
7380 return comp.failWin32ResourceWithOwnedBundle(win32_resource, finished_bundle);
7381}
7382
7383fn failWin32ResourceWithOwnedBundle(
7384 comp: *Compilation,
7385 win32_resource: *Win32Resource,
7386 err_bundle: ErrorBundle,
7387) error{ OutOfMemory, AnalysisFail } {
7388 @branchHint(.cold);
7389 {
7390 comp.mutex.lock();
7391 defer comp.mutex.unlock();
7392 try comp.failed_win32_resources.putNoClobber(comp.gpa, win32_resource, err_bundle);
7393 }
7394 win32_resource.status = .failure;
7395 return error.AnalysisFail;
7396}
7397
7398pub const FileExt = enum {
7399 c,
7400 cpp,
7401 h,
7402 hpp,
7403 hm,
7404 hmm,
7405 m,
7406 mm,
7407 ll,
7408 bc,
7409 assembly,
7410 assembly_with_cpp,
7411 shared_library,
7412 object,
7413 static_library,
7414 zig,
7415 def,
7416 rc,
7417 res,
7418 manifest,
7419 unknown,
7420
7421 pub fn clangNeedsLanguageOverride(ext: FileExt) bool {
7422 return switch (ext) {
7423 .h,
7424 .hpp,
7425 .hm,
7426 .hmm,
7427 => true,
7428
7429 .c,
7430 .cpp,
7431 .m,
7432 .mm,
7433 .ll,
7434 .bc,
7435 .assembly,
7436 .assembly_with_cpp,
7437 .shared_library,
7438 .object,
7439 .static_library,
7440 .zig,
7441 .def,
7442 .rc,
7443 .res,
7444 .manifest,
7445 .unknown,
7446 => false,
7447 };
7448 }
7449
7450 pub fn clangSupportsDiagnostics(ext: FileExt) bool {
7451 return switch (ext) {
7452 .c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm, .ll, .bc => true,
7453
7454 .assembly,
7455 .assembly_with_cpp,
7456 .shared_library,
7457 .object,
7458 .static_library,
7459 .zig,
7460 .def,
7461 .rc,
7462 .res,
7463 .manifest,
7464 .unknown,
7465 => false,
7466 };
7467 }
7468
7469 pub fn clangSupportsDepFile(ext: FileExt) bool {
7470 return switch (ext) {
7471 .assembly_with_cpp, .c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm => true,
7472
7473 .ll,
7474 .bc,
7475 .assembly,
7476 .shared_library,
7477 .object,
7478 .static_library,
7479 .zig,
7480 .def,
7481 .rc,
7482 .res,
7483 .manifest,
7484 .unknown,
7485 => false,
7486 };
7487 }
7488
7489 pub fn canonicalName(ext: FileExt, target: *const Target) [:0]const u8 {
7490 return switch (ext) {
7491 .c => ".c",
7492 .cpp => ".cpp",
7493 .h => ".h",
7494 .hpp => ".hpp",
7495 .hm => ".hm",
7496 .hmm => ".hmm",
7497 .m => ".m",
7498 .mm => ".mm",
7499 .ll => ".ll",
7500 .bc => ".bc",
7501 .assembly => ".s",
7502 .assembly_with_cpp => ".S",
7503 .shared_library => target.dynamicLibSuffix(),
7504 .object => target.ofmt.fileExt(target.cpu.arch),
7505 .static_library => target.staticLibSuffix(),
7506 .zig => ".zig",
7507 .def => ".def",
7508 .rc => ".rc",
7509 .res => ".res",
7510 .manifest => ".manifest",
7511 .unknown => "",
7512 };
7513 }
7514};
7515
7516pub fn hasObjectExt(filename: []const u8) bool {
7517 return mem.endsWith(u8, filename, ".o") or
7518 mem.endsWith(u8, filename, ".lo") or
7519 mem.endsWith(u8, filename, ".obj") or
7520 mem.endsWith(u8, filename, ".rmeta");
7521}
7522
7523pub fn hasStaticLibraryExt(filename: []const u8) bool {
7524 return mem.endsWith(u8, filename, ".a") or
7525 mem.endsWith(u8, filename, ".lib") or
7526 mem.endsWith(u8, filename, ".rlib");
7527}
7528
7529pub fn hasCExt(filename: []const u8) bool {
7530 return mem.endsWith(u8, filename, ".c");
7531}
7532
7533pub fn hasCHExt(filename: []const u8) bool {
7534 return mem.endsWith(u8, filename, ".h");
7535}
7536
7537pub fn hasCppExt(filename: []const u8) bool {
7538 return mem.endsWith(u8, filename, ".C") or
7539 mem.endsWith(u8, filename, ".cc") or
7540 mem.endsWith(u8, filename, ".cp") or
7541 mem.endsWith(u8, filename, ".CPP") or
7542 mem.endsWith(u8, filename, ".cpp") or
7543 mem.endsWith(u8, filename, ".cxx") or
7544 mem.endsWith(u8, filename, ".c++");
7545}
7546
7547pub fn hasCppHExt(filename: []const u8) bool {
7548 return mem.endsWith(u8, filename, ".hh") or
7549 mem.endsWith(u8, filename, ".hpp") or
7550 mem.endsWith(u8, filename, ".hxx");
7551}
7552
7553pub fn hasObjCExt(filename: []const u8) bool {
7554 return mem.endsWith(u8, filename, ".m");
7555}
7556
7557pub fn hasObjCHExt(filename: []const u8) bool {
7558 return mem.endsWith(u8, filename, ".hm");
7559}
7560
7561pub fn hasObjCppExt(filename: []const u8) bool {
7562 return mem.endsWith(u8, filename, ".M") or
7563 mem.endsWith(u8, filename, ".mm");
7564}
7565
7566pub fn hasObjCppHExt(filename: []const u8) bool {
7567 return mem.endsWith(u8, filename, ".hmm");
7568}
7569
7570pub fn hasSharedLibraryExt(filename: []const u8) bool {
7571 if (mem.endsWith(u8, filename, ".so") or
7572 mem.endsWith(u8, filename, ".dll") or
7573 mem.endsWith(u8, filename, ".dylib") or
7574 mem.endsWith(u8, filename, ".tbd"))
7575 {
7576 return true;
7577 }
7578 // Look for .so.X, .so.X.Y, .so.X.Y.Z
7579 var it = mem.splitScalar(u8, filename, '.');
7580 _ = it.first();
7581 var so_txt = it.next() orelse return false;
7582 while (!mem.eql(u8, so_txt, "so")) {
7583 so_txt = it.next() orelse return false;
7584 }
7585 const n1 = it.next() orelse return false;
7586 const n2 = it.next();
7587 const n3 = it.next();
7588
7589 _ = std.fmt.parseInt(u32, n1, 10) catch return false;
7590 if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
7591 if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
7592 if (it.next() != null) return false;
7593
7594 return true;
7595}
7596
7597pub fn classifyFileExt(filename: []const u8) FileExt {
7598 if (hasCExt(filename)) {
7599 return .c;
7600 } else if (hasCHExt(filename)) {
7601 return .h;
7602 } else if (hasCppExt(filename)) {
7603 return .cpp;
7604 } else if (hasCppHExt(filename)) {
7605 return .hpp;
7606 } else if (hasObjCExt(filename)) {
7607 return .m;
7608 } else if (hasObjCHExt(filename)) {
7609 return .hm;
7610 } else if (hasObjCppExt(filename)) {
7611 return .mm;
7612 } else if (hasObjCppHExt(filename)) {
7613 return .hmm;
7614 } else if (mem.endsWith(u8, filename, ".ll")) {
7615 return .ll;
7616 } else if (mem.endsWith(u8, filename, ".bc")) {
7617 return .bc;
7618 } else if (mem.endsWith(u8, filename, ".s")) {
7619 return .assembly;
7620 } else if (mem.endsWith(u8, filename, ".S")) {
7621 return .assembly_with_cpp;
7622 } else if (mem.endsWith(u8, filename, ".zig")) {
7623 return .zig;
7624 } else if (hasSharedLibraryExt(filename)) {
7625 return .shared_library;
7626 } else if (hasStaticLibraryExt(filename)) {
7627 return .static_library;
7628 } else if (hasObjectExt(filename)) {
7629 return .object;
7630 } else if (mem.endsWith(u8, filename, ".def")) {
7631 return .def;
7632 } else if (std.ascii.endsWithIgnoreCase(filename, ".rc")) {
7633 return .rc;
7634 } else if (std.ascii.endsWithIgnoreCase(filename, ".res")) {
7635 return .res;
7636 } else if (std.ascii.endsWithIgnoreCase(filename, ".manifest")) {
7637 return .manifest;
7638 } else {
7639 return .unknown;
7640 }
7641}
7642
7643test "classifyFileExt" {
7644 try std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc"));
7645 try std.testing.expectEqual(FileExt.m, classifyFileExt("foo.m"));
7646 try std.testing.expectEqual(FileExt.mm, classifyFileExt("foo.mm"));
7647 try std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim"));
7648 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so"));
7649 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1"));
7650 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2"));
7651 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2.3"));
7652 try std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~"));
7653 try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
7654}
7655
7656fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Cache.Path {
7657 return (try crtFilePath(&comp.crt_files, basename)) orelse {
7658 const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable;
7659 const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir;
7660 const full_path = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
7661 return Cache.Path.initCwd(full_path);
7662 };
7663}
7664
7665pub fn crtFileAsString(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
7666 const path = try get_libc_crt_file(comp, arena, basename);
7667 return path.toString(arena);
7668}
7669
7670fn crtFilePath(crt_files: *std.StringHashMapUnmanaged(CrtFile), basename: []const u8) Allocator.Error!?Cache.Path {
7671 const crt_file = crt_files.get(basename) orelse return null;
7672 return crt_file.full_object_path;
7673}
7674
7675fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
7676 const is_exe_or_dyn_lib = switch (comp.config.output_mode) {
7677 .Obj => false,
7678 .Lib => comp.config.link_mode == .dynamic,
7679 .Exe => true,
7680 };
7681 const ofmt = comp.root_mod.resolved_target.result.ofmt;
7682 return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c;
7683}
7684
7685pub fn setAllocFailure(comp: *Compilation) void {
7686 @branchHint(.cold);
7687 log.debug("memory allocation failure", .{});
7688 comp.alloc_failure_occurred = true;
7689}
7690
7691/// Assumes that Compilation mutex is locked.
7692/// See also `lockAndSetMiscFailure`.
7693pub fn setMiscFailure(
7694 comp: *Compilation,
7695 tag: MiscTask,
7696 comptime format: []const u8,
7697 args: anytype,
7698) void {
7699 comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1) catch return comp.setAllocFailure();
7700 const msg = std.fmt.allocPrint(comp.gpa, format, args) catch return comp.setAllocFailure();
7701 const gop = comp.misc_failures.getOrPutAssumeCapacity(tag);
7702 if (gop.found_existing) {
7703 gop.value_ptr.deinit(comp.gpa);
7704 }
7705 gop.value_ptr.* = .{ .msg = msg };
7706}
7707
7708/// See also `setMiscFailure`.
7709pub fn lockAndSetMiscFailure(
7710 comp: *Compilation,
7711 tag: MiscTask,
7712 comptime format: []const u8,
7713 args: anytype,
7714) void {
7715 comp.mutex.lock();
7716 defer comp.mutex.unlock();
7717
7718 return setMiscFailure(comp, tag, format, args);
7719}
7720
7721pub fn dump_argv(argv: []const []const u8) void {
7722 var buffer: [64]u8 = undefined;
7723 const stderr, _ = std.debug.lockStderrWriter(&buffer);
7724 defer std.debug.unlockStderrWriter();
7725 nosuspend {
7726 for (argv, 0..) |arg, i| {
7727 if (i != 0) stderr.writeByte(' ') catch return;
7728 stderr.writeAll(arg) catch return;
7729 }
7730 stderr.writeByte('\n') catch return;
7731 }
7732}
7733
7734pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend {
7735 const target = &comp.root_mod.resolved_target.result;
7736 return target_util.zigBackend(target, comp.config.use_llvm);
7737}
7738
7739pub const SubUpdateError = UpdateError || error{AlreadyReported};
7740pub fn updateSubCompilation(
7741 parent_comp: *Compilation,
7742 sub_comp: *Compilation,
7743 misc_task: MiscTask,
7744 prog_node: std.Progress.Node,
7745) SubUpdateError!void {
7746 {
7747 const sub_node = prog_node.start(@tagName(misc_task), 0);
7748 defer sub_node.end();
7749
7750 try sub_comp.update(sub_node);
7751 }
7752
7753 // Look for compilation errors in this sub compilation
7754 const gpa = parent_comp.gpa;
7755
7756 var errors = try sub_comp.getAllErrorsAlloc();
7757 defer errors.deinit(gpa);
7758
7759 if (errors.errorMessageCount() > 0) {
7760 parent_comp.mutex.lock();
7761 defer parent_comp.mutex.unlock();
7762 try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1);
7763 parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{
7764 .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {t} failed", .{misc_task}),
7765 .children = errors,
7766 });
7767 errors = .empty; // ownership moved to the failures map
7768 return error.AlreadyReported;
7769 }
7770}
7771
7772fn buildOutputFromZig(
7773 comp: *Compilation,
7774 src_basename: []const u8,
7775 root_name: []const u8,
7776 output_mode: std.builtin.OutputMode,
7777 link_mode: std.builtin.LinkMode,
7778 misc_task_tag: MiscTask,
7779 prog_node: std.Progress.Node,
7780 options: RtOptions,
7781 out: *?CrtFile,
7782) SubUpdateError!void {
7783 const tracy_trace = trace(@src());
7784 defer tracy_trace.end();
7785
7786 const gpa = comp.gpa;
7787 const io = comp.io;
7788 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
7789 defer arena_allocator.deinit();
7790 const arena = arena_allocator.allocator();
7791
7792 assert(output_mode != .Exe);
7793
7794 const strip = comp.compilerRtStrip();
7795 const optimize_mode = comp.compilerRtOptMode();
7796
7797 const config = Config.resolve(.{
7798 .output_mode = output_mode,
7799 .link_mode = link_mode,
7800 .resolved_target = comp.root_mod.resolved_target,
7801 .is_test = false,
7802 .have_zcu = true,
7803 .emit_bin = true,
7804 .root_optimize_mode = optimize_mode,
7805 .root_strip = strip,
7806 .link_libc = comp.config.link_libc,
7807 .any_unwind_tables = comp.root_mod.unwind_tables != .none,
7808 .any_error_tracing = false,
7809 .root_error_tracing = false,
7810 .lto = if (options.allow_lto) comp.config.lto else .none,
7811 }) catch |err| {
7812 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to resolve compilation config: {t}", .{ misc_task_tag, err });
7813 return error.AlreadyReported;
7814 };
7815
7816 const root_mod = Package.Module.create(arena, .{
7817 .paths = .{
7818 .root = .zig_lib_root,
7819 .root_src_path = src_basename,
7820 },
7821 .fully_qualified_name = "root",
7822 .inherited = .{
7823 .resolved_target = comp.root_mod.resolved_target,
7824 .strip = strip,
7825 .stack_check = false,
7826 .stack_protector = 0,
7827 .red_zone = comp.root_mod.red_zone,
7828 .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
7829 .unwind_tables = comp.root_mod.unwind_tables,
7830 .pic = comp.root_mod.pic,
7831 .optimize_mode = optimize_mode,
7832 .structured_cfg = comp.root_mod.structured_cfg,
7833 .no_builtin = true,
7834 .code_model = comp.root_mod.code_model,
7835 .error_tracing = false,
7836 .valgrind = if (options.checks_valgrind) comp.root_mod.valgrind else null,
7837 },
7838 .global = config,
7839 .cc_argv = &.{},
7840 .parent = null,
7841 }) catch |err| {
7842 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to create module: {t}", .{ misc_task_tag, err });
7843 return error.AlreadyReported;
7844 };
7845
7846 const parent_whole_cache: ?ParentWholeCache = switch (comp.cache_use) {
7847 .whole => |whole| .{
7848 .manifest = whole.cache_manifest.?,
7849 .mutex = &whole.cache_manifest_mutex,
7850 .prefix_map = .{
7851 0, // cwd is the same
7852 1, // zig lib dir is the same
7853 3, // local cache is mapped to global cache
7854 3, // global cache is the same
7855 },
7856 },
7857 .incremental, .none => null,
7858 };
7859
7860 var sub_create_diag: CreateDiagnostic = undefined;
7861 const sub_compilation = Compilation.create(gpa, arena, io, &sub_create_diag, .{
7862 .dirs = comp.dirs.withoutLocalCache(),
7863 .cache_mode = .whole,
7864 .parent_whole_cache = parent_whole_cache,
7865 .self_exe_path = comp.self_exe_path,
7866 .config = config,
7867 .root_mod = root_mod,
7868 .root_name = root_name,
7869 .thread_pool = comp.thread_pool,
7870 .libc_installation = comp.libc_installation,
7871 .emit_bin = .yes_cache,
7872 .function_sections = true,
7873 .data_sections = true,
7874 .verbose_cc = comp.verbose_cc,
7875 .verbose_link = comp.verbose_link,
7876 .verbose_air = comp.verbose_air,
7877 .verbose_intern_pool = comp.verbose_intern_pool,
7878 .verbose_generic_instances = comp.verbose_intern_pool,
7879 .verbose_llvm_ir = comp.verbose_llvm_ir,
7880 .verbose_llvm_bc = comp.verbose_llvm_bc,
7881 .verbose_cimport = comp.verbose_cimport,
7882 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
7883 .clang_passthrough_mode = comp.clang_passthrough_mode,
7884 .skip_linker_dependencies = true,
7885 }) catch |err| switch (err) {
7886 error.CreateFail => {
7887 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag });
7888 return error.AlreadyReported;
7889 },
7890 else => |e| return e,
7891 };
7892 defer sub_compilation.destroy();
7893
7894 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
7895
7896 const crt_file = try sub_compilation.toCrtFile();
7897 assert(out.* == null);
7898 out.* = crt_file;
7899
7900 comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
7901}
7902
7903pub const CrtFileOptions = struct {
7904 function_sections: ?bool = null,
7905 data_sections: ?bool = null,
7906 omit_frame_pointer: ?bool = null,
7907 unwind_tables: ?std.builtin.UnwindTables = null,
7908 pic: ?bool = null,
7909 no_builtin: ?bool = null,
7910
7911 allow_lto: bool = true,
7912};
7913
7914pub fn build_crt_file(
7915 comp: *Compilation,
7916 root_name: []const u8,
7917 output_mode: std.builtin.OutputMode,
7918 misc_task_tag: MiscTask,
7919 prog_node: std.Progress.Node,
7920 /// These elements have to get mutated to add the owner module after it is
7921 /// created within this function.
7922 c_source_files: []CSourceFile,
7923 options: CrtFileOptions,
7924) SubUpdateError!void {
7925 const tracy_trace = trace(@src());
7926 defer tracy_trace.end();
7927
7928 const gpa = comp.gpa;
7929 const io = comp.io;
7930 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
7931 defer arena_allocator.deinit();
7932 const arena = arena_allocator.allocator();
7933
7934 const basename = try std.zig.binNameAlloc(gpa, .{
7935 .root_name = root_name,
7936 .target = &comp.root_mod.resolved_target.result,
7937 .output_mode = output_mode,
7938 });
7939
7940 const config = Config.resolve(.{
7941 .output_mode = output_mode,
7942 .resolved_target = comp.root_mod.resolved_target,
7943 .is_test = false,
7944 .have_zcu = false,
7945 .emit_bin = true,
7946 .root_optimize_mode = comp.compilerRtOptMode(),
7947 .root_strip = comp.compilerRtStrip(),
7948 .link_libc = false,
7949 .any_unwind_tables = options.unwind_tables != .none,
7950 .lto = switch (output_mode) {
7951 .Lib => if (options.allow_lto) comp.config.lto else .none,
7952 .Obj, .Exe => .none,
7953 },
7954 }) catch |err| {
7955 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to resolve compilation config: {t}", .{ misc_task_tag, err });
7956 return error.AlreadyReported;
7957 };
7958 const root_mod = Package.Module.create(arena, .{
7959 .paths = .{
7960 .root = .zig_lib_root,
7961 .root_src_path = "",
7962 },
7963 .fully_qualified_name = "root",
7964 .inherited = .{
7965 .resolved_target = comp.root_mod.resolved_target,
7966 .strip = comp.compilerRtStrip(),
7967 .stack_check = false,
7968 .stack_protector = 0,
7969 .sanitize_c = .off,
7970 .sanitize_thread = false,
7971 .red_zone = comp.root_mod.red_zone,
7972 // Some libcs (e.g. musl) are opinionated about -fomit-frame-pointer.
7973 .omit_frame_pointer = options.omit_frame_pointer orelse comp.root_mod.omit_frame_pointer,
7974 .valgrind = false,
7975 // Some libcs (e.g. MinGW) are opinionated about -funwind-tables.
7976 .unwind_tables = options.unwind_tables orelse .none,
7977 // Some CRT objects (e.g. musl's rcrt1.o and Scrt1.o) are opinionated about PIC.
7978 .pic = options.pic orelse comp.root_mod.pic,
7979 .optimize_mode = comp.compilerRtOptMode(),
7980 .structured_cfg = comp.root_mod.structured_cfg,
7981 // Some libcs (e.g. musl) are opinionated about -fno-builtin.
7982 .no_builtin = options.no_builtin orelse comp.root_mod.no_builtin,
7983 .code_model = comp.root_mod.code_model,
7984 },
7985 .global = config,
7986 .cc_argv = &.{},
7987 .parent = null,
7988 }) catch |err| {
7989 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to create module: {t}", .{ misc_task_tag, err });
7990 return error.AlreadyReported;
7991 };
7992
7993 for (c_source_files) |*item| {
7994 item.owner = root_mod;
7995 }
7996
7997 var sub_create_diag: CreateDiagnostic = undefined;
7998 const sub_compilation = Compilation.create(gpa, arena, io, &sub_create_diag, .{
7999 .dirs = comp.dirs.withoutLocalCache(),
8000 .self_exe_path = comp.self_exe_path,
8001 .cache_mode = .whole,
8002 .config = config,
8003 .root_mod = root_mod,
8004 .root_name = root_name,
8005 .thread_pool = comp.thread_pool,
8006 .libc_installation = comp.libc_installation,
8007 .emit_bin = .yes_cache,
8008 .function_sections = options.function_sections orelse false,
8009 .data_sections = options.data_sections orelse false,
8010 .c_source_files = c_source_files,
8011 .verbose_cc = comp.verbose_cc,
8012 .verbose_link = comp.verbose_link,
8013 .verbose_air = comp.verbose_air,
8014 .verbose_intern_pool = comp.verbose_intern_pool,
8015 .verbose_generic_instances = comp.verbose_generic_instances,
8016 .verbose_llvm_ir = comp.verbose_llvm_ir,
8017 .verbose_llvm_bc = comp.verbose_llvm_bc,
8018 .verbose_cimport = comp.verbose_cimport,
8019 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
8020 .clang_passthrough_mode = comp.clang_passthrough_mode,
8021 .skip_linker_dependencies = true,
8022 }) catch |err| switch (err) {
8023 error.CreateFail => {
8024 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag });
8025 return error.AlreadyReported;
8026 },
8027 else => |e| return e,
8028 };
8029 defer sub_compilation.destroy();
8030
8031 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
8032
8033 const crt_file = try sub_compilation.toCrtFile();
8034 comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
8035
8036 {
8037 comp.mutex.lock();
8038 defer comp.mutex.unlock();
8039 try comp.crt_files.ensureUnusedCapacity(gpa, 1);
8040 comp.crt_files.putAssumeCapacityNoClobber(basename, crt_file);
8041 }
8042}
8043
8044pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const Compilation.Config) void {
8045 comp.queuePrelinkTasks(switch (config.output_mode) {
8046 .Exe => unreachable,
8047 .Obj => &.{.{ .load_object = path }},
8048 .Lib => &.{switch (config.link_mode) {
8049 .static => .{ .load_archive = path },
8050 .dynamic => .{ .load_dso = path },
8051 }},
8052 });
8053}
8054
8055/// Only valid to call during `update`. Automatically handles queuing up a
8056/// linker worker task if there is not already one.
8057pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) void {
8058 comp.link_prog_node.increaseEstimatedTotalItems(tasks.len);
8059 comp.link_task_queue.enqueuePrelink(comp, tasks) catch |err| switch (err) {
8060 error.OutOfMemory => return comp.setAllocFailure(),
8061 };
8062}
8063
8064/// The reason for the double-queue here is that the first queue ensures any
8065/// resolve_type_fully tasks are complete before this dispatch function is called.
8066fn dispatchZcuLinkTask(comp: *Compilation, tid: usize, task: link.ZcuTask) void {
8067 if (!comp.separateCodegenThreadOk()) {
8068 assert(tid == 0);
8069 if (task == .link_func) {
8070 assert(task.link_func.mir.status.load(.monotonic) != .pending);
8071 }
8072 link.doZcuTask(comp, tid, task);
8073 task.deinit(comp.zcu.?);
8074 return;
8075 }
8076 comp.link_task_queue.enqueueZcu(comp, task) catch |err| switch (err) {
8077 error.OutOfMemory => {
8078 task.deinit(comp.zcu.?);
8079 comp.setAllocFailure();
8080 },
8081 };
8082}
8083
8084pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {
8085 return .{
8086 .full_object_path = .{
8087 .root_dir = comp.dirs.local_cache,
8088 .sub_path = try fs.path.join(comp.gpa, &.{
8089 "o",
8090 &Cache.binToHex(comp.digest.?),
8091 comp.emit_bin.?,
8092 }),
8093 },
8094 .lock = comp.cache_use.whole.moveLock(),
8095 };
8096}
8097
8098pub fn getCrtPaths(
8099 comp: *Compilation,
8100 arena: Allocator,
8101) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
8102 const target = &comp.root_mod.resolved_target.result;
8103 return getCrtPathsInner(arena, target, comp.config, comp.libc_installation, &comp.crt_files);
8104}
8105
8106fn getCrtPathsInner(
8107 arena: Allocator,
8108 target: *const std.Target,
8109 config: Config,
8110 libc_installation: ?*const LibCInstallation,
8111 crt_files: *std.StringHashMapUnmanaged(CrtFile),
8112) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
8113 const basenames = LibCInstallation.CrtBasenames.get(.{
8114 .target = target,
8115 .link_libc = config.link_libc,
8116 .output_mode = config.output_mode,
8117 .link_mode = config.link_mode,
8118 .pie = config.pie,
8119 });
8120 if (libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target);
8121
8122 return .{
8123 .crt0 = if (basenames.crt0) |basename| try crtFilePath(crt_files, basename) else null,
8124 .crti = if (basenames.crti) |basename| try crtFilePath(crt_files, basename) else null,
8125 .crtbegin = if (basenames.crtbegin) |basename| try crtFilePath(crt_files, basename) else null,
8126 .crtend = if (basenames.crtend) |basename| try crtFilePath(crt_files, basename) else null,
8127 .crtn = if (basenames.crtn) |basename| try crtFilePath(crt_files, basename) else null,
8128 };
8129}
8130
8131pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
8132 // Avoid deadlocking on building import libs such as kernel32.lib
8133 // This can happen when the user uses `build-exe foo.obj -lkernel32` and
8134 // then when we create a sub-Compilation for zig libc, it also tries to
8135 // build kernel32.lib.
8136 if (comp.skip_linker_dependencies) return;
8137 const target = &comp.root_mod.resolved_target.result;
8138 if (target.os.tag != .windows or target.ofmt == .c) return;
8139
8140 // This happens when an `extern "foo"` function is referenced.
8141 // If we haven't seen this library yet and we're targeting Windows, we need
8142 // to queue up a work item to produce the DLL import library for this.
8143 const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name);
8144 if (gop.found_existing) return;
8145 {
8146 errdefer _ = comp.windows_libs.pop();
8147 gop.key_ptr.* = try comp.gpa.dupe(u8, lib_name);
8148 }
8149 try comp.queueJob(.{ .windows_import_lib = gop.index });
8150}
8151
8152/// This decides the optimization mode for all zig-provided libraries, including
8153/// compiler-rt, libcxx, libc, libunwind, etc.
8154pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode {
8155 if (comp.debug_compiler_runtime_libs) {
8156 return .Debug;
8157 }
8158 const target = &comp.root_mod.resolved_target.result;
8159 switch (comp.root_mod.optimize_mode) {
8160 .Debug, .ReleaseSafe => return target_util.defaultCompilerRtOptimizeMode(target),
8161 .ReleaseFast => return .ReleaseFast,
8162 .ReleaseSmall => return .ReleaseSmall,
8163 }
8164}
8165
8166/// This decides whether to strip debug info for all zig-provided libraries, including
8167/// compiler-rt, libcxx, libc, libunwind, etc.
8168pub fn compilerRtStrip(comp: Compilation) bool {
8169 return comp.root_mod.strip;
8170}