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