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 };
2105 try zcu.init(gpa, io, options.thread_limit);
2106 break :blk zcu;
2107 } else blk: {
2108 if (options.emit_h != .no) return diag.fail(.emit_h_without_zcu);
2109 break :blk null;
2110 };
2111 errdefer if (opt_zcu) |zcu| zcu.deinit();
2112
2113 comp.* = .{
2114 .gpa = gpa,
2115 .arena = arena,
2116 .io = io,
2117 .thread_limit = options.thread_limit,
2118 .zcu = opt_zcu,
2119 .cache_use = undefined, // populated below
2120 .bin_file = null, // populated below if necessary
2121 .root_mod = options.root_mod,
2122 .config = options.config,
2123 .dirs = options.dirs,
2124 .c_object_work_queue = .empty,
2125 .win32_resource_work_queue = .empty,
2126 .c_source_files = options.c_source_files,
2127 .rc_source_files = options.rc_source_files,
2128 .cache_parent = cache,
2129 .self_exe_path = options.self_exe_path,
2130 .libc_include_dir_list = libc_dirs.libc_include_dir_list,
2131 .libc_framework_dir_list = libc_dirs.libc_framework_dir_list,
2132 .rc_includes = options.rc_includes,
2133 .mingw_unicode_entry_point = options.mingw_unicode_entry_point,
2134 .clang_passthrough_mode = options.clang_passthrough_mode,
2135 .clang_preprocessor_mode = options.clang_preprocessor_mode,
2136 .verbose_cc = options.verbose_cc,
2137 .verbose_air = options.verbose_air,
2138 .verbose_intern_pool = options.verbose_intern_pool,
2139 .verbose_generic_instances = options.verbose_generic_instances,
2140 .verbose_llvm_ir = options.verbose_llvm_ir,
2141 .verbose_llvm_bc = options.verbose_llvm_bc,
2142 .link_depfile = options.link_depfile,
2143 .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features,
2144 .verbose_link = options.verbose_link,
2145 .disable_c_depfile = options.disable_c_depfile,
2146 .reference_trace = options.reference_trace,
2147 .time_report = if (options.time_report) .init else null,
2148 .stack_report = options.stack_report,
2149 .test_filters = options.test_filters,
2150 .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs,
2151 .debug_compile_errors = options.debug_compile_errors,
2152 .debug_incremental = options.debug_incremental,
2153 .root_name = root_name,
2154 .sysroot = sysroot,
2155 .windows_libs = .empty,
2156 .windows_libs_num_done = 0,
2157 .version = options.version,
2158 .libc_installation = libc_dirs.libc_installation,
2159 .compiler_rt_strat = compiler_rt_strat,
2160 .ubsan_rt_strat = ubsan_rt_strat,
2161 .zigc_strat = zigc_strat,
2162 .link_inputs = options.link_inputs,
2163 .framework_dirs = options.framework_dirs,
2164 .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
2165 .skip_linker_dependencies = options.skip_linker_dependencies,
2166 .queued_jobs = .{},
2167 .function_sections = options.function_sections,
2168 .data_sections = options.data_sections,
2169 .native_system_include_paths = options.native_system_include_paths,
2170 .force_undefined_symbols = options.force_undefined_symbols,
2171 .link_eh_frame_hdr = link_eh_frame_hdr,
2172 .global_cc_argv = options.global_cc_argv,
2173 .file_system_inputs = options.file_system_inputs,
2174 .parent_whole_cache = options.parent_whole_cache,
2175 .link_diags = .init(gpa, io),
2176 .oneshot_prelink_tasks = .empty,
2177 .emit_bin = try options.emit_bin.resolve(arena, &options, .bin),
2178 .emit_asm = try options.emit_asm.resolve(arena, &options, .@"asm"),
2179 .emit_implib = try options.emit_implib.resolve(arena, &options, .implib),
2180 .emit_llvm_ir = try options.emit_llvm_ir.resolve(arena, &options, .llvm_ir),
2181 .emit_llvm_bc = try options.emit_llvm_bc.resolve(arena, &options, .llvm_bc),
2182 .emit_docs = try options.emit_docs.resolve(arena, &options, .docs),
2183 .environ_map = options.environ_map,
2184 };
2185
2186 errdefer {
2187 for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib);
2188 comp.windows_libs.deinit(gpa);
2189 }
2190 try comp.windows_libs.ensureUnusedCapacity(gpa, options.windows_lib_names.len);
2191 for (options.windows_lib_names) |windows_lib| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, windows_lib), {});
2192
2193 // Prevent some footguns by making the "any" fields of config reflect
2194 // the default Module settings.
2195 comp.config.any_unwind_tables = any_unwind_tables;
2196 comp.config.any_non_single_threaded = any_non_single_threaded;
2197 comp.config.any_sanitize_thread = any_sanitize_thread;
2198 comp.config.any_sanitize_c = any_sanitize_c;
2199 comp.config.any_fuzz = any_fuzz;
2200
2201 if (opt_zcu) |zcu| {
2202 // Finish initializing the `zcu` after the fields on `comp` have been initialized.
2203 zcu.initAfterCompilation();
2204
2205 // Populate `zcu.module_roots`.
2206 const active = zcu.acquire();
2207 defer active.release();
2208 active.pt.populateModuleRootTable() catch |err| switch (err) {
2209 error.OutOfMemory => |e| return e,
2210 error.IllegalZigImport => return diag.fail(.illegal_zig_import),
2211 };
2212 }
2213
2214 const lf_open_opts: link.File.OpenOptions = .{
2215 .linker_script = options.linker_script,
2216 .z_nodelete = options.linker_z_nodelete,
2217 .z_notext = options.linker_z_notext,
2218 .z_defs = options.linker_z_defs,
2219 .z_origin = options.linker_z_origin,
2220 .z_nocopyreloc = options.linker_z_nocopyreloc,
2221 .z_now = options.linker_z_now,
2222 .z_relro = options.linker_z_relro,
2223 .z_common_page_size = options.linker_z_common_page_size,
2224 .z_max_page_size = options.linker_z_max_page_size,
2225 .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
2226 .frameworks = options.frameworks,
2227 .lib_directories = options.lib_directories,
2228 .framework_dirs = options.framework_dirs,
2229 .rpath_list = options.rpath_list,
2230 .symbol_wrap_set = options.symbol_wrap_set,
2231 .repro = options.linker_repro orelse (options.root_mod.optimize_mode != .debug),
2232 .allow_shlib_undefined = options.linker_allow_shlib_undefined,
2233 .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
2234 .compress_debug_sections = options.linker_compress_debug_sections orelse .none,
2235 .module_definition_file = options.linker_module_definition_file,
2236 .sort_section = options.linker_sort_section,
2237 .import_symbols = options.linker_import_symbols,
2238 .import_table = options.linker_import_table,
2239 .export_table = options.linker_export_table,
2240 .growable_table = options.linker_growable_table,
2241 .initial_memory = options.linker_initial_memory,
2242 .max_memory = options.linker_max_memory,
2243 .global_base = options.linker_global_base,
2244 .export_symbol_names = options.linker_export_symbol_names,
2245 .print_gc_sections = options.linker_print_gc_sections,
2246 .print_icf_sections = options.linker_print_icf_sections,
2247 .print_map = options.linker_print_map,
2248 .nmagic = options.linker_nmagic,
2249 .fatal_warnings = options.linker_fatal_warnings,
2250 .tsaware = options.linker_tsaware,
2251 .nxcompat = options.linker_nxcompat,
2252 .dynamicbase = options.linker_dynamicbase,
2253 .major_subsystem_version = options.major_subsystem_version,
2254 .minor_subsystem_version = options.minor_subsystem_version,
2255 .entry = options.entry,
2256 .stack_size = options.stack_size,
2257 .image_base = options.image_base,
2258 .version_script = options.version_script,
2259 .allow_undefined_version = options.linker_allow_undefined_version,
2260 .enable_new_dtags = options.linker_enable_new_dtags,
2261 .gc_sections = options.linker_gc_sections,
2262 .emit_relocs = options.link_emit_relocs,
2263 .soname = options.soname,
2264 .compatibility_version = options.compatibility_version,
2265 .build_id = build_id,
2266 .subsystem = options.subsystem,
2267 .hash_style = options.hash_style,
2268 .enable_link_snapshots = options.enable_link_snapshots,
2269 .install_name = options.install_name,
2270 .entitlements = options.entitlements,
2271 .pagezero_size = options.pagezero_size,
2272 .headerpad_size = options.headerpad_size,
2273 .headerpad_max_install_names = options.headerpad_max_install_names,
2274 .dead_strip_dylibs = options.dead_strip_dylibs,
2275 .force_load_objc = options.force_load_objc,
2276 .discard_local_symbols = options.discard_local_symbols,
2277 .pdb_source_path = options.pdb_source_path,
2278 .pdb_out_path = options.pdb_out_path,
2279 .entry_addr = null, // CLI does not expose this option (yet?)
2280 .object_host_name = "env",
2281 };
2282
2283 switch (options.cache_mode) {
2284 .none => {
2285 const none = try arena.create(CacheUse.None);
2286 none.* = .{ .tmp_artifact_directory = null };
2287 comp.cache_use = .{ .none = none };
2288 if (comp.emit_bin) |path| {
2289 comp.bin_file = link.File.open(arena, comp, .{
2290 .root_dir = .cwd(),
2291 .sub_path = path,
2292 }, lf_open_opts) catch |err| {
2293 return diag.fail(.{ .open_output_bin = err });
2294 };
2295 }
2296 },
2297 .incremental => {
2298 // Options that are specific to zig source files, that cannot be
2299 // modified between incremental updates.
2300 var hash = cache.hash;
2301
2302 // Synchronize with other matching comments: ZigOnlyHashStuff
2303 hash.add(use_llvm);
2304 hash.add(options.config.use_lib_llvm);
2305 hash.add(options.config.use_lld);
2306 hash.add(options.config.use_new_linker);
2307 hash.add(options.config.dll_export_fns);
2308 hash.add(options.config.is_test);
2309 hash.addListOfBytes(options.test_filters);
2310 hash.add(options.skip_linker_dependencies);
2311 hash.add(options.emit_h != .no);
2312 hash.add(error_limit);
2313
2314 // Here we put the root source file path name, but *not* with addFile.
2315 // We want the hash to be the same regardless of the contents of the
2316 // source file, because incremental compilation will handle it, but we
2317 // do want to namespace different source file names because they are
2318 // likely different compilations and therefore this would be likely to
2319 // cause cache hits.
2320 if (comp.zcu) |zcu| {
2321 try addModuleTableToCacheHash(zcu, &hash);
2322 } else {
2323 cache_helpers.addModule(&hash, options.root_mod);
2324 }
2325
2326 // In the case of incremental cache mode, this `artifact_directory`
2327 // is computed based on a hash of non-linker inputs, and it is where all
2328 // build artifacts are stored (even while in-progress).
2329 comp.digest = hash.peekBin();
2330 const digest = hash.final();
2331
2332 const artifact_sub_dir = "o" ++ fs.path.sep_str ++ digest;
2333 var artifact_dir = options.dirs.local_cache.handle.createDirPathOpen(io, artifact_sub_dir, .{}) catch |err| {
2334 return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = artifact_sub_dir, .err = err } });
2335 };
2336 errdefer artifact_dir.close(io);
2337 const artifact_directory: Cache.Directory = .{
2338 .handle = artifact_dir,
2339 .path = try options.dirs.local_cache.join(arena, &.{artifact_sub_dir}),
2340 };
2341
2342 const incremental = try arena.create(CacheUse.Incremental);
2343 incremental.* = .{
2344 .artifact_directory = artifact_directory,
2345 };
2346 comp.cache_use = .{ .incremental = incremental };
2347
2348 if (comp.emit_bin) |cache_rel_path| {
2349 const emit: Cache.Path = .{
2350 .root_dir = artifact_directory,
2351 .sub_path = cache_rel_path,
2352 };
2353 comp.bin_file = link.File.open(arena, comp, emit, lf_open_opts) catch |err| {
2354 return diag.fail(.{ .open_output_bin = err });
2355 };
2356 }
2357 },
2358 .whole => {
2359 // For whole cache mode, we don't know where to put outputs from the linker until
2360 // the final cache hash, which is available after the compilation is complete.
2361 //
2362 // Therefore, `comp.bin_file` is left `null` (already done) until `update`, where
2363 // it may find a cache hit, or else will use a temporary directory to hold output
2364 // artifacts.
2365 const whole = try arena.create(CacheUse.Whole);
2366 whole.* = .{
2367 .lf_open_opts = lf_open_opts,
2368 .cache_manifest = null,
2369 .cache_manifest_mutex = .init,
2370 .tmp_artifact_directory = null,
2371 .lock = null,
2372 };
2373 comp.cache_use = .{ .whole = whole };
2374 },
2375 }
2376
2377 if (use_llvm and
2378 (comp.emit_bin != null or
2379 comp.emit_asm != null or
2380 comp.emit_llvm_ir != null or
2381 comp.emit_llvm_bc != null or
2382 comp.verbose_llvm_ir != null or
2383 comp.verbose_llvm_bc != null))
2384 {
2385 if (opt_zcu) |zcu| {
2386 zcu.llvm_object = try LlvmObject.create(arena, zcu);
2387 }
2388 }
2389
2390 break :comp comp;
2391 };
2392 errdefer comp.destroy();
2393
2394 if (target.ofmt == .c) return comp;
2395
2396 // Add a `CObject` for each `c_source_files`.
2397 try comp.c_objects.ensureTotalCapacity(gpa, options.c_source_files.len);
2398 for (options.c_source_files) |c_source_file| {
2399 const c_object = try gpa.create(CObject);
2400 errdefer gpa.destroy(c_object);
2401
2402 c_object.* = .{
2403 .status = .{ .new = {} },
2404 .src = c_source_file,
2405 };
2406 comp.c_objects.appendAssumeCapacity(c_object);
2407 }
2408
2409 // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`.
2410 const win32_resource_count =
2411 options.rc_source_files.len + @intFromBool(options.manifest_file != null);
2412 if (win32_resource_count > 0) {
2413 dev.check(.win32_resource);
2414 try comp.win32_resources.ensureTotalCapacity(gpa, win32_resource_count);
2415 for (options.rc_source_files) |rc_source_file| {
2416 const win32_resource = try gpa.create(Win32Resource);
2417 errdefer gpa.destroy(win32_resource);
2418
2419 win32_resource.* = .{
2420 .status = .{ .new = {} },
2421 .src = .{ .rc = rc_source_file },
2422 };
2423 comp.win32_resources.appendAssumeCapacity(win32_resource);
2424 }
2425
2426 if (options.manifest_file) |manifest_path| {
2427 const win32_resource = try gpa.create(Win32Resource);
2428 errdefer gpa.destroy(win32_resource);
2429
2430 win32_resource.* = .{
2431 .status = .{ .new = {} },
2432 .src = .{ .manifest = manifest_path },
2433 };
2434 comp.win32_resources.appendAssumeCapacity(win32_resource);
2435 }
2436 }
2437
2438 if (comp.emit_bin != null) {
2439 if (!comp.skip_linker_dependencies) {
2440 // If we need to build libc for the target, add work items for it.
2441 // We go through the work queue so that building can be done in parallel.
2442 // If linking against host libc installation, instead queue up jobs
2443 // for loading those files in the linker.
2444 if (comp.config.link_libc and is_exe_or_dyn_lib) {
2445 // If the "is darwin" check is moved below the libc_installation check below,
2446 // error.LibCInstallationMissingCrtDir is returned from lci.resolveCrtPaths().
2447 if (target.isDarwinLibC()) {
2448 // TODO delete logic from MachO flush() and queue up tasks here instead.
2449 } else if (comp.libc_installation) |lci| {
2450 const basenames = LibCInstallation.CrtBasenames.get(.{
2451 .target = target,
2452 .link_libc = comp.config.link_libc,
2453 .output_mode = comp.config.output_mode,
2454 .link_mode = comp.config.link_mode,
2455 .pie = comp.config.pie,
2456 });
2457 const paths = lci.resolveCrtPaths(arena, basenames, target) catch |err| switch (err) {
2458 error.OutOfMemory => |e| return e,
2459 error.LibCInstallationMissingCrtDir => return diag.fail(.libc_installation_missing_crt_dir),
2460 };
2461
2462 const field_names = @typeInfo(@TypeOf(paths)).@"struct".field_names;
2463 try comp.oneshot_prelink_tasks.ensureUnusedCapacity(gpa, field_names.len + 1);
2464 inline for (field_names) |field_name| {
2465 if (@field(paths, field_name)) |path| {
2466 comp.oneshot_prelink_tasks.appendAssumeCapacity(.{ .load_object = path });
2467 }
2468 }
2469 // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.
2470 comp.oneshot_prelink_tasks.appendAssumeCapacity(.load_host_libc);
2471 } else if (target.isMuslLibC()) {
2472 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2473
2474 if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| {
2475 comp.queued_jobs.musl_crt_file[@backingInt(f)] = true;
2476 }
2477 switch (comp.config.link_mode) {
2478 .static => comp.queued_jobs.musl_crt_file[@backingInt(musl.CrtFile.libc_a)] = true,
2479 .dynamic => comp.queued_jobs.musl_crt_file[@backingInt(musl.CrtFile.libc_so)] = true,
2480 }
2481 } else if (target.isGnuLibC()) {
2482 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2483
2484 if (glibc.needsCrt0(comp.config.output_mode)) |f| {
2485 comp.queued_jobs.glibc_crt_file[@backingInt(f)] = true;
2486 }
2487 comp.queued_jobs.glibc_shared_objects = true;
2488
2489 comp.queued_jobs.glibc_crt_file[@backingInt(glibc.CrtFile.libc_nonshared_a)] = true;
2490 } else if (target.isFreeBSDLibC()) {
2491 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2492
2493 if (freebsd.needsCrt0(comp.config.output_mode)) |f| {
2494 comp.queued_jobs.freebsd_crt_file[@backingInt(f)] = true;
2495 }
2496
2497 comp.queued_jobs.freebsd_shared_objects = true;
2498 } else if (target.isNetBSDLibC()) {
2499 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2500
2501 if (netbsd.needsCrt0(comp.config.output_mode)) |f| {
2502 comp.queued_jobs.netbsd_crt_file[@backingInt(f)] = true;
2503 }
2504
2505 comp.queued_jobs.netbsd_shared_objects = true;
2506 } else if (target.isOpenBSDLibC()) {
2507 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2508
2509 if (openbsd.needsCrt0(comp.config.output_mode)) |f| {
2510 comp.queued_jobs.openbsd_crt_file[@backingInt(f)] = true;
2511 }
2512
2513 comp.queued_jobs.openbsd_shared_objects = true;
2514 } else if (target.isWasiLibC()) {
2515 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2516
2517 comp.queued_jobs.wasi_libc_crt_file[@backingInt(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true;
2518 comp.queued_jobs.wasi_libc_crt_file[@backingInt(wasi_libc.CrtFile.libc_a)] = true;
2519 } else if (target.isMinGW()) {
2520 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2521
2522 const main_crt_file: mingw.CrtFile = if (is_dyn_lib) .dllcrt2_o else .crt2_o;
2523 comp.queued_jobs.mingw_crt_file[@backingInt(main_crt_file)] = true;
2524 comp.queued_jobs.mingw_crt_file[@backingInt(mingw.CrtFile.libmingw32_lib)] = true;
2525
2526 // When linking mingw-w64 there are some import libs we always need.
2527 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
2528 for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, name), {});
2529 } else {
2530 return diag.fail(.cross_libc_unavailable);
2531 }
2532 }
2533
2534 if (comp.wantBuildLibUnwindFromSource()) {
2535 comp.queued_jobs.libunwind = true;
2536 }
2537 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
2538 comp.queued_jobs.libcxx = true;
2539 comp.queued_jobs.libcxxabi = true;
2540 }
2541 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) {
2542 comp.queued_jobs.libtsan = true;
2543 }
2544
2545 switch (comp.compiler_rt_strat) {
2546 .none, .zcu => {},
2547 .lib => {
2548 log.debug("queuing a job to build compiler_rt_lib", .{});
2549 comp.queued_jobs.compiler_rt_lib = true;
2550 },
2551 .obj => {
2552 log.debug("queuing a job to build compiler_rt_obj", .{});
2553 comp.queued_jobs.compiler_rt_obj = true;
2554 },
2555 }
2556
2557 switch (comp.ubsan_rt_strat) {
2558 .none, .zcu => {},
2559 .lib => {
2560 log.debug("queuing a job to build ubsan_rt_lib", .{});
2561 comp.queued_jobs.ubsan_rt_lib = true;
2562 },
2563 .obj => {
2564 log.debug("queuing a job to build ubsan_rt_obj", .{});
2565 comp.queued_jobs.ubsan_rt_obj = true;
2566 },
2567 }
2568
2569 switch (comp.zigc_strat) {
2570 .none, .zcu => {},
2571 .lib => {
2572 log.debug("queuing a job to build libzigc", .{});
2573 comp.queued_jobs.zigc_lib = true;
2574 },
2575 .obj => unreachable, // only available as a static library or inside an existing ZCU
2576 }
2577
2578 if (is_exe_or_dyn_lib and comp.config.any_fuzz) {
2579 log.debug("queuing a job to build libfuzzer", .{});
2580 comp.queued_jobs.fuzzer_lib = true;
2581 }
2582 }
2583
2584 try comp.oneshot_prelink_tasks.append(gpa, .load_explicitly_provided);
2585 }
2586 log.debug("queued oneshot prelink tasks: {d}", .{comp.oneshot_prelink_tasks.items.len});
2587 return comp;
2588}
2589
2590fn printVerboseLlvmCpuFeatures(
2591 w: *Writer,
2592 arena: Allocator,
2593 root_name: []const u8,
2594 target: *const std.Target,
2595 cf: [*:0]const u8,
2596) (Writer.Error || Allocator.Error)!void {
2597 try w.print("compilation: {s}\n", .{root_name});
2598 try w.print(" target: {s}\n", .{try target.zigTriple(arena)});
2599 try w.print(" cpu: {s}\n", .{target.cpu.model.name});
2600 try w.print(" features: {s}\n", .{cf});
2601}
2602
2603pub fn destroy(comp: *Compilation) void {
2604 const gpa = comp.gpa;
2605 const io = comp.io;
2606
2607 if (comp.bin_file) |lf| lf.destroy();
2608 if (comp.zcu) |zcu| zcu.deinit();
2609 comp.cache_use.deinit(io);
2610
2611 comp.c_object_work_queue.deinit(gpa);
2612 comp.win32_resource_work_queue.deinit(gpa);
2613
2614 for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib);
2615 comp.windows_libs.deinit(gpa);
2616
2617 {
2618 var it = comp.crt_files.iterator();
2619 while (it.next()) |entry| {
2620 gpa.free(entry.key_ptr.*);
2621 entry.value_ptr.deinit(gpa, io);
2622 }
2623 comp.crt_files.deinit(gpa);
2624 }
2625 if (comp.libcxx_static_lib) |*crt_file| crt_file.deinit(gpa, io);
2626 if (comp.libcxxabi_static_lib) |*crt_file| crt_file.deinit(gpa, io);
2627 if (comp.libunwind_static_lib) |*crt_file| crt_file.deinit(gpa, io);
2628 if (comp.tsan_lib) |*crt_file| crt_file.deinit(gpa, io);
2629 if (comp.ubsan_rt_lib) |*crt_file| crt_file.deinit(gpa, io);
2630 if (comp.ubsan_rt_obj) |*crt_file| crt_file.deinit(gpa, io);
2631 if (comp.zigc_static_lib) |*crt_file| crt_file.deinit(gpa, io);
2632 if (comp.compiler_rt_lib) |*crt_file| crt_file.deinit(gpa, io);
2633 if (comp.compiler_rt_obj) |*crt_file| crt_file.deinit(gpa, io);
2634 if (comp.fuzzer_lib) |*crt_file| crt_file.deinit(gpa, io);
2635
2636 if (comp.glibc_so_files) |*glibc_file| {
2637 glibc_file.deinit(gpa, io);
2638 }
2639
2640 if (comp.freebsd_so_files) |*freebsd_file| {
2641 freebsd_file.deinit(gpa, io);
2642 }
2643
2644 if (comp.netbsd_so_files) |*netbsd_file| {
2645 netbsd_file.deinit(gpa, io);
2646 }
2647
2648 if (comp.openbsd_so_files) |*openbsd_file| {
2649 openbsd_file.deinit(gpa, io);
2650 }
2651
2652 for (comp.c_objects.items) |c_object| {
2653 c_object.destroy(gpa, io);
2654 }
2655 comp.c_objects.deinit(gpa);
2656
2657 for (comp.failed_c_objects.values()) |bundle| {
2658 bundle.destroy(gpa);
2659 }
2660 comp.failed_c_objects.deinit(gpa);
2661
2662 for (comp.win32_resources.items) |win32_resource| {
2663 win32_resource.destroy(gpa, io);
2664 }
2665 comp.win32_resources.deinit(gpa);
2666
2667 for (comp.failed_win32_resources.values()) |*value| {
2668 value.deinit(gpa);
2669 }
2670 comp.failed_win32_resources.deinit(gpa);
2671
2672 if (comp.time_report) |*tr| tr.deinit(gpa);
2673
2674 comp.link_diags.deinit();
2675 comp.oneshot_prelink_tasks.deinit(gpa);
2676
2677 comp.clearMiscFailures();
2678
2679 comp.cache_parent.manifest_dir.close(io);
2680}
2681
2682pub fn clearMiscFailures(comp: *Compilation) void {
2683 comp.alloc_failure_occurred = false;
2684 comp.link_diags.flags = .{};
2685 for (comp.misc_failures.values()) |*value| {
2686 value.deinit(comp.gpa);
2687 }
2688 comp.misc_failures.deinit(comp.gpa);
2689 comp.misc_failures = .{};
2690}
2691
2692pub fn getTarget(self: *const Compilation) *const Target {
2693 return &self.root_mod.resolved_target.result;
2694}
2695
2696/// Only legal to call when cache mode is incremental and a link file is present.
2697pub fn hotCodeSwap(
2698 comp: *Compilation,
2699 prog_node: std.Progress.Node,
2700 pid: std.process.Child.Id,
2701) !void {
2702 const lf = comp.bin_file.?;
2703 lf.child_pid = pid;
2704 try lf.makeWritable();
2705 try comp.update(prog_node);
2706 try lf.makeExecutable();
2707}
2708
2709fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
2710 const io = comp.io;
2711
2712 switch (comp.cache_use) {
2713 .none => |none| {
2714 if (none.tmp_artifact_directory) |*tmp_dir| {
2715 tmp_dir.handle.close(io);
2716 none.tmp_artifact_directory = null;
2717 if (dev.env == .bootstrap) {
2718 // zig1 uses `CacheMode.none`, but it doesn't need to know how to delete
2719 // temporary directories; it doesn't have a real cache directory anyway.
2720 return;
2721 }
2722 // Usually, we want to delete the temporary directory. However, if we are emitting
2723 // an unstripped Mach-O binary with the LLVM backend, then the temporary directory
2724 // contains the ZCU object file emitted by LLVM, which contains debug symbols not
2725 // replicated in the output binary (the output instead contains a reference to that
2726 // file which debug tooling can look through). So, in that particular case, we need
2727 // to keep this directory around so that the output binary can be debugged.
2728 if (comp.bin_file != null and comp.getTarget().ofmt == .macho and comp.config.debug_format != .strip) {
2729 // We are emitting an unstripped Mach-O binary with the LLVM backend: the ZCU
2730 // object file must remain on-disk for its debug info.
2731 return;
2732 }
2733 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2734 comp.dirs.local_cache.handle.deleteTree(io, tmp_dir_sub_path) catch |err| {
2735 log.warn("failed to delete temporary directory '{s}{c}{s}': {t}", .{
2736 comp.dirs.local_cache.path orelse ".", fs.path.sep, tmp_dir_sub_path, err,
2737 });
2738 };
2739 }
2740 },
2741 .incremental => return,
2742 .whole => |whole| {
2743 if (whole.cache_manifest) |man| {
2744 man.deinit();
2745 whole.cache_manifest = null;
2746 }
2747 if (comp.bin_file) |lf| {
2748 lf.destroy();
2749 comp.bin_file = null;
2750 }
2751 if (whole.tmp_artifact_directory) |*tmp_dir| {
2752 tmp_dir.handle.close(io);
2753 whole.tmp_artifact_directory = null;
2754 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2755 comp.dirs.local_cache.handle.deleteTree(io, tmp_dir_sub_path) catch |err| {
2756 log.warn("failed to delete temporary directory '{s}{c}{s}': {t}", .{
2757 comp.dirs.local_cache.path orelse ".", fs.path.sep, tmp_dir_sub_path, err,
2758 });
2759 };
2760 }
2761 },
2762 }
2763}
2764
2765pub const UpdateError = error{
2766 OutOfMemory,
2767 Canceled,
2768 Unexpected,
2769};
2770
2771/// Detect changes to source files, perform semantic analysis, and update the output files.
2772pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateError!void {
2773 const tracy_frame = tracy.namedFrame(comp.root_name);
2774 defer tracy_frame.end();
2775
2776 const gpa = comp.gpa;
2777 const io = comp.io;
2778
2779 // This arena is scoped to this one update.
2780 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
2781 defer arena_allocator.deinit();
2782 const arena = arena_allocator.allocator();
2783
2784 comp.clearMiscFailures();
2785 comp.last_update_was_cache_hit = false;
2786 if (comp.time_report) |*tr| {
2787 tr.deinit(gpa); // this is information about an old update
2788 tr.* = .init;
2789 }
2790
2791 var tmp_dir_rand_int: u64 = undefined;
2792 var man: Cache.Manifest = undefined;
2793 defer cleanupAfterUpdate(comp, tmp_dir_rand_int);
2794
2795 // If using the whole caching strategy, we check for *everything* up front, including
2796 // C source files.
2797 log.debug("Compilation.update for {s}, CacheMode.{t}", .{ comp.root_name, comp.cache_use });
2798 switch (comp.cache_use) {
2799 .none => |none| {
2800 assert(none.tmp_artifact_directory == null);
2801 none.tmp_artifact_directory = d: {
2802 io.random(@ptrCast(&tmp_dir_rand_int));
2803 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2804 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
2805 const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {
2806 return comp.setMiscFailure(.open_output, "failed to create output directory {q}: {t}", .{
2807 path, err,
2808 });
2809 };
2810 break :d .{ .path = path, .handle = handle };
2811 };
2812 },
2813 .incremental => {},
2814 .whole => |whole| {
2815 assert(comp.bin_file == null);
2816 // We are about to obtain this lock, so here we give other processes a chance first.
2817 whole.releaseLock(io);
2818
2819 man = comp.cache_parent.obtain();
2820 whole.cache_manifest = &man;
2821 try addNonIncrementalStuffToCacheManifest(comp, &man);
2822
2823 // Under `--time-report`, ignore cache hits; do the work anyway for those juicy numbers.
2824 const ignore_hit = comp.time_report != null;
2825
2826 if (ignore_hit) {
2827 // We're going to do the work regardless of whether this is a hit or a miss.
2828 man.want_shared_lock = false;
2829 }
2830
2831 const is_hit = man.hit(main_progress_node) catch |err| switch (err) {
2832 error.CacheCheckFailed => switch (man.diagnostic) {
2833 .none => unreachable,
2834 .manifest_create, .manifest_read, .manifest_lock => |e| return comp.setMiscFailure(
2835 .check_whole_cache,
2836 "failed to check cache: {t} {t}",
2837 .{ man.diagnostic, e },
2838 ),
2839 .file_open, .file_stat, .file_read, .file_hash => |op| {
2840 const pp = man.files.keys()[op.file_index].prefixed_path;
2841 const prefix = man.cache.prefixes()[pp.prefix];
2842 return comp.setMiscFailure(.check_whole_cache, "failed to check cache: {f}{s} {t} {t}", .{
2843 prefix, pp.sub_path, man.diagnostic, op.err,
2844 });
2845 },
2846 },
2847 error.OutOfMemory, error.Canceled => |e| return e,
2848 error.InvalidFormat => return comp.setMiscFailure(
2849 .check_whole_cache,
2850 "failed to check cache: invalid manifest file format",
2851 .{},
2852 ),
2853 };
2854 if (is_hit and !ignore_hit) {
2855 // In this case the cache hit contains the full set of file system inputs. Nice!
2856 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
2857 if (comp.parent_whole_cache) |pwc| {
2858 try pwc.mutex.lock(io);
2859 defer pwc.mutex.unlock(io);
2860 try man.populateOtherManifest(pwc.manifest, pwc.prefix_map);
2861 }
2862
2863 comp.last_update_was_cache_hit = true;
2864 log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name});
2865 const bin_digest = man.finalBin();
2866
2867 comp.digest = bin_digest;
2868
2869 assert(whole.lock == null);
2870 whole.lock = man.toOwnedLock();
2871 return;
2872 }
2873 log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name});
2874
2875 if (ignore_hit) {
2876 // Okay, now set this back so that `writeManifest` will downgrade our lock later.
2877 man.want_shared_lock = true;
2878 }
2879
2880 // Compile the artifacts to a temporary directory.
2881 whole.tmp_artifact_directory = d: {
2882 io.random(@ptrCast(&tmp_dir_rand_int));
2883 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2884 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
2885 const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {
2886 return comp.setMiscFailure(.open_output, "failed to create output directory '{s}': {t}", .{ path, err });
2887 };
2888 break :d .{ .path = path, .handle = handle };
2889 };
2890 if (comp.emit_bin) |sub_path| {
2891 const emit: Cache.Path = .{
2892 .root_dir = whole.tmp_artifact_directory.?,
2893 .sub_path = sub_path,
2894 };
2895 comp.bin_file = link.File.createEmpty(arena, comp, emit, whole.lf_open_opts) catch |err| {
2896 return comp.setMiscFailure(.open_output, "failed to open output file '{f}': {t}", .{ emit, err });
2897 };
2898 }
2899 },
2900 }
2901
2902 // From this point we add a preliminary set of file system inputs that
2903 // affects both incremental and whole cache mode. For incremental cache
2904 // mode, the long-lived compiler state will track additional file system
2905 // inputs discovered after this point. For whole cache mode, we rely on
2906 // these inputs to make it past AstGen, and once there, we can rely on
2907 // learning file system inputs from the Cache object.
2908
2909 // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
2910 // Add a Job for each C object.
2911 if (comp.bin_file != null and comp.bin_file.?.post_prelink) {
2912 assert(comp.config.incremental);
2913 // TODO: this indicates that we are using incremental compilation and this is not the first
2914 // incremental update. The incremental linkers do not (currently?) support updating C inputs
2915 // incrementally. The frontend needs to learn to trigger a full rebuild if a C link input
2916 // changes. For now, to avoid crashing the linker in this case, don't kick off C object
2917 // updates if we've done prelink already. https://codeberg.org/ziglang/zig/issues/32081
2918 } else {
2919 try comp.c_object_work_queue.ensureUnusedCapacity(gpa, comp.c_objects.items.len);
2920 for (comp.c_objects.items) |c_object| {
2921 comp.c_object_work_queue.pushBackAssumeCapacity(c_object);
2922 try comp.appendFileSystemInput(try .fromUnresolved(arena, comp.dirs, &.{c_object.src.src_path}));
2923 }
2924 }
2925
2926 for (comp.link_inputs) |input| if (input.path()) |path| {
2927 try comp.appendFileSystemInput(try .fromUnresolved(arena, comp.dirs, &.{
2928 path.root_dir.path orelse ".",
2929 path.sub_path,
2930 }));
2931 };
2932
2933 // For compiling Win32 resources, we rely on the cache hash system to avoid duplicating work.
2934 // Add a Job for each Win32 resource file.
2935 try comp.win32_resource_work_queue.ensureUnusedCapacity(gpa, comp.win32_resources.items.len);
2936 for (comp.win32_resources.items) |win32_resource| {
2937 comp.win32_resource_work_queue.pushBackAssumeCapacity(win32_resource);
2938 switch (win32_resource.src) {
2939 .rc => |f| {
2940 try comp.appendFileSystemInput(try .fromUnresolved(arena, comp.dirs, &.{f.src_path}));
2941 },
2942 .manifest => {},
2943 }
2944 }
2945
2946 if (comp.zcu) |zcu| {
2947 assert(zcu.cur_analysis_timer == null);
2948
2949 zcu.skip_analysis_this_update = false;
2950
2951 // TODO: doing this in `resolveReferences` later could avoid adding inputs for dead embedfiles. Investigate!
2952 for (zcu.embed_table.keys()) |embed_file| {
2953 try comp.appendFileSystemInput(embed_file.path);
2954 }
2955
2956 zcu.analysis_roots_len = 0;
2957
2958 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.std_mod;
2959 zcu.analysis_roots_len += 1;
2960
2961 // Normally we rely on importing std to in turn import the root source file in the start code.
2962 // However, the main module is distinct from the root module in tests, so that won't happen there.
2963 if (comp.config.is_test and zcu.main_mod != zcu.std_mod) {
2964 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.main_mod;
2965 zcu.analysis_roots_len += 1;
2966 }
2967
2968 if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
2969 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = compiler_rt_mod;
2970 zcu.analysis_roots_len += 1;
2971 }
2972
2973 if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| {
2974 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = ubsan_rt_mod;
2975 zcu.analysis_roots_len += 1;
2976 }
2977
2978 if (zcu.root_mod.deps.get("zigc")) |zigc_mod| {
2979 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zigc_mod;
2980 zcu.analysis_roots_len += 1;
2981 }
2982 }
2983
2984 // The linker progress node is set up here instead of in `performAllTheWork`, because
2985 // we also want it around during `flush`.
2986 if (comp.bin_file) |lf| {
2987 // mirrors logic in `Compilation.flush`:
2988 // Always: linker flush
2989 var initial_estimated_total: usize = 1;
2990 const llvm = if (comp.zcu) |zcu| zcu.llvm_object != null else false;
2991 // For llvm: "LLVM Emit Object" and "Parse Object" with the zcu object
2992 if (llvm) {
2993 initial_estimated_total += 2;
2994 }
2995 // Prelink
2996 if (!lf.post_prelink or llvm) {
2997 initial_estimated_total += 1;
2998 }
2999
3000 comp.link_prog_node = main_progress_node.start("Linking", initial_estimated_total);
3001 lf.startProgress(comp.link_prog_node);
3002 }
3003 defer if (comp.bin_file) |lf| {
3004 lf.endProgress();
3005 comp.link_prog_node.end();
3006 comp.link_prog_node = .none;
3007 };
3008
3009 try comp.performAllTheWork(main_progress_node, arena);
3010
3011 if (comp.zcu) |zcu| {
3012 const active = zcu.acquire();
3013 defer active.release();
3014 const pt = active.pt;
3015
3016 assert(zcu.cur_analysis_timer == null);
3017
3018 if (!zcu.skip_analysis_this_update) {
3019 if (comp.config.is_test) {
3020 // The `test_functions` decl has been intentionally postponed until now,
3021 // at which point we must populate it with the list of test functions that
3022 // have been discovered and not filtered out.
3023 try pt.populateTestFunctions();
3024 }
3025
3026 link.updateErrorData(pt);
3027
3028 try pt.processExports();
3029 }
3030
3031 if (build_options.enable_debug_extensions and comp.verbose_intern_pool) {
3032 std.debug.print("intern pool stats for '{s}':\n", .{comp.root_name});
3033 zcu.intern_pool.dump();
3034 }
3035
3036 if (build_options.enable_debug_extensions and comp.verbose_generic_instances) {
3037 std.debug.print("generic instances for '{s}:0x{x}':\n", .{ comp.root_name, @intFromPtr(zcu) });
3038 zcu.intern_pool.dumpGenericInstances(gpa);
3039 }
3040 }
3041
3042 if (comp.link_depfile) |depfile_path| if (comp.bin_file) |lf| {
3043 assert(comp.file_system_inputs != null);
3044 comp.createDepFile(depfile_path, lf.emit) catch |err| comp.setMiscFailure(
3045 .link_depfile,
3046 "unable to write linker dependency file: {t}",
3047 .{err},
3048 );
3049 };
3050
3051 if (anyErrors(comp)) {
3052 // Skip flushing and keep source files loaded for error reporting.
3053 return;
3054 }
3055
3056 if (comp.zcu == null and comp.config.output_mode == .Obj and comp.c_objects.items.len == 1) {
3057 // This is `zig build-obj foo.c`. We can emit asm and LLVM IR/bitcode.
3058 const c_obj_path = comp.c_objects.items[0].status.success.object_path;
3059 if (comp.emit_asm) |path| try comp.emitFromCObject(arena, c_obj_path, ".s", path);
3060 if (comp.emit_llvm_ir) |path| try comp.emitFromCObject(arena, c_obj_path, ".ll", path);
3061 if (comp.emit_llvm_bc) |path| try comp.emitFromCObject(arena, c_obj_path, ".bc", path);
3062 }
3063
3064 switch (comp.cache_use) {
3065 .none, .incremental => try flush(comp, arena),
3066 .whole => |whole| {
3067 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
3068 if (comp.parent_whole_cache) |pwc| {
3069 try pwc.mutex.lock(io);
3070 defer pwc.mutex.unlock(io);
3071 try man.populateOtherManifest(pwc.manifest, pwc.prefix_map);
3072 }
3073
3074 const bin_digest = man.finalBin();
3075 const hex_digest = Cache.binToHex(bin_digest);
3076
3077 // Work around windows `AccessDenied` if any files within this
3078 // directory are open by closing and reopening the file handles.
3079 const need_writable_dance: enum { no, lf_only, lf_and_debug } = w: {
3080 if (builtin.os.tag == .windows) {
3081 if (comp.bin_file) |lf| {
3082 // We cannot just call `makeExecutable` as it makes a false
3083 // assumption that we have a file handle open only when linking
3084 // an executable file. This used to be true when our linkers
3085 // were incapable of emitting relocatables and static archive.
3086 // Now that they are capable, we need to unconditionally close
3087 // the file handle and re-open it in the follow up call to
3088 // `makeWritable`.
3089 if (lf.file) |f| {
3090 f.close(io);
3091 lf.file = null;
3092
3093 if (lf.closeDebugInfo()) break :w .lf_and_debug;
3094 break :w .lf_only;
3095 }
3096 }
3097 }
3098 break :w .no;
3099 };
3100
3101 // Rename the temporary directory into place.
3102 // Close tmp dir and link.File to avoid open handle during rename.
3103 whole.tmp_artifact_directory.?.handle.close(io);
3104 whole.tmp_artifact_directory = null;
3105 const s = fs.path.sep_str;
3106 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int);
3107 const o_sub_path = "o" ++ s ++ hex_digest;
3108 renameTmpIntoCache(io, comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| {
3109 return comp.setMiscFailure(
3110 .rename_results,
3111 "failed to rename compilation results ('{f}{s}') into local cache ('{f}{s}'): {t}",
3112 .{
3113 comp.dirs.local_cache, tmp_dir_sub_path,
3114 comp.dirs.local_cache, o_sub_path,
3115 err,
3116 },
3117 );
3118 };
3119 comp.digest = bin_digest;
3120
3121 // The linker flush functions need to know the final output path
3122 // for debug info purposes because executable debug info contains
3123 // references object file paths.
3124 if (comp.bin_file) |lf| {
3125 lf.emit = .{
3126 .root_dir = comp.dirs.local_cache,
3127 .sub_path = try fs.path.join(arena, &.{ o_sub_path, comp.emit_bin.? }),
3128 };
3129 const result: (link.File.OpenError || error{HotSwapUnavailableOnHostOperatingSystem})!void = switch (need_writable_dance) {
3130 .no => {},
3131 .lf_only => lf.makeWritable(),
3132 .lf_and_debug => res: {
3133 lf.makeWritable() catch |err| break :res err;
3134 lf.reopenDebugInfo() catch |err| break :res err;
3135 },
3136 };
3137 result catch |err| {
3138 return comp.setMiscFailure(
3139 .rename_results,
3140 "failed to re-open renamed compilation results ('{f}{s}'): {t}",
3141 .{ comp.dirs.local_cache, o_sub_path, err },
3142 );
3143 };
3144 }
3145
3146 try flush(comp, arena);
3147
3148 // Calling `flush` may have produced errors, in which case the
3149 // cache manifest must not be written.
3150 if (anyErrors(comp)) return;
3151
3152 // Failure here only means an unnecessary cache miss.
3153 man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err});
3154
3155 assert(whole.lock == null);
3156 whole.lock = man.toOwnedLock();
3157 },
3158 }
3159}
3160
3161/// Thread-safe. Assumes that `comp.mutex` is *not* already held by the caller.
3162pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocator.Error!void {
3163 const gpa = comp.gpa;
3164 const io = comp.io;
3165 const fsi = comp.file_system_inputs orelse return;
3166 const prefixes = comp.cache_parent.prefixes();
3167
3168 const want_prefix_dir: Cache.Directory = switch (path.root) {
3169 .zig_lib => comp.dirs.zig_lib,
3170 .global_cache => comp.dirs.global_cache,
3171 .local_cache => comp.dirs.local_cache,
3172 .build_root => comp.dirs.build_root,
3173 .none => .cwd(),
3174 };
3175 const prefix: u8 = for (prefixes, 1..) |prefix_dir, i| {
3176 if (prefix_dir.eql(want_prefix_dir)) {
3177 break @intCast(i);
3178 }
3179 } else std.debug.panic(
3180 "missing prefix directory {t} ('{f}') for {q}",
3181 .{ path.root, want_prefix_dir, path.sub_path },
3182 );
3183
3184 // There may be concurrent calls to this function from C object workers and/or the main thread.
3185 comp.mutex.lockUncancelable(io);
3186 defer comp.mutex.unlock(io);
3187
3188 try fsi.ensureUnusedCapacity(gpa, path.sub_path.len + 3);
3189 if (fsi.items.len > 0) fsi.appendAssumeCapacity(0);
3190 fsi.appendAssumeCapacity(prefix);
3191 fsi.appendSliceAssumeCapacity(path.sub_path);
3192}
3193
3194fn resolveEmitPath(comp: *Compilation, path: []const u8) Cache.Path {
3195 return .{
3196 .root_dir = switch (comp.cache_use) {
3197 .none => .cwd(),
3198 .incremental => |i| i.artifact_directory,
3199 .whole => |w| w.tmp_artifact_directory.?,
3200 },
3201 .sub_path = path,
3202 };
3203}
3204/// Like `resolveEmitPath`, but for calling during `flush`. The returned `Cache.Path` may reference
3205/// memory from `arena`, and may reference `path` itself.
3206/// If `kind == .temp`, then the returned path will be in a temporary or cache directory. This is
3207/// useful for intermediate files, such as the ZCU object file emitted by the LLVM backend.
3208pub fn resolveEmitPathFlush(
3209 comp: *Compilation,
3210 arena: Allocator,
3211 kind: enum { temp, artifact },
3212 path: []const u8,
3213) Allocator.Error!Cache.Path {
3214 switch (comp.cache_use) {
3215 .none => |none| return .{
3216 .root_dir = switch (kind) {
3217 .temp => none.tmp_artifact_directory.?,
3218 .artifact => .cwd(),
3219 },
3220 .sub_path = path,
3221 },
3222 .incremental, .whole => return .{
3223 .root_dir = comp.dirs.local_cache,
3224 .sub_path = try fs.path.join(arena, &.{
3225 "o",
3226 &Cache.binToHex(comp.digest.?),
3227 path,
3228 }),
3229 },
3230 }
3231}
3232
3233fn flush(comp: *Compilation, arena: Allocator) (Io.Cancelable || Allocator.Error)!void {
3234 const io = comp.io;
3235 const tid: Zcu.PerThread.Id = .acquire(io);
3236 defer tid.release(io);
3237 if (comp.zcu) |zcu| {
3238 if (zcu.llvm_object) |llvm_object| {
3239
3240 // Emit the ZCU object from LLVM now; it's required to flush the output file.
3241 // If there's an output file, it wants to decide where the LLVM object goes!
3242 const sub_prog_node = comp.link_prog_node.start("LLVM Emit Object", 0);
3243 defer sub_prog_node.end();
3244
3245 var timer = comp.startTimer();
3246 defer if (timer.finish(io)) |ns| {
3247 comp.mutex.lockUncancelable(io);
3248 defer comp.mutex.unlock(io);
3249 comp.time_report.?.stats.real_ns_llvm_emit = ns;
3250 };
3251
3252 const zcu_obj_path: ?Cache.Path = if (comp.bin_file != null) p: {
3253 break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename);
3254 } else null;
3255
3256 const active = zcu.activate(tid);
3257 defer active.deactivate();
3258 llvm_object.emit(active.pt, .{
3259 .pre_ir_path = comp.verbose_llvm_ir,
3260 .pre_bc_path = comp.verbose_llvm_bc,
3261
3262 .bin_path = if (zcu_obj_path) |p| try p.toStringZ(arena) else null,
3263 .asm_path = p: {
3264 const raw = comp.emit_asm orelse break :p null;
3265 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
3266 break :p try p.toStringZ(arena);
3267 },
3268 .post_ir_path = p: {
3269 const raw = comp.emit_llvm_ir orelse break :p null;
3270 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
3271 break :p try p.toStringZ(arena);
3272 },
3273 .post_bc_path = p: {
3274 const raw = comp.emit_llvm_bc orelse break :p null;
3275 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
3276 break :p try p.toStringZ(arena);
3277 },
3278
3279 .is_debug = comp.root_mod.optimize_mode == .debug,
3280 .is_small = comp.root_mod.optimize_mode == .small,
3281 .time_report = if (comp.time_report) |*p| p else null,
3282 .sanitize_thread = comp.config.any_sanitize_thread,
3283 .fuzz = comp.config.any_fuzz,
3284 .lto = comp.config.lto,
3285 }) catch |err| switch (err) {
3286 error.AlreadyReported => {},
3287 error.OutOfMemory => |e| return e,
3288 };
3289
3290 if (zcu_obj_path) |path| {
3291 // Tell the linker backend about the ZCU object emitted by LLVM.
3292 link.doPrelinkTask(comp, .{ .load_object = path });
3293 // `link.Queue` has not called `prelink` because it knew we would want to send that
3294 // final link input. It is *our* responsibility to call `prelink` now we're done.
3295 comp.bin_file.?.prelink() catch |err| switch (err) {
3296 error.AlreadyReported => return,
3297 else => |e| return e,
3298 };
3299 }
3300 }
3301 }
3302 if (comp.bin_file) |lf| {
3303 var timer = comp.startTimer();
3304 defer if (timer.finish(io)) |ns| {
3305 comp.mutex.lockUncancelable(io);
3306 defer comp.mutex.unlock(io);
3307 comp.time_report.?.stats.real_ns_link_flush = ns;
3308 };
3309 // This is needed before reading the error flags.
3310 lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) {
3311 error.AlreadyReported => return,
3312 error.OutOfMemory, error.Canceled => |e| return e,
3313 };
3314 }
3315}
3316
3317/// This function is called by the frontend before flush(). It communicates that
3318/// `options.bin_file.emit` directory needs to be renamed from
3319/// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`.
3320/// The frontend would like to simply perform a file system rename, however,
3321/// some linker backends care about the file paths of the objects they are linking.
3322/// So this function call tells linker backends to rename the paths of object files
3323/// to observe the new directory path.
3324/// Linker backends which do not have this requirement can fall back to the simple
3325/// implementation at the bottom of this function.
3326/// This function is only called when CacheMode is `whole`.
3327fn renameTmpIntoCache(
3328 io: Io,
3329 cache_directory: Cache.Directory,
3330 tmp_dir_sub_path: []const u8,
3331 o_sub_path: []const u8,
3332) !void {
3333 var seen_eaccess = false;
3334 while (true) {
3335 Io.Dir.rename(
3336 cache_directory.handle,
3337 tmp_dir_sub_path,
3338 cache_directory.handle,
3339 o_sub_path,
3340 io,
3341 ) catch |err| switch (err) {
3342 // On Windows, rename fails with `AccessDenied` rather than `PathAlreadyExists`.
3343 // See https://github.com/ziglang/zig/issues/8362
3344 error.AccessDenied => switch (builtin.os.tag) {
3345 .windows => {
3346 if (seen_eaccess) return error.AccessDenied;
3347 seen_eaccess = true;
3348 try cache_directory.handle.deleteTree(io, o_sub_path);
3349 continue;
3350 },
3351 else => return error.AccessDenied,
3352 },
3353 error.DirNotEmpty => {
3354 try cache_directory.handle.deleteTree(io, o_sub_path);
3355 continue;
3356 },
3357 error.FileNotFound => {
3358 try cache_directory.handle.createDirPath(io, "o");
3359 continue;
3360 },
3361 else => |e| return e,
3362 };
3363 break;
3364 }
3365}
3366
3367/// This is only observed at compile-time and used to emit a compile error
3368/// to remind the programmer to update multiple related pieces of code that
3369/// are in different locations. Bump this number when adding or deleting
3370/// anything from the link cache manifest.
3371pub const link_hash_implementation_version = 14;
3372
3373fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
3374 comptime assert(link_hash_implementation_version == 14);
3375
3376 if (comp.zcu) |zcu| {
3377 // No need to hash the actual file contents here because it is
3378 // redundant with the logic in `PerThread.update` which iterates over
3379 // `zcu.alive_files` and adds those files discovered via `@import` to
3380 // the whole cache manifest.
3381 try addModuleTableToCacheHash(zcu, &man.hash);
3382
3383 // Synchronize with other matching comments: ZigOnlyHashStuff
3384 man.hash.addListOfBytes(comp.test_filters);
3385 man.hash.add(comp.skip_linker_dependencies);
3386 //man.hash.add(zcu.emit_h != .no);
3387 man.hash.add(zcu.error_limit);
3388 } else {
3389 cache_helpers.addModule(&man.hash, comp.root_mod);
3390 }
3391
3392 try link.hashInputs(man, comp.link_inputs);
3393
3394 for (comp.c_objects.items) |c_object| {
3395 _ = try man.addFilePath(.initCwd(c_object.src.src_path), null);
3396 man.hash.addOptional(c_object.src.ext);
3397 man.hash.addListOfBytes(c_object.src.extra_flags);
3398 }
3399
3400 for (comp.win32_resources.items) |win32_resource| {
3401 switch (win32_resource.src) {
3402 .rc => |rc_src| {
3403 _ = try man.addFilePath(.initCwd(rc_src.src_path), null);
3404 man.hash.addListOfBytes(rc_src.extra_flags);
3405 },
3406 .manifest => |manifest_path| {
3407 _ = try man.addFilePath(.initCwd(manifest_path), null);
3408 },
3409 }
3410 }
3411
3412 man.hash.add(comp.config.use_llvm);
3413 man.hash.add(comp.config.use_lib_llvm);
3414 man.hash.add(comp.config.use_lld);
3415 man.hash.add(comp.config.use_new_linker);
3416 man.hash.add(comp.config.is_test);
3417 man.hash.add(comp.config.import_memory);
3418 man.hash.add(comp.config.export_memory);
3419 man.hash.add(comp.config.shared_memory);
3420 man.hash.add(comp.config.dll_export_fns);
3421 man.hash.add(comp.config.rdynamic);
3422
3423 man.hash.addOptionalBytes(comp.sysroot);
3424 man.hash.addOptional(comp.version);
3425 man.hash.add(comp.link_eh_frame_hdr);
3426 man.hash.add(comp.skip_linker_dependencies);
3427 man.hash.add(comp.compiler_rt_strat);
3428 man.hash.add(comp.ubsan_rt_strat);
3429 man.hash.add(comp.zigc_strat);
3430 man.hash.add(comp.rc_includes);
3431 man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
3432 man.hash.addListOfBytes(comp.framework_dirs);
3433 man.hash.addListOfBytes(comp.windows_libs.keys());
3434
3435 man.hash.addListOfBytes(comp.global_cc_argv);
3436
3437 const opts = comp.cache_use.whole.lf_open_opts;
3438
3439 try man.addOptionalFilePath(opts.linker_script);
3440 try man.addOptionalFilePath(opts.version_script);
3441 man.hash.add(opts.allow_undefined_version);
3442 man.hash.addOptional(opts.enable_new_dtags);
3443
3444 man.hash.addOptional(opts.stack_size);
3445 man.hash.addOptional(opts.image_base);
3446 man.hash.addOptional(opts.gc_sections);
3447 man.hash.add(opts.emit_relocs);
3448 const target = &comp.root_mod.resolved_target.result;
3449 if (target.ofmt == .macho or target.ofmt == .coff) {
3450 // TODO remove this, libraries need to be resolved by the frontend. this is already
3451 // done by ELF.
3452 for (opts.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path);
3453 }
3454 man.hash.addListOfBytes(opts.rpath_list);
3455 man.hash.addListOfBytes(opts.symbol_wrap_set.keys());
3456 if (comp.config.link_libc) {
3457 LibCInstallation.addToHash(comp.libc_installation, &man.hash, target.abi);
3458 man.hash.addOptionalBytes(target.dynamic_linker.get());
3459 }
3460 man.hash.add(opts.repro);
3461 man.hash.addOptional(opts.allow_shlib_undefined);
3462 man.hash.add(opts.bind_global_refs_locally);
3463
3464 const EntryTag = @typeInfo(link.File.OpenOptions.Entry).@"union".tag_type.?;
3465 man.hash.add(@as(EntryTag, opts.entry));
3466 switch (opts.entry) {
3467 .default, .disabled, .enabled => {},
3468 .named => |name| man.hash.addBytes(name),
3469 }
3470
3471 // ELF specific stuff
3472 man.hash.add(opts.z_nodelete);
3473 man.hash.add(opts.z_notext);
3474 man.hash.add(opts.z_defs);
3475 man.hash.add(opts.z_origin);
3476 man.hash.add(opts.z_nocopyreloc);
3477 man.hash.add(opts.z_now);
3478 man.hash.add(opts.z_relro);
3479 man.hash.add(opts.z_common_page_size orelse 0);
3480 man.hash.add(opts.z_max_page_size orelse 0);
3481 man.hash.add(opts.hash_style);
3482 man.hash.add(opts.compress_debug_sections);
3483 man.hash.addOptional(opts.sort_section);
3484 man.hash.addOptionalBytes(opts.soname);
3485 man.hash.add(opts.build_id);
3486
3487 // WASM specific stuff
3488 man.hash.addOptional(opts.initial_memory);
3489 man.hash.addOptional(opts.max_memory);
3490 man.hash.addOptional(opts.global_base);
3491 man.hash.addListOfBytes(opts.export_symbol_names);
3492 man.hash.add(opts.import_symbols);
3493 man.hash.add(opts.import_table);
3494 man.hash.add(opts.export_table);
3495 man.hash.add(opts.growable_table);
3496
3497 // Mach-O specific stuff
3498 try link.File.MachO.hashAddFrameworks(man, opts.frameworks);
3499 try man.addOptionalFilePath(opts.entitlements);
3500 man.hash.addOptional(opts.pagezero_size);
3501 man.hash.addOptional(opts.headerpad_size);
3502 man.hash.add(opts.headerpad_max_install_names);
3503 man.hash.add(opts.dead_strip_dylibs);
3504 man.hash.add(opts.force_load_objc);
3505 man.hash.add(opts.discard_local_symbols);
3506 man.hash.addOptional(opts.compatibility_version);
3507 man.hash.addOptionalBytes(opts.install_name);
3508 man.hash.addOptional(opts.darwin_sdk_layout);
3509
3510 // COFF specific stuff
3511 man.hash.addOptional(opts.subsystem);
3512 man.hash.add(opts.tsaware);
3513 man.hash.add(opts.nxcompat);
3514 man.hash.add(opts.dynamicbase);
3515 man.hash.addOptional(opts.major_subsystem_version);
3516 man.hash.addOptional(opts.minor_subsystem_version);
3517 man.hash.addOptionalBytes(opts.pdb_source_path);
3518 man.hash.addOptionalBytes(opts.module_definition_file);
3519}
3520
3521fn emitFromCObject(
3522 comp: *Compilation,
3523 arena: Allocator,
3524 c_obj_path: Cache.Path,
3525 new_ext: []const u8,
3526 unresolved_emit_path: []const u8,
3527) Allocator.Error!void {
3528 const io = comp.io;
3529 // The dirname and stem (i.e. everything but the extension), of the sub path of the C object.
3530 // We'll append `new_ext` to it to get the path to the right thing (asm, LLVM IR, etc).
3531 const c_obj_dir_and_stem: []const u8 = p: {
3532 const p = c_obj_path.sub_path;
3533 const ext_len = fs.path.extension(p).len;
3534 break :p p[0 .. p.len - ext_len];
3535 };
3536 const src_path: Cache.Path = .{
3537 .root_dir = c_obj_path.root_dir,
3538 .sub_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ c_obj_dir_and_stem, new_ext }),
3539 };
3540 const emit_path = comp.resolveEmitPath(unresolved_emit_path);
3541
3542 Io.Dir.copyFile(
3543 src_path.root_dir.handle,
3544 src_path.sub_path,
3545 emit_path.root_dir.handle,
3546 emit_path.sub_path,
3547 io,
3548 .{},
3549 ) catch |err| log.err("unable to copy '{f}' to '{f}': {t}", .{ src_path, emit_path, err });
3550}
3551
3552/// Having the file open for writing is problematic as far as executing the
3553/// binary is concerned. This will remove the write flag, or close the file,
3554/// or whatever is needed so that it can be executed.
3555/// After this, one must call` makeFileWritable` before calling `update`.
3556pub fn makeBinFileExecutable(comp: *Compilation) !void {
3557 if (!dev.env.supports(.make_executable)) return;
3558 const lf = comp.bin_file orelse return;
3559 return lf.makeExecutable();
3560}
3561
3562pub fn makeBinFileWritable(comp: *Compilation) !void {
3563 const lf = comp.bin_file orelse return;
3564 return lf.makeWritable();
3565}
3566
3567const Header = extern struct {
3568 intern_pool: extern struct {
3569 thread_count: u32,
3570 src_hash_deps_len: u32,
3571 nav_val_deps_len: u32,
3572 nav_ty_deps_len: u32,
3573 type_layout_deps_len: u32,
3574 struct_defaults_deps_len: u32,
3575 func_ies_deps_len: u32,
3576 source_file_deps_len: u32,
3577 embed_file_deps_len: u32,
3578 namespace_deps_len: u32,
3579 namespace_name_deps_len: u32,
3580 first_dependency_len: u32,
3581 dep_entries_len: u32,
3582 free_dep_entries_len: u32,
3583 },
3584
3585 const PerThread = extern struct {
3586 intern_pool: extern struct {
3587 items_len: u32,
3588 extra_len: u32,
3589 limbs_len: u32,
3590 strings_len: u32,
3591 string_bytes_len: u32,
3592 tracked_insts_len: u32,
3593 files_len: u32,
3594 },
3595 };
3596};
3597
3598/// Note that all state that is included in the cache hash namespace is *not*
3599/// saved, such as the target and most CLI flags. A cache hit will only occur
3600/// when subsequent compiler invocations use the same set of flags.
3601pub fn saveState(comp: *Compilation) !void {
3602 dev.check(.incremental);
3603
3604 const lf = comp.bin_file orelse return;
3605
3606 const gpa = comp.gpa;
3607 const io = comp.io;
3608
3609 var bufs = std.array_list.Managed([]const u8).init(gpa);
3610 defer bufs.deinit();
3611
3612 var pt_headers = std.array_list.Managed(Header.PerThread).init(gpa);
3613 defer pt_headers.deinit();
3614
3615 if (comp.zcu) |zcu| {
3616 const ip = &zcu.intern_pool;
3617 const header: Header = .{
3618 .intern_pool = .{
3619 .thread_count = @intCast(ip.locals.len),
3620 .src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
3621 .nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
3622 .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
3623 .type_layout_deps_len = @intCast(ip.type_layout_deps.count()),
3624 .struct_defaults_deps_len = @intCast(ip.struct_defaults_deps.count()),
3625 .func_ies_deps_len = @intCast(ip.func_ies_deps.count()),
3626 .source_file_deps_len = @intCast(ip.source_file_deps.count()),
3627 .embed_file_deps_len = @intCast(ip.embed_file_deps.count()),
3628 .namespace_deps_len = @intCast(ip.namespace_deps.count()),
3629 .namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
3630 .first_dependency_len = @intCast(ip.first_dependency.count()),
3631 .dep_entries_len = @intCast(ip.dep_entries.items.len),
3632 .free_dep_entries_len = @intCast(ip.free_dep_entries.items.len),
3633 },
3634 };
3635
3636 try pt_headers.ensureTotalCapacityPrecise(header.intern_pool.thread_count);
3637 for (ip.locals) |*local| pt_headers.appendAssumeCapacity(.{
3638 .intern_pool = .{
3639 .items_len = @intCast(local.mutate.items.len),
3640 .extra_len = @intCast(local.mutate.extra.len),
3641 .limbs_len = @intCast(local.mutate.limbs.len),
3642 .strings_len = @intCast(local.mutate.strings.len),
3643 .string_bytes_len = @intCast(local.mutate.string_bytes.len),
3644 .tracked_insts_len = @intCast(local.mutate.tracked_insts.len),
3645 .files_len = @intCast(local.mutate.files.len),
3646 },
3647 });
3648
3649 try bufs.ensureTotalCapacityPrecise(26 + 9 * pt_headers.items.len);
3650 addBuf(&bufs, mem.asBytes(&header));
3651 addBuf(&bufs, @ptrCast(pt_headers.items));
3652
3653 addBuf(&bufs, @ptrCast(ip.src_hash_deps.keys()));
3654 addBuf(&bufs, @ptrCast(ip.src_hash_deps.values()));
3655 addBuf(&bufs, @ptrCast(ip.nav_val_deps.keys()));
3656 addBuf(&bufs, @ptrCast(ip.nav_val_deps.values()));
3657 addBuf(&bufs, @ptrCast(ip.nav_ty_deps.keys()));
3658 addBuf(&bufs, @ptrCast(ip.nav_ty_deps.values()));
3659 addBuf(&bufs, @ptrCast(ip.type_layout_deps.keys()));
3660 addBuf(&bufs, @ptrCast(ip.type_layout_deps.values()));
3661 addBuf(&bufs, @ptrCast(ip.struct_defaults_deps.keys()));
3662 addBuf(&bufs, @ptrCast(ip.struct_defaults_deps.values()));
3663 addBuf(&bufs, @ptrCast(ip.func_ies_deps.keys()));
3664 addBuf(&bufs, @ptrCast(ip.func_ies_deps.values()));
3665 addBuf(&bufs, @ptrCast(ip.source_file_deps.keys()));
3666 addBuf(&bufs, @ptrCast(ip.source_file_deps.values()));
3667 addBuf(&bufs, @ptrCast(ip.embed_file_deps.keys()));
3668 addBuf(&bufs, @ptrCast(ip.embed_file_deps.values()));
3669 addBuf(&bufs, @ptrCast(ip.namespace_deps.keys()));
3670 addBuf(&bufs, @ptrCast(ip.namespace_deps.values()));
3671 addBuf(&bufs, @ptrCast(ip.namespace_name_deps.keys()));
3672 addBuf(&bufs, @ptrCast(ip.namespace_name_deps.values()));
3673
3674 addBuf(&bufs, @ptrCast(ip.first_dependency.keys()));
3675 addBuf(&bufs, @ptrCast(ip.first_dependency.values()));
3676 addBuf(&bufs, @ptrCast(ip.dep_entries.items));
3677 addBuf(&bufs, @ptrCast(ip.free_dep_entries.items));
3678
3679 for (ip.locals, pt_headers.items) |*local, pt_header| {
3680 if (pt_header.intern_pool.limbs_len > 0) {
3681 addBuf(&bufs, @ptrCast(local.shared.limbs.view().items(.@"0")[0..pt_header.intern_pool.limbs_len]));
3682 }
3683 if (pt_header.intern_pool.extra_len > 0) {
3684 addBuf(&bufs, @ptrCast(local.shared.extra.view().items(.@"0")[0..pt_header.intern_pool.extra_len]));
3685 }
3686 if (pt_header.intern_pool.items_len > 0) {
3687 addBuf(&bufs, @ptrCast(local.shared.items.view().items(.data)[0..pt_header.intern_pool.items_len]));
3688 addBuf(&bufs, @ptrCast(local.shared.items.view().items(.tag)[0..pt_header.intern_pool.items_len]));
3689 }
3690 if (pt_header.intern_pool.strings_len > 0) {
3691 addBuf(&bufs, @ptrCast(local.shared.strings.view().items(.@"0")[0..pt_header.intern_pool.strings_len]));
3692 }
3693 if (pt_header.intern_pool.string_bytes_len > 0) {
3694 addBuf(&bufs, local.shared.string_bytes.view().items(.@"0")[0..pt_header.intern_pool.string_bytes_len]);
3695 }
3696 if (pt_header.intern_pool.tracked_insts_len > 0) {
3697 addBuf(&bufs, @ptrCast(local.shared.tracked_insts.view().items(.@"0")[0..pt_header.intern_pool.tracked_insts_len]));
3698 }
3699 if (pt_header.intern_pool.files_len > 0) {
3700 addBuf(&bufs, @ptrCast(local.shared.files.view().items(.bin_digest)[0..pt_header.intern_pool.files_len]));
3701 addBuf(&bufs, @ptrCast(local.shared.files.view().items(.root_type)[0..pt_header.intern_pool.files_len]));
3702 }
3703 }
3704
3705 //// TODO: compilation errors
3706 //// TODO: namespaces
3707 //// TODO: decls
3708 }
3709
3710 // linker state
3711 switch (lf.tag) {
3712 .wasm => {
3713 dev.check(link.File.Tag.wasm.devFeature());
3714 const wasm = lf.cast(.wasm).?;
3715 const is_obj = comp.config.output_mode == .Obj;
3716 try bufs.ensureUnusedCapacity(85);
3717 addBuf(&bufs, wasm.string_bytes.items);
3718 // TODO make it well-defined memory layout
3719 //addBuf(&bufs, @ptrCast(wasm.objects.items));
3720 addBuf(&bufs, @ptrCast(wasm.func_types.keys()));
3721 addBuf(&bufs, @ptrCast(wasm.object_function_imports.keys()));
3722 addBuf(&bufs, @ptrCast(wasm.object_function_imports.values()));
3723 addBuf(&bufs, @ptrCast(wasm.object_functions.items));
3724 addBuf(&bufs, @ptrCast(wasm.object_global_imports.keys()));
3725 addBuf(&bufs, @ptrCast(wasm.object_global_imports.values()));
3726 addBuf(&bufs, @ptrCast(wasm.object_globals.items));
3727 addBuf(&bufs, @ptrCast(wasm.object_table_imports.keys()));
3728 addBuf(&bufs, @ptrCast(wasm.object_table_imports.values()));
3729 addBuf(&bufs, @ptrCast(wasm.object_tables.items));
3730 addBuf(&bufs, @ptrCast(wasm.object_memory_imports.keys()));
3731 addBuf(&bufs, @ptrCast(wasm.object_memory_imports.values()));
3732 addBuf(&bufs, @ptrCast(wasm.object_memories.items));
3733 addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.tag)));
3734 addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.offset)));
3735 // TODO handle the union safety field
3736 //addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.pointee)));
3737 addBuf(&bufs, @ptrCast(wasm.object_relocations.items(.addend)));
3738 addBuf(&bufs, @ptrCast(wasm.object_init_funcs.items));
3739 addBuf(&bufs, @ptrCast(wasm.object_data_segments.items));
3740 addBuf(&bufs, @ptrCast(wasm.object_datas.items));
3741 addBuf(&bufs, @ptrCast(wasm.object_data_imports.keys()));
3742 addBuf(&bufs, @ptrCast(wasm.object_data_imports.values()));
3743 addBuf(&bufs, @ptrCast(wasm.object_custom_segments.keys()));
3744 addBuf(&bufs, @ptrCast(wasm.object_custom_segments.values()));
3745 // TODO make it well-defined memory layout
3746 // addBuf(&bufs, @ptrCast(wasm.object_comdats.items));
3747 addBuf(&bufs, @ptrCast(wasm.object_relocations_table.keys()));
3748 addBuf(&bufs, @ptrCast(wasm.object_relocations_table.values()));
3749 addBuf(&bufs, @ptrCast(wasm.object_comdat_symbols.items(.kind)));
3750 addBuf(&bufs, @ptrCast(wasm.object_comdat_symbols.items(.index)));
3751 addBuf(&bufs, @ptrCast(wasm.zcu_relocations.items(.tag)));
3752 addBuf(&bufs, @ptrCast(wasm.zcu_relocations.items(.offset)));
3753 // TODO handle the union safety field
3754 //addBuf(&bufs, @ptrCast(wasm.zcu_relocations.items(.pointee)));
3755 addBuf(&bufs, @ptrCast(wasm.zcu_relocations.items(.addend)));
3756 addBuf(&bufs, @ptrCast(wasm.uav_fixups.items));
3757 addBuf(&bufs, @ptrCast(wasm.nav_fixups.items));
3758 addBuf(&bufs, @ptrCast(wasm.func_table_fixups.items));
3759 if (is_obj) {
3760 addBuf(&bufs, @ptrCast(wasm.navs_obj.keys()));
3761 addBuf(&bufs, @ptrCast(wasm.navs_obj.values()));
3762 addBuf(&bufs, @ptrCast(wasm.uavs_obj.keys()));
3763 addBuf(&bufs, @ptrCast(wasm.uavs_obj.values()));
3764 } else {
3765 addBuf(&bufs, @ptrCast(wasm.navs_exe.keys()));
3766 addBuf(&bufs, @ptrCast(wasm.navs_exe.values()));
3767 addBuf(&bufs, @ptrCast(wasm.uavs_exe.keys()));
3768 addBuf(&bufs, @ptrCast(wasm.uavs_exe.values()));
3769 }
3770 addBuf(&bufs, @ptrCast(wasm.overaligned_uavs.keys()));
3771 addBuf(&bufs, @ptrCast(wasm.overaligned_uavs.values()));
3772 addBuf(&bufs, @ptrCast(wasm.zcu_funcs.keys()));
3773 // TODO handle the union safety field
3774 // addBuf(&bufs, @ptrCast(wasm.zcu_funcs.values()));
3775 addBuf(&bufs, @ptrCast(wasm.nav_exports.keys()));
3776 addBuf(&bufs, @ptrCast(wasm.nav_exports.values()));
3777 addBuf(&bufs, @ptrCast(wasm.uav_exports.keys()));
3778 addBuf(&bufs, @ptrCast(wasm.uav_exports.values()));
3779 addBuf(&bufs, @ptrCast(wasm.imports.keys()));
3780 addBuf(&bufs, @ptrCast(wasm.missing_exports.keys()));
3781 addBuf(&bufs, @ptrCast(wasm.function_exports.keys()));
3782 addBuf(&bufs, @ptrCast(wasm.function_exports.values()));
3783 addBuf(&bufs, @ptrCast(wasm.hidden_function_exports.keys()));
3784 addBuf(&bufs, @ptrCast(wasm.hidden_function_exports.values()));
3785 addBuf(&bufs, @ptrCast(wasm.global_exports.items));
3786 addBuf(&bufs, @ptrCast(wasm.functions.keys()));
3787 addBuf(&bufs, @ptrCast(wasm.function_imports.keys()));
3788 addBuf(&bufs, @ptrCast(wasm.function_imports.values()));
3789 addBuf(&bufs, @ptrCast(wasm.data_imports.keys()));
3790 addBuf(&bufs, @ptrCast(wasm.data_imports.values()));
3791 addBuf(&bufs, @ptrCast(wasm.data_segments.keys()));
3792 addBuf(&bufs, @ptrCast(wasm.globals.keys()));
3793 addBuf(&bufs, @ptrCast(wasm.global_imports.keys()));
3794 addBuf(&bufs, @ptrCast(wasm.global_imports.values()));
3795 addBuf(&bufs, @ptrCast(wasm.tables.keys()));
3796 addBuf(&bufs, @ptrCast(wasm.table_imports.keys()));
3797 addBuf(&bufs, @ptrCast(wasm.table_imports.values()));
3798 addBuf(&bufs, @ptrCast(wasm.zcu_indirect_function_set.keys()));
3799 addBuf(&bufs, @ptrCast(wasm.object_indirect_function_import_set.keys()));
3800 addBuf(&bufs, @ptrCast(wasm.object_indirect_function_set.keys()));
3801 addBuf(&bufs, @ptrCast(wasm.mir_instructions.items(.tag)));
3802 // TODO handle the union safety field
3803 //addBuf(&bufs, @ptrCast(wasm.mir_instructions.items(.data)));
3804 addBuf(&bufs, @ptrCast(wasm.mir_extra.items));
3805 addBuf(&bufs, @ptrCast(wasm.mir_locals.items));
3806 addBuf(&bufs, @ptrCast(wasm.tag_name_bytes.items));
3807 addBuf(&bufs, @ptrCast(wasm.tag_name_offs.items));
3808
3809 // TODO add as header fields
3810 // entry_resolution: FunctionImport.Resolution
3811 // function_exports_len: u32
3812 // global_exports_len: u32
3813 // functions_end_prelink: u32
3814 // globals_end_prelink: u32
3815 // error_name_table_ref_count: u32
3816 // tag_name_table_ref_count: u32
3817 // any_tls_relocs: bool
3818 // any_passive_inits: bool
3819 },
3820 else => log.err("TODO implement saving linker state for {s}", .{@tagName(lf.tag)}),
3821 }
3822
3823 var basename_buf: [255]u8 = undefined;
3824 const basename = std.mem.print(&basename_buf, "{s}.zcs", .{
3825 comp.root_name,
3826 }) catch o: {
3827 basename_buf[basename_buf.len - 4 ..].* = ".zcs".*;
3828 break :o &basename_buf;
3829 };
3830
3831 // Using an atomic file prevents a crash or power failure from corrupting
3832 // the previous incremental compilation state.
3833 var af = try lf.emit.root_dir.handle.createFileAtomic(io, basename, .{ .replace = true });
3834 defer af.deinit(io);
3835
3836 var write_buffer: [1024]u8 = undefined;
3837 var file_writer = af.file.writer(io, &write_buffer);
3838 try file_writer.interface.writeVecAll(bufs.items);
3839 try file_writer.interface.flush();
3840 try af.replace(io);
3841}
3842
3843fn addBuf(list: *std.array_list.Managed([]const u8), buf: []const u8) void {
3844 if (buf.len == 0) return;
3845 list.appendAssumeCapacity(buf);
3846}
3847
3848/// This function is temporally single-threaded.
3849pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
3850 const gpa = comp.gpa;
3851 const io = comp.io;
3852
3853 var bundle: ErrorBundle.Wip = undefined;
3854 try bundle.init(gpa);
3855 defer bundle.deinit();
3856
3857 for (comp.failed_c_objects.values()) |diag_bundle| {
3858 try diag_bundle.addToErrorBundle(io, &bundle);
3859 }
3860
3861 for (comp.failed_win32_resources.values()) |error_bundle| {
3862 try bundle.addBundleAsRoots(error_bundle);
3863 }
3864
3865 for (comp.link_diags.lld.items) |lld_error| {
3866 const notes_len = @as(u32, @intCast(lld_error.context_lines.len));
3867
3868 try bundle.addRootErrorMessage(.{
3869 .msg = try bundle.addString(lld_error.msg),
3870 .notes_len = notes_len,
3871 });
3872 const notes_start = try bundle.reserveNotes(notes_len);
3873 for (notes_start.., lld_error.context_lines) |note, context_line| {
3874 bundle.extra.items[note] = @backingInt(bundle.addErrorMessageAssumeCapacity(.{
3875 .msg = try bundle.addString(context_line),
3876 }));
3877 }
3878 }
3879 for (comp.misc_failures.values()) |*value| {
3880 try bundle.addRootErrorMessage(.{
3881 .msg = try bundle.addString(value.msg),
3882 .notes_len = if (value.children) |b| b.errorMessageCount() else 0,
3883 });
3884 if (value.children) |b| try bundle.addBundleAsNotes(b);
3885 }
3886 if (comp.alloc_failure_occurred or comp.link_diags.flags.alloc_failure_occurred) {
3887 try bundle.addRootErrorMessage(.{
3888 .msg = try bundle.addString("memory allocation failure"),
3889 });
3890 }
3891
3892 if (comp.zcu) |zcu| zcu_errors: {
3893 if (zcu.multi_module_err != null) {
3894 try zcu.addFileInMultipleModulesError(&bundle);
3895 break :zcu_errors;
3896 }
3897 for (zcu.failed_imports.items) |failed| {
3898 assert(zcu.alive_files.contains(failed.file_index)); // otherwise it wouldn't have been added
3899 const file = zcu.fileByIndex(failed.file_index);
3900 const tree = file.getTree(zcu) catch |err| {
3901 try unableToLoadZcuFile(zcu, &bundle, file, err);
3902 continue;
3903 };
3904 const start = tree.tokenStart(failed.import_token);
3905 const end = start + tree.tokenSlice(failed.import_token).len;
3906 const loc = std.zig.findLineColumn(tree.source, start);
3907 try bundle.addRootErrorMessage(.{
3908 .msg = switch (failed.kind) {
3909 .file_outside_module_root => try bundle.addString("import of file outside module path"),
3910 .illegal_zig_import => try bundle.addString("this compiler implementation does not allow importing files from this directory"),
3911 },
3912 .src_loc = try bundle.addSourceLocation(.{
3913 .src_path = try bundle.printString("{f}", .{file.path.fmt(comp)}),
3914 .span_start = start,
3915 .span_main = start,
3916 .span_end = @intCast(end),
3917 .line = @intCast(loc.line),
3918 .column = @intCast(loc.column),
3919 .source_line = try bundle.addString(loc.source_line),
3920 }),
3921 .notes_len = 0,
3922 });
3923 }
3924
3925 // Before iterating `failed_files`, we need to sort it into a consistent order so that error
3926 // messages appear consistently despite different ordering from the AstGen worker pool. File
3927 // paths are a great key for this sort! We are using sorting the `ArrayHashMap` itself to
3928 // make sure it reindexes; that's important because these entries need to be retained for
3929 // future updates.
3930 const FileSortCtx = struct {
3931 zcu: *Zcu,
3932 failed_files_keys: []const Zcu.File.Index,
3933 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
3934 const lhs_path = ctx.zcu.fileByIndex(ctx.failed_files_keys[lhs_index]).path;
3935 const rhs_path = ctx.zcu.fileByIndex(ctx.failed_files_keys[rhs_index]).path;
3936 if (lhs_path.root != rhs_path.root) return @backingInt(lhs_path.root) < @backingInt(rhs_path.root);
3937 return std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).compare(.lt);
3938 }
3939 };
3940 zcu.failed_files.sort(@as(FileSortCtx, .{
3941 .zcu = zcu,
3942 .failed_files_keys = zcu.failed_files.keys(),
3943 }));
3944
3945 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file_index, error_msg| {
3946 if (!zcu.alive_files.contains(file_index)) continue;
3947 const file = zcu.fileByIndex(file_index);
3948 const is_retryable = switch (file.status) {
3949 .retryable_failure => true,
3950 .success, .astgen_failure => false,
3951 .never_loaded => unreachable,
3952 };
3953 if (error_msg) |msg| {
3954 assert(is_retryable);
3955 try addWholeFileError(zcu, &bundle, file_index, msg);
3956 } else {
3957 assert(!is_retryable);
3958 // AstGen/ZoirGen succeeded with errors. Note that this may include AST errors.
3959 // Tree must be loaded.
3960 _ = file.getTree(zcu) catch |err| {
3961 try unableToLoadZcuFile(zcu, &bundle, file, err);
3962 continue;
3963 };
3964 const path = try std.fmt.allocPrint(gpa, "{f}", .{file.path.fmt(comp)});
3965 defer gpa.free(path);
3966 if (file.zir != null) {
3967 try bundle.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, path);
3968 } else if (file.zoir != null) {
3969 try bundle.addZoirErrorMessages(file.zoir.?, file.tree.?, file.source.?, path);
3970 } else {
3971 // Either Zir or Zoir must have been loaded.
3972 unreachable;
3973 }
3974 }
3975 }
3976 if (zcu.skip_analysis_this_update) break :zcu_errors;
3977 var sorted_failed_analysis: std.array_hash_map.Auto(InternPool.AnalUnit, *Zcu.ErrorMsg).DataList.Slice = s: {
3978 const SortOrder = struct {
3979 zcu: *Zcu,
3980 errors: []const *Zcu.ErrorMsg,
3981 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
3982 return Zcu.ErrorMsg.order(
3983 ctx.errors[lhs_index],
3984 ctx.errors[rhs_index],
3985 ctx.zcu,
3986 ).compare(.lt);
3987 }
3988 };
3989
3990 // We can't directly sort `zcu.failed_analysis.entries`, because that would leave the map
3991 // in an invalid state, and we need it intact for future incremental updates. The amount
3992 // of data here is only as large as the number of analysis errors, so just dupe it all.
3993 var entries = try zcu.failed_analysis.entries.clone(gpa);
3994 errdefer entries.deinit(gpa);
3995
3996 entries.sort(SortOrder{
3997 .zcu = zcu,
3998 .errors = entries.items(.value),
3999 });
4000 break :s entries.slice();
4001 };
4002 defer sorted_failed_analysis.deinit(gpa);
4003 var added_any_analysis_error = false;
4004 for (sorted_failed_analysis.items(.key), sorted_failed_analysis.items(.value)) |anal_unit, error_msg| {
4005 if (comp.config.incremental) {
4006 const refs = try zcu.resolveReferences();
4007 if (!refs.contains(anal_unit)) continue;
4008 }
4009
4010 std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{f}'", .{
4011 error_msg.msg, zcu.fmtAnalUnit(anal_unit),
4012 });
4013
4014 try addModuleErrorMsg(zcu, &bundle, error_msg.*, added_any_analysis_error);
4015 added_any_analysis_error = true;
4016 }
4017 try zcu.addDependencyLoopErrors(&bundle);
4018 for (zcu.failed_codegen.values()) |error_msg| {
4019 try addModuleErrorMsg(zcu, &bundle, error_msg.*, false);
4020 }
4021 for (zcu.failed_types.values()) |error_msg| {
4022 try addModuleErrorMsg(zcu, &bundle, error_msg.*, false);
4023 }
4024 for (zcu.failed_exports.values()) |value| {
4025 try addModuleErrorMsg(zcu, &bundle, value.*, false);
4026 }
4027
4028 const actual_error_count = zcu.intern_pool.global_error_set.getNamesFromMainThread().len;
4029 if (actual_error_count > zcu.error_limit) {
4030 try bundle.addRootErrorMessage(.{
4031 .msg = try bundle.printString("ZCU used more errors than possible: used {d}, max {d}", .{
4032 actual_error_count, zcu.error_limit,
4033 }),
4034 .notes_len = 1,
4035 });
4036 const notes_start = try bundle.reserveNotes(1);
4037 bundle.extra.items[notes_start] = @backingInt(bundle.addErrorMessageAssumeCapacity(.{
4038 .msg = try bundle.printString("use '--error-limit {d}' to increase limit", .{
4039 actual_error_count,
4040 }),
4041 }));
4042 }
4043 }
4044
4045 if (bundle.root_list.items.len == 0) {
4046 if (comp.link_diags.flags.no_entry_point_found) {
4047 try bundle.addRootErrorMessage(.{
4048 .msg = try bundle.addString("no entry point found"),
4049 });
4050 }
4051 }
4052
4053 if (comp.link_diags.flags.missing_libc) {
4054 try bundle.addRootErrorMessage(.{
4055 .msg = try bundle.addString("libc not available"),
4056 .notes_len = 2,
4057 });
4058 const notes_start = try bundle.reserveNotes(2);
4059 bundle.extra.items[notes_start + 0] = @backingInt(bundle.addErrorMessageAssumeCapacity(.{
4060 .msg = try bundle.addString("run 'zig libc -h' to learn about libc installations"),
4061 }));
4062 bundle.extra.items[notes_start + 1] = @backingInt(bundle.addErrorMessageAssumeCapacity(.{
4063 .msg = try bundle.addString("run 'zig targets' to see the targets for which zig can always provide libc"),
4064 }));
4065 }
4066
4067 try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file);
4068
4069 const compile_log_text: []const u8 = compile_log_text: {
4070 const zcu = comp.zcu orelse break :compile_log_text "";
4071 if (zcu.skip_analysis_this_update) break :compile_log_text "";
4072 if (zcu.compile_logs.count() == 0) break :compile_log_text "";
4073
4074 // If there are no other errors, we include a "found compile log statement" error.
4075 // Otherwise, we just show the compile log output, with no error.
4076 const include_compile_log_sources = bundle.root_list.items.len == 0;
4077
4078 const refs = try zcu.resolveReferences();
4079
4080 var messages: std.ArrayList(Zcu.ErrorMsg) = .empty;
4081 defer messages.deinit(gpa);
4082 for (zcu.compile_logs.keys(), zcu.compile_logs.values()) |logging_unit, compile_log| {
4083 if (!refs.contains(logging_unit)) continue;
4084 try messages.append(gpa, .{
4085 .src_loc = compile_log.src(),
4086 .msg = "", // populated later, but must be valid for `sort` call below
4087 .notes = &.{},
4088 // We actually clear this later for most of these, but we populate
4089 // this field for now to avoid having to allocate more data to track
4090 // which compile log text this corresponds to.
4091 .reference_trace_root = logging_unit.toOptional(),
4092 });
4093 }
4094
4095 if (messages.items.len == 0) break :compile_log_text "";
4096
4097 // Okay, there *are* referenced compile logs. Sort them into a consistent order.
4098
4099 std.mem.sort(Zcu.ErrorMsg, messages.items, zcu, struct {
4100 fn lessThan(zcu_inner: *Zcu, lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool {
4101 return Zcu.ErrorMsg.order(&lhs, &rhs, zcu_inner).compare(.lt);
4102 }
4103 }.lessThan);
4104
4105 var log_text: std.ArrayList(u8) = .empty;
4106 defer log_text.deinit(gpa);
4107
4108 // Index 0 will be the root message; the rest will be notes.
4109 // Only the actual message, i.e. index 0, will retain its reference trace.
4110 try appendCompileLogLines(&log_text, zcu, messages.items[0].reference_trace_root.unwrap().?);
4111 messages.items[0].notes = messages.items[1..];
4112 messages.items[0].msg = "found compile log statement";
4113 for (messages.items[1..]) |*note| {
4114 try appendCompileLogLines(&log_text, zcu, note.reference_trace_root.unwrap().?);
4115 note.reference_trace_root = .none; // notes don't have reference traces
4116 note.msg = "also here";
4117 }
4118
4119 // We don't actually include the error here if `!include_compile_log_sources`.
4120 // The sorting above was still necessary, though, to get `log_text` in the right order.
4121 if (include_compile_log_sources) {
4122 try addModuleErrorMsg(zcu, &bundle, messages.items[0], false);
4123 }
4124
4125 break :compile_log_text try log_text.toOwnedSlice(gpa);
4126 };
4127 defer gpa.free(compile_log_text);
4128
4129 // TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a
4130 // very common way for incremental compilation bugs to manifest, so let's always check it.
4131 if (comp.zcu) |zcu| if (comp.config.incremental and bundle.root_list.items.len == 0) {
4132 for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
4133 const refs = try zcu.resolveReferences();
4134 var ref = refs.get(failed_unit) orelse continue;
4135 // This AU is referenced and has a transitive compile error, meaning it referenced something with a compile error.
4136 // However, we haven't reported any such error.
4137 // This is a compiler bug.
4138 print_ctx: {
4139 const stderr = std.debug.lockStderr(&.{}).terminal();
4140 defer std.debug.unlockStderr();
4141 const w = stderr.writer;
4142 w.writeAll("referenced transitive analysis errors, but none actually emitted\n") catch break :print_ctx;
4143 w.print("{f} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)}) catch break :print_ctx;
4144 while (ref) |r| {
4145 w.print("referenced by: {f}{s}\n", .{
4146 zcu.fmtAnalUnit(r.referencer),
4147 if (zcu.transitive_failed_analysis.contains(r.referencer)) " [transitive failure]" else "",
4148 }) catch break :print_ctx;
4149 ref = refs.get(r.referencer).?;
4150 }
4151 }
4152 if (comp.debugIncremental()) {
4153 std.debug.print("skipping compiler panic to allow incremental debug server usage", .{});
4154 try bundle.addRootErrorMessage(.{
4155 .msg = try bundle.addString("compiler bug: referenced transitive analysis errors, but none actually emitted"),
4156 });
4157 } else {
4158 @panic("referenced transitive analysis errors, but none actually emitted");
4159 }
4160 }
4161 };
4162
4163 return bundle.toOwnedBundle(compile_log_text);
4164}
4165
4166/// Writes all compile log lines belonging to `logging_unit` into `log_text` using `zcu.gpa`.
4167fn appendCompileLogLines(log_text: *std.ArrayList(u8), zcu: *Zcu, logging_unit: InternPool.AnalUnit) Allocator.Error!void {
4168 const gpa = zcu.gpa;
4169 const ip = &zcu.intern_pool;
4170 var opt_line_idx = zcu.compile_logs.get(logging_unit).?.first_line.toOptional();
4171 while (opt_line_idx.unwrap()) |line_idx| {
4172 const line = line_idx.get(zcu).*;
4173 opt_line_idx = line.next;
4174 const line_slice = line.data.toSlice(ip);
4175 try log_text.ensureUnusedCapacity(gpa, line_slice.len + 1);
4176 log_text.appendSliceAssumeCapacity(line_slice);
4177 log_text.appendAssumeCapacity('\n');
4178 }
4179}
4180
4181pub fn anyErrors(comp: *Compilation) bool {
4182 var errors = comp.getAllErrorsAlloc() catch return true;
4183 defer errors.deinit(comp.gpa);
4184 return errors.errorMessageCount() > 0;
4185}
4186
4187pub const ErrorNoteHashContext = struct {
4188 eb: *const ErrorBundle.Wip,
4189
4190 pub fn hash(ctx: ErrorNoteHashContext, key: ErrorBundle.ErrorMessage) u32 {
4191 var hasher = std.hash.Wyhash.init(0);
4192 const eb = ctx.eb.tmpBundle();
4193
4194 hasher.update(eb.nullTerminatedString(key.msg));
4195 if (key.src_loc != .none) {
4196 const src = eb.getSourceLocation(key.src_loc);
4197 hasher.update(eb.nullTerminatedString(src.src_path));
4198 std.hash.autoHash(&hasher, src.line);
4199 std.hash.autoHash(&hasher, src.column);
4200 std.hash.autoHash(&hasher, src.span_main);
4201 }
4202
4203 return @as(u32, @truncate(hasher.final()));
4204 }
4205
4206 pub fn eql(
4207 ctx: ErrorNoteHashContext,
4208 a: ErrorBundle.ErrorMessage,
4209 b: ErrorBundle.ErrorMessage,
4210 b_index: usize,
4211 ) bool {
4212 _ = b_index;
4213 const eb = ctx.eb.tmpBundle();
4214 const msg_a = eb.nullTerminatedString(a.msg);
4215 const msg_b = eb.nullTerminatedString(b.msg);
4216 if (!mem.eql(u8, msg_a, msg_b)) return false;
4217
4218 if (a.src_loc == .none and b.src_loc == .none) return true;
4219 if (a.src_loc == .none or b.src_loc == .none) return false;
4220 const src_a = eb.getSourceLocation(a.src_loc);
4221 const src_b = eb.getSourceLocation(b.src_loc);
4222
4223 const src_path_a = eb.nullTerminatedString(src_a.src_path);
4224 const src_path_b = eb.nullTerminatedString(src_b.src_path);
4225
4226 return mem.eql(u8, src_path_a, src_path_b) and
4227 src_a.line == src_b.line and
4228 src_a.column == src_b.column and
4229 src_a.span_main == src_b.span_main;
4230 }
4231};
4232
4233const default_reference_trace_len = 2;
4234pub fn addModuleErrorMsg(
4235 zcu: *Zcu,
4236 eb: *ErrorBundle.Wip,
4237 module_err_msg: Zcu.ErrorMsg,
4238 /// If `-freference-trace` is not specified, we only want to show the one reference trace.
4239 /// So, this is whether we have already emitted an error with a reference trace.
4240 already_added_error: bool,
4241) Allocator.Error!void {
4242 const gpa = eb.gpa;
4243 const err_src_loc = module_err_msg.src_loc.upgrade(zcu);
4244 const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| {
4245 return unableToLoadZcuFile(zcu, eb, err_src_loc.file_scope, err);
4246 };
4247 const err_span = err_src_loc.span(zcu) catch |err| {
4248 return unableToLoadZcuFile(zcu, eb, err_src_loc.file_scope, err);
4249 };
4250 const err_loc = std.zig.findLineColumn(err_source, err_span.main);
4251
4252 var ref_traces: std.ArrayList(ErrorBundle.ReferenceTrace) = .empty;
4253 defer ref_traces.deinit(gpa);
4254
4255 if (module_err_msg.reference_trace_root.unwrap()) |root| {
4256 const frame_limit: u32 = zcu.comp.reference_trace orelse refs: {
4257 if (already_added_error) break :refs 0;
4258 break :refs default_reference_trace_len;
4259 };
4260 try zcu.populateReferenceTrace(root, frame_limit, eb, &ref_traces);
4261 }
4262
4263 const src_loc = try eb.addSourceLocation(.{
4264 .src_path = try eb.printString("{f}", .{err_src_loc.file_scope.path.fmt(zcu.comp)}),
4265 .span_start = err_span.start,
4266 .span_main = err_span.main,
4267 .span_end = err_span.end,
4268 .line = @intCast(err_loc.line),
4269 .column = @intCast(err_loc.column),
4270 .source_line = try eb.addString(err_loc.source_line),
4271 .reference_trace_len = @intCast(ref_traces.items.len),
4272 });
4273
4274 for (ref_traces.items) |rt| {
4275 try eb.addReferenceTrace(rt);
4276 }
4277
4278 // De-duplicate error notes. The main use case in mind for this is
4279 // too many "note: called from here" notes when eval branch quota is reached.
4280 var notes: std.array_hash_map.Custom(ErrorBundle.ErrorMessage, void, ErrorNoteHashContext, true) = .empty;
4281 defer notes.deinit(gpa);
4282
4283 var last_note_loc: ?std.zig.Loc = null;
4284 for (module_err_msg.notes) |module_note| {
4285 const note_src_loc = module_note.src_loc.upgrade(zcu);
4286 const source = note_src_loc.file_scope.getSource(zcu) catch |err| {
4287 return unableToLoadZcuFile(zcu, eb, note_src_loc.file_scope, err);
4288 };
4289 const span = note_src_loc.span(zcu) catch |err| {
4290 return unableToLoadZcuFile(zcu, eb, note_src_loc.file_scope, err);
4291 };
4292 const loc = std.zig.findLineColumn(source, span.main);
4293
4294 const omit_source_line = loc.eql(err_loc) or (last_note_loc != null and loc.eql(last_note_loc.?));
4295 last_note_loc = loc;
4296
4297 const gop = try notes.getOrPutContext(gpa, .{
4298 .msg = try eb.addString(module_note.msg),
4299 .src_loc = try eb.addSourceLocation(.{
4300 .src_path = try eb.printString("{f}", .{note_src_loc.file_scope.path.fmt(zcu.comp)}),
4301 .span_start = span.start,
4302 .span_main = span.main,
4303 .span_end = span.end,
4304 .line = @intCast(loc.line),
4305 .column = @intCast(loc.column),
4306 .source_line = if (omit_source_line) 0 else try eb.addString(loc.source_line),
4307 }),
4308 }, .{ .eb = eb });
4309 if (gop.found_existing) {
4310 gop.key_ptr.count += 1;
4311 }
4312 }
4313
4314 const notes_len: u32 = @intCast(notes.entries.len);
4315
4316 try eb.addRootErrorMessage(.{
4317 .msg = try eb.addString(module_err_msg.msg),
4318 .src_loc = src_loc,
4319 .notes_len = notes_len,
4320 });
4321
4322 const notes_start = try eb.reserveNotes(notes_len);
4323
4324 for (notes_start.., notes.keys()) |i, note| {
4325 eb.extra.items[i] = @backingInt(eb.addErrorMessageAssumeCapacity(note));
4326 }
4327}
4328
4329fn addWholeFileError(
4330 zcu: *Zcu,
4331 eb: *ErrorBundle.Wip,
4332 file_index: Zcu.File.Index,
4333 msg: []const u8,
4334) Allocator.Error!void {
4335 // note: "file imported here" on the import reference token
4336 const imported_note: ?ErrorBundle.MessageIndex = switch (zcu.alive_files.get(file_index).?) {
4337 .analysis_root => null,
4338 .import => |import| note: {
4339 const file = zcu.fileByIndex(import.importer);
4340 // `errorBundleTokenSrc` expects the tree to be loaded
4341 _ = file.getTree(zcu) catch |err| {
4342 return unableToLoadZcuFile(zcu, eb, file, err);
4343 };
4344 break :note try eb.addErrorMessage(.{
4345 .msg = try eb.addString("file imported here"),
4346 .src_loc = try file.errorBundleTokenSrc(import.tok, zcu, eb),
4347 });
4348 },
4349 };
4350
4351 try eb.addRootErrorMessage(.{
4352 .msg = try eb.addString(msg),
4353 .src_loc = try zcu.fileByIndex(file_index).errorBundleWholeFileSrc(zcu, eb),
4354 .notes_len = if (imported_note != null) 1 else 0,
4355 });
4356 if (imported_note) |n| {
4357 const note_idx = try eb.reserveNotes(1);
4358 eb.extra.items[note_idx] = @backingInt(n);
4359 }
4360}
4361
4362/// Adds an error to `eb` that the contents of `file` could not be loaded due to `err`. This is
4363/// useful if `Zcu.File.getSource`/`Zcu.File.getTree` fails while lowering compile errors.
4364pub fn unableToLoadZcuFile(
4365 zcu: *const Zcu,
4366 eb: *ErrorBundle.Wip,
4367 file: *Zcu.File,
4368 err: Zcu.File.GetSourceError,
4369) Allocator.Error!void {
4370 const msg = switch (err) {
4371 error.OutOfMemory => |e| return e,
4372 error.FileChanged => try eb.addString("file contents changed during update"),
4373 else => |e| try eb.printString("unable to load: {t}", .{e}),
4374 };
4375 try eb.addRootErrorMessage(.{
4376 .msg = msg,
4377 .src_loc = try file.errorBundleWholeFileSrc(zcu, eb),
4378 });
4379}
4380
4381fn performAllTheWork(
4382 comp: *Compilation,
4383 main_progress_node: std.Progress.Node,
4384 update_arena: Allocator,
4385) (Allocator.Error || Io.Cancelable)!void {
4386 const io = comp.io;
4387
4388 // This is awkward: we don't want to start the timer until later, but we won't want to stop it
4389 // until the wait groups finish. That means we need do do this.
4390 var decl_work_timer: ?Timer = null;
4391 defer commit_timer: {
4392 const t = &(decl_work_timer orelse break :commit_timer);
4393 const ns = t.finish(io) orelse break :commit_timer;
4394 comp.mutex.lockUncancelable(io);
4395 defer comp.mutex.unlock(io);
4396 comp.time_report.?.stats.real_ns_decls = ns;
4397 }
4398
4399 var misc_group: Io.Group = .init;
4400 defer misc_group.cancel(io);
4401
4402 try comp.link_queue.start(comp, update_arena);
4403 defer comp.link_queue.cancel(io);
4404
4405 misc_group.concurrent(io, dispatchPrelinkWork, .{ comp, main_progress_node }) catch |err| switch (err) {
4406 error.ConcurrencyUnavailable => {
4407 // Do it immediately so that the link queue isn't blocked
4408 dispatchPrelinkWork(comp, main_progress_node);
4409 },
4410 };
4411
4412 if (comp.emit_docs != null) {
4413 dev.check(.docs_emit);
4414 misc_group.async(io, workerDocsCopy, .{comp});
4415 misc_group.async(io, workerDocsWasm, .{ comp, main_progress_node });
4416 }
4417
4418 defer if (comp.zcu) |zcu| zcu.codegen_task_pool.cancel(zcu);
4419 if (comp.zcu) |zcu| {
4420 // Regardless of errors, `comp.zcu` needs to update its generation number.
4421 defer zcu.generation += 1;
4422 const active = zcu.acquire();
4423 defer active.release();
4424 try active.pt.update(main_progress_node, &decl_work_timer);
4425 }
4426
4427 comp.link_queue.finishZcuQueue(comp);
4428
4429 // Main thread work is all done, now just wait for all async work.
4430 try misc_group.await(io);
4431
4432 // This has to happen again after the main semantic analysis loop because it is possible for Sema to
4433 // call `addLinkLib` and hence add more items to `comp.windows_libs`.
4434 for (comp.windows_libs.keys()[comp.windows_libs_num_done..]) |lib_name|
4435 misc_group.async(io, buildMingwImportLib, .{ comp, lib_name, false, main_progress_node });
4436 comp.windows_libs_num_done = @intCast(comp.windows_libs.count());
4437 try misc_group.await(io);
4438
4439 comp.link_queue.wait(io);
4440}
4441
4442fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node) void {
4443 const io = comp.io;
4444
4445 // TODO should this function be cancelable?
4446 const prev_cancel_prot = io.swapCancelProtection(.blocked);
4447 defer _ = io.swapCancelProtection(prev_cancel_prot);
4448
4449 var prelink_group: Io.Group = .init;
4450 defer prelink_group.cancel(io);
4451
4452 comp.queuePrelinkTasks(comp.oneshot_prelink_tasks.items) catch |err| switch (err) {
4453 error.Canceled => unreachable, // see swapCancelProtection above
4454 };
4455 comp.oneshot_prelink_tasks.clearRetainingCapacity();
4456
4457 // In case it failed last time, try again. `clearMiscFailures` was already
4458 // called at the start of `update`.
4459 if (comp.queued_jobs.compiler_rt_lib and comp.compiler_rt_lib == null) {
4460 // LLVM disables LTO for its compiler-rt and we've had various issues with LTO of our
4461 // compiler-rt due to LLD bugs as well, e.g.:
4462 //
4463 // https://github.com/llvm/llvm-project/issues/43698#issuecomment-2542660611
4464 prelink_group.async(io, buildRt, .{
4465 comp,
4466 "compiler_rt.zig",
4467 "compiler_rt",
4468 .Lib,
4469 .static,
4470 .compiler_rt,
4471 main_progress_node,
4472 RtOptions{
4473 .checks_valgrind = true,
4474 .allow_lto = false,
4475 },
4476 &comp.compiler_rt_lib,
4477 });
4478 }
4479
4480 if (comp.queued_jobs.compiler_rt_obj and comp.compiler_rt_obj == null) {
4481 prelink_group.async(io, buildRt, .{
4482 comp,
4483 "compiler_rt.zig",
4484 "compiler_rt",
4485 .Obj,
4486 .static,
4487 .compiler_rt,
4488 main_progress_node,
4489 RtOptions{
4490 .checks_valgrind = true,
4491 .allow_lto = false,
4492 },
4493 &comp.compiler_rt_obj,
4494 });
4495 }
4496
4497 if (comp.queued_jobs.fuzzer_lib and comp.fuzzer_lib == null) {
4498 prelink_group.async(io, buildRt, .{
4499 comp,
4500 "fuzzer.zig",
4501 "fuzzer",
4502 .Lib,
4503 .static,
4504 .libfuzzer,
4505 main_progress_node,
4506 RtOptions{},
4507 &comp.fuzzer_lib,
4508 });
4509 }
4510
4511 if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) {
4512 prelink_group.async(io, buildRt, .{
4513 comp,
4514 "ubsan_rt.zig",
4515 "ubsan_rt",
4516 .Lib,
4517 .static,
4518 .libubsan,
4519 main_progress_node,
4520 RtOptions{
4521 .allow_lto = false,
4522 },
4523 &comp.ubsan_rt_lib,
4524 });
4525 }
4526
4527 if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) {
4528 prelink_group.async(io, buildRt, .{
4529 comp,
4530 "ubsan_rt.zig",
4531 "ubsan_rt",
4532 .Obj,
4533 .static,
4534 .libubsan,
4535 main_progress_node,
4536 RtOptions{
4537 .allow_lto = false,
4538 },
4539 &comp.ubsan_rt_obj,
4540 });
4541 }
4542
4543 if (comp.queued_jobs.glibc_shared_objects) {
4544 prelink_group.async(io, buildGlibcSharedObjects, .{ comp, main_progress_node });
4545 }
4546
4547 if (comp.queued_jobs.freebsd_shared_objects) {
4548 prelink_group.async(io, buildFreeBSDSharedObjects, .{ comp, main_progress_node });
4549 }
4550
4551 if (comp.queued_jobs.netbsd_shared_objects) {
4552 prelink_group.async(io, buildNetBSDSharedObjects, .{ comp, main_progress_node });
4553 }
4554
4555 if (comp.queued_jobs.openbsd_shared_objects) {
4556 prelink_group.async(io, buildOpenBSDSharedObjects, .{ comp, main_progress_node });
4557 }
4558
4559 if (comp.queued_jobs.libunwind) {
4560 prelink_group.async(io, buildLibUnwind, .{ comp, main_progress_node });
4561 }
4562
4563 if (comp.queued_jobs.libcxx) {
4564 prelink_group.async(io, buildLibCxx, .{ comp, main_progress_node });
4565 }
4566
4567 if (comp.queued_jobs.libcxxabi) {
4568 prelink_group.async(io, buildLibCxxAbi, .{ comp, main_progress_node });
4569 }
4570
4571 if (comp.queued_jobs.libtsan) {
4572 prelink_group.async(io, buildLibTsan, .{ comp, main_progress_node });
4573 }
4574
4575 if (comp.queued_jobs.zigc_lib and comp.zigc_static_lib == null) {
4576 prelink_group.async(io, buildLibZigC, .{ comp, main_progress_node });
4577 }
4578
4579 for (0..@typeInfo(musl.CrtFile).@"enum".field_names.len) |i| {
4580 if (comp.queued_jobs.musl_crt_file[i]) {
4581 const tag: musl.CrtFile = @fromBackingInt(@intCast(i));
4582 prelink_group.async(io, buildMuslCrtFile, .{ comp, tag, main_progress_node });
4583 }
4584 }
4585
4586 for (0..@typeInfo(glibc.CrtFile).@"enum".field_names.len) |i| {
4587 if (comp.queued_jobs.glibc_crt_file[i]) {
4588 const tag: glibc.CrtFile = @fromBackingInt(@intCast(i));
4589 prelink_group.async(io, buildGlibcCrtFile, .{ comp, tag, main_progress_node });
4590 }
4591 }
4592
4593 for (0..@typeInfo(freebsd.CrtFile).@"enum".field_names.len) |i| {
4594 if (comp.queued_jobs.freebsd_crt_file[i]) {
4595 const tag: freebsd.CrtFile = @fromBackingInt(@intCast(i));
4596 prelink_group.async(io, buildFreeBSDCrtFile, .{ comp, tag, main_progress_node });
4597 }
4598 }
4599
4600 for (0..@typeInfo(netbsd.CrtFile).@"enum".field_names.len) |i| {
4601 if (comp.queued_jobs.netbsd_crt_file[i]) {
4602 const tag: netbsd.CrtFile = @fromBackingInt(@intCast(i));
4603 prelink_group.async(io, buildNetBSDCrtFile, .{ comp, tag, main_progress_node });
4604 }
4605 }
4606
4607 for (0..@typeInfo(openbsd.CrtFile).@"enum".field_names.len) |i| {
4608 if (comp.queued_jobs.openbsd_crt_file[i]) {
4609 const tag: openbsd.CrtFile = @fromBackingInt(@intCast(i));
4610 prelink_group.async(io, buildOpenBSDCrtFile, .{ comp, tag, main_progress_node });
4611 }
4612 }
4613
4614 for (0..@typeInfo(wasi_libc.CrtFile).@"enum".field_names.len) |i| {
4615 if (comp.queued_jobs.wasi_libc_crt_file[i]) {
4616 const tag: wasi_libc.CrtFile = @fromBackingInt(@intCast(i));
4617 prelink_group.async(io, buildWasiLibcCrtFile, .{ comp, tag, main_progress_node });
4618 }
4619 }
4620
4621 for (0..@typeInfo(mingw.CrtFile).@"enum".field_names.len) |i| {
4622 if (comp.queued_jobs.mingw_crt_file[i]) {
4623 const tag: mingw.CrtFile = @fromBackingInt(@intCast(i));
4624 prelink_group.async(io, buildMingwCrtFile, .{ comp, tag, main_progress_node });
4625 }
4626 }
4627
4628 while (comp.c_object_work_queue.popFront()) |c_object| {
4629 prelink_group.async(io, workerUpdateCObject, .{
4630 comp, c_object, main_progress_node,
4631 });
4632 }
4633
4634 while (comp.win32_resource_work_queue.popFront()) |win32_resource| {
4635 prelink_group.async(io, workerUpdateWin32Resource, .{
4636 comp, win32_resource, main_progress_node,
4637 });
4638 }
4639
4640 while (comp.windows_libs_num_done < comp.windows_libs.count()) {
4641 prelink_group.async(io, buildMingwImportLib, .{
4642 comp,
4643 comp.windows_libs.keys()[comp.windows_libs_num_done],
4644 true,
4645 main_progress_node,
4646 });
4647 comp.windows_libs_num_done += 1;
4648 }
4649
4650 prelink_group.await(io) catch |err| switch (err) {
4651 error.Canceled => unreachable, // see swapCancelProtection above
4652 };
4653 comp.link_queue.finishPrelinkQueue(comp) catch |err| switch (err) {
4654 error.Canceled => unreachable, // see swapCancelProtection above
4655 };
4656}
4657
4658fn createDepFile(comp: *Compilation, dep_file: []const u8, bin_file: Cache.Path) anyerror!void {
4659 const io = comp.io;
4660
4661 var af = try Io.Dir.cwd().createFileAtomic(io, dep_file, .{ .replace = true });
4662 defer af.deinit(io);
4663
4664 var buf: [4096]u8 = undefined;
4665 var file_writer = af.file.writer(io, &buf);
4666
4667 comp.writeDepFile(bin_file, &file_writer.interface) catch |err| switch (err) {
4668 error.WriteFailed => return file_writer.err.?,
4669 };
4670 try file_writer.flush();
4671 try af.replace(io);
4672}
4673
4674fn writeDepFile(
4675 comp: *Compilation,
4676 bin_file: Cache.Path,
4677 w: *std.Io.Writer,
4678) std.Io.Writer.Error!void {
4679 const prefixes = comp.cache_parent.prefixes();
4680 const fsi = comp.file_system_inputs.?.items;
4681
4682 try w.print("{f}:", .{bin_file});
4683
4684 if (fsi.len > 0) {
4685 var it = std.mem.splitScalar(u8, fsi, 0);
4686 while (it.next()) |input| try w.print(" \\\n {f}{s}", .{ prefixes[input[0] - 1], input[1..] });
4687 }
4688
4689 if (fsi.len > 0) {
4690 var it = std.mem.splitScalar(u8, fsi, 0);
4691 while (it.next()) |input| try w.print("\n\n{f}{s}:", .{ prefixes[input[0] - 1], input[1..] });
4692 }
4693
4694 try w.writeByte('\n');
4695}
4696
4697fn workerDocsCopy(comp: *Compilation) void {
4698 docsCopyFallible(comp) catch |err| return comp.lockAndSetMiscFailure(
4699 .docs_copy,
4700 "unable to copy autodocs artifacts: {s}",
4701 .{@errorName(err)},
4702 );
4703}
4704
4705fn docsCopyFallible(comp: *Compilation) anyerror!void {
4706 const zcu = comp.zcu orelse return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{});
4707 const io = comp.io;
4708
4709 const docs_path = comp.resolveEmitPath(comp.emit_docs.?);
4710 var out_dir = docs_path.root_dir.handle.createDirPathOpen(io, docs_path.sub_path, .{}) catch |err| {
4711 return comp.lockAndSetMiscFailure(
4712 .docs_copy,
4713 "unable to create output directory '{f}': {s}",
4714 .{ docs_path, @errorName(err) },
4715 );
4716 };
4717 defer out_dir.close(io);
4718
4719 for (&[_][]const u8{ "docs/main.js", "docs/index.html" }) |sub_path| {
4720 const basename = fs.path.basename(sub_path);
4721 comp.dirs.zig_lib.handle.copyFile(sub_path, out_dir, basename, io, .{}) catch |err|
4722 return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy {s}: {t}", .{ sub_path, err });
4723 }
4724
4725 var tar_file = out_dir.createFile(io, "sources.tar", .{}) catch |err| {
4726 return comp.lockAndSetMiscFailure(
4727 .docs_copy,
4728 "unable to create '{f}/sources.tar': {s}",
4729 .{ docs_path, @errorName(err) },
4730 );
4731 };
4732 defer tar_file.close(io);
4733
4734 var buffer: [1024]u8 = undefined;
4735 var tar_file_writer = tar_file.writer(io, &buffer);
4736
4737 var seen_table: std.array_hash_map.Auto(*Module, []const u8) = .empty;
4738 defer seen_table.deinit(comp.gpa);
4739
4740 try seen_table.put(comp.gpa, zcu.main_mod, comp.root_name);
4741 try seen_table.put(comp.gpa, zcu.std_mod, zcu.std_mod.fully_qualified_name);
4742
4743 var i: usize = 0;
4744 while (i < seen_table.count()) : (i += 1) {
4745 const mod = seen_table.keys()[i];
4746 try comp.docsCopyModule(mod, seen_table.values()[i], &tar_file_writer);
4747
4748 const deps = mod.deps.values();
4749 try seen_table.ensureUnusedCapacity(comp.gpa, deps.len);
4750 for (deps) |dep| seen_table.putAssumeCapacity(dep, dep.fully_qualified_name);
4751 }
4752
4753 tar_file_writer.end() catch |err| {
4754 return comp.lockAndSetMiscFailure(
4755 .docs_copy,
4756 "unable to write '{f}/sources.tar': {t}",
4757 .{ docs_path, err },
4758 );
4759 };
4760}
4761
4762fn docsCopyModule(
4763 comp: *Compilation,
4764 module: *Module,
4765 name: []const u8,
4766 tar_file_writer: *Io.File.Writer,
4767) !void {
4768 const io = comp.io;
4769 const root = module.root;
4770 var mod_dir = d: {
4771 const root_dir, const sub_path = root.openInfo(comp.dirs);
4772 break :d root_dir.openDir(io, sub_path, .{ .iterate = true });
4773 } catch |err| {
4774 return comp.lockAndSetMiscFailure(.docs_copy, "unable to open directory '{f}': {t}", .{ root.fmt(comp), err });
4775 };
4776 defer mod_dir.close(io);
4777
4778 var walker = try mod_dir.walk(comp.gpa);
4779 defer walker.deinit();
4780
4781 var archiver: std.tar.Writer = .{ .underlying_writer = &tar_file_writer.interface };
4782 archiver.prefix = name;
4783
4784 var path_buf: std.ArrayList(u8) = .empty;
4785 defer path_buf.deinit(comp.gpa);
4786
4787 var buffer: [1024]u8 = undefined;
4788
4789 while (try walker.next(io)) |entry| {
4790 switch (entry.kind) {
4791 .file => {
4792 if (!std.mem.endsWith(u8, entry.basename, ".zig")) continue;
4793 if (std.mem.eql(u8, entry.basename, "test.zig")) continue;
4794 if (std.mem.endsWith(u8, entry.basename, "_test.zig")) continue;
4795 },
4796 else => continue,
4797 }
4798 var file = mod_dir.openFile(io, entry.path, .{}) catch |err| {
4799 return comp.lockAndSetMiscFailure(.docs_copy, "unable to open {f}{s}: {t}", .{
4800 root.fmt(comp), entry.path, err,
4801 });
4802 };
4803 defer file.close(io);
4804 const stat = try file.stat(io);
4805 var file_reader: Io.File.Reader = .initSize(file, io, &buffer, stat.size);
4806
4807 const posix_path = if (comptime std.fs.path.sep == std.fs.path.sep_posix)
4808 entry.path
4809 else blk: {
4810 path_buf.clearRetainingCapacity();
4811 try path_buf.appendSlice(comp.gpa, entry.path);
4812 std.mem.replaceScalar(u8, path_buf.items, std.fs.path.sep, std.fs.path.sep_posix);
4813 break :blk path_buf.items;
4814 };
4815
4816 archiver.writeFileTimestamp(posix_path, &file_reader, stat.mtime) catch |err| {
4817 return comp.lockAndSetMiscFailure(.docs_copy, "unable to archive {f}{s}: {t}", .{
4818 root.fmt(comp), entry.path, err,
4819 });
4820 };
4821 }
4822}
4823
4824fn workerDocsWasm(comp: *Compilation, parent_prog_node: std.Progress.Node) void {
4825 const prog_node = parent_prog_node.start("Compile Autodocs", 0);
4826 defer prog_node.end();
4827
4828 workerDocsWasmFallible(comp, prog_node) catch |err| switch (err) {
4829 error.AlreadyReported => return,
4830 else => comp.lockAndSetMiscFailure(.docs_wasm, "unable to build autodocs: {t}", .{err}),
4831 };
4832}
4833
4834fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubUpdateError!void {
4835 const gpa = comp.gpa;
4836 const io = comp.io;
4837
4838 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
4839 defer arena_allocator.deinit();
4840 const arena = arena_allocator.allocator();
4841
4842 const optimize_mode: std.lang.Optimize = .small;
4843 const output_mode = std.lang.OutputMode.Exe;
4844 const resolved_target: Module.ResolvedTarget = .{
4845 .result = std.zig.system.resolveTargetQuery(io, .{
4846 .cpu_arch = .wasm32,
4847 .os_tag = .freestanding,
4848 .cpu_features_add = std.Target.wasm.featureSet(&.{
4849 .atomics,
4850 // .extended_const, not supported by Safari
4851 .reference_types,
4852 //.relaxed_simd, not supported by Firefox or Safari
4853 // observed to cause Error occured during wast conversion :
4854 // Unknown operator: 0xfd058 in Firefox 117
4855 //.simd128,
4856 // .tail_call, not supported by Safari
4857 }),
4858 }) catch unreachable,
4859
4860 .is_native_os = false,
4861 .is_native_abi = false,
4862 .is_explicit_dynamic_linker = false,
4863 };
4864
4865 const config = Config.resolve(.{
4866 .output_mode = output_mode,
4867 .resolved_target = resolved_target,
4868 .is_test = false,
4869 .have_zcu = true,
4870 .emit_bin = true,
4871 .root_optimize_mode = optimize_mode,
4872 .link_libc = false,
4873 .rdynamic = true,
4874 }) catch |err| {
4875 comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: failed to resolve compilation config: {t}", .{err});
4876 return error.AlreadyReported;
4877 };
4878
4879 const src_basename = "main.zig";
4880 const root_name = fs.path.stem(src_basename);
4881
4882 const dirs = comp.dirs.withoutLocalCache();
4883
4884 const root_mod = Module.create(arena, .{
4885 .paths = .{
4886 .root = try .fromRoot(arena, dirs, .zig_lib, "docs/wasm"),
4887 .root_src_path = src_basename,
4888 },
4889 .fully_qualified_name = root_name,
4890 .inherited = .{
4891 .resolved_target = resolved_target,
4892 .optimize_mode = optimize_mode,
4893 },
4894 .global = config,
4895 .cc_argv = &.{},
4896 .parent = null,
4897 }) catch |err| {
4898 comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: failed to create root module: {t}", .{err});
4899 return error.AlreadyReported;
4900 };
4901 const walk_mod = Module.create(arena, .{
4902 .paths = .{
4903 .root = try .fromRoot(arena, dirs, .zig_lib, "docs/wasm"),
4904 .root_src_path = "Walk.zig",
4905 },
4906 .fully_qualified_name = "Walk",
4907 .inherited = .{
4908 .resolved_target = resolved_target,
4909 .optimize_mode = optimize_mode,
4910 },
4911 .global = config,
4912 .cc_argv = &.{},
4913 .parent = root_mod,
4914 }) catch |err| {
4915 comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: failed to create 'Walk' module: {t}", .{err});
4916 return error.AlreadyReported;
4917 };
4918 try root_mod.deps.put(arena, "Walk", walk_mod);
4919
4920 var sub_create_diag: CreateDiagnostic = undefined;
4921 const sub_compilation = Compilation.create(gpa, arena, io, &sub_create_diag, .{
4922 .thread_limit = comp.thread_limit,
4923 .dirs = dirs,
4924 .self_exe_path = comp.self_exe_path,
4925 .config = config,
4926 .root_mod = root_mod,
4927 .entry = .disabled,
4928 .cache_mode = .whole,
4929 .root_name = root_name,
4930 .libc_installation = comp.libc_installation,
4931 .emit_bin = .yes_cache,
4932 .verbose_cc = comp.verbose_cc,
4933 .verbose_link = comp.verbose_link,
4934 .verbose_air = comp.verbose_air,
4935 .verbose_intern_pool = comp.verbose_intern_pool,
4936 .verbose_generic_instances = comp.verbose_intern_pool,
4937 .verbose_llvm_ir = comp.verbose_llvm_ir,
4938 .verbose_llvm_bc = comp.verbose_llvm_bc,
4939 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
4940 .environ_map = comp.environ_map,
4941 }) catch |err| switch (err) {
4942 error.CreateFail => {
4943 comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: {f}", .{sub_create_diag});
4944 return error.AlreadyReported;
4945 },
4946 else => |e| return e,
4947 };
4948 defer sub_compilation.destroy();
4949
4950 try comp.updateSubCompilation(sub_compilation, .docs_wasm, prog_node);
4951
4952 var crt_file = try sub_compilation.toCrtFile();
4953 defer crt_file.deinit(gpa, io);
4954
4955 const docs_bin_file = crt_file.full_object_path;
4956 assert(docs_bin_file.sub_path.len > 0); // emitted binary is not a directory
4957
4958 const docs_path = comp.resolveEmitPath(comp.emit_docs.?);
4959 var out_dir = docs_path.root_dir.handle.createDirPathOpen(io, docs_path.sub_path, .{}) catch |err| {
4960 comp.lockAndSetMiscFailure(
4961 .docs_copy,
4962 "unable to create output directory '{f}': {t}",
4963 .{ docs_path, err },
4964 );
4965 return error.AlreadyReported;
4966 };
4967 defer out_dir.close(io);
4968
4969 Io.Dir.copyFile(
4970 crt_file.full_object_path.root_dir.handle,
4971 crt_file.full_object_path.sub_path,
4972 out_dir,
4973 "main.wasm",
4974 io,
4975 .{},
4976 ) catch |err| {
4977 comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{f}' to '{f}': {t}", .{
4978 crt_file.full_object_path, docs_path, err,
4979 });
4980 return error.AlreadyReported;
4981 };
4982}
4983
4984pub fn obtainCObjectCacheManifest(
4985 comp: *const Compilation,
4986 owner_mod: *Module,
4987) Cache.Manifest {
4988 var man = comp.cache_parent.obtain();
4989
4990 // Only things that need to be added on top of the base hash, and only
4991 // things that apply to compiling C objects. No linking stuff here!
4992 // Also nothing that applies only to compiling .zig code.
4993 cache_helpers.addModule(&man.hash, owner_mod);
4994 man.hash.addListOfBytes(comp.global_cc_argv);
4995 man.hash.add(comp.config.link_libcpp);
4996
4997 // When libc_installation is null it means that Zig generated this dir list
4998 // based on the zig library directory alone. The zig lib directory file
4999 // path is purposefully either in the cache or not in the cache. The
5000 // decision should not be overridden here.
5001 if (comp.libc_installation != null) {
5002 man.hash.addListOfBytes(comp.libc_include_dir_list);
5003 }
5004
5005 return man;
5006}
5007
5008pub fn obtainWin32ResourceCacheManifest(comp: *const Compilation) Cache.Manifest {
5009 var man = comp.cache_parent.obtain();
5010
5011 man.hash.add(comp.rc_includes);
5012
5013 return man;
5014}
5015
5016pub const TranslateCResult = struct {
5017 // Only valid if `errors` is not empty
5018 digest: [Cache.bin_digest_len]u8,
5019 cache_hit: bool,
5020 errors: std.zig.ErrorBundle,
5021
5022 pub fn deinit(result: *TranslateCResult, gpa: mem.Allocator) void {
5023 result.errors.deinit(gpa);
5024 }
5025};
5026
5027pub fn translateC(
5028 comp: *Compilation,
5029 arena: Allocator,
5030 man: *Cache.Manifest,
5031 ext: FileExt,
5032 source_path: []const u8,
5033 translated_basename: []const u8,
5034 owner_mod: *Module,
5035 prog_node: std.Progress.Node,
5036 environ_map: *const std.process.Environ.Map,
5037) !TranslateCResult {
5038 dev.check(.translate_c_command);
5039
5040 const gpa = comp.gpa;
5041 const io = comp.io;
5042 const tmp_basename = r: {
5043 var x: u64 = undefined;
5044 io.random(@ptrCast(&x));
5045 break :r std.fmt.hex(x);
5046 };
5047 const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename;
5048 const cache_dir = comp.dirs.local_cache.handle;
5049 var cache_tmp_dir = try cache_dir.createDirPathOpen(io, tmp_sub_path, .{});
5050 defer cache_tmp_dir.close(io);
5051
5052 const translated_path = try comp.dirs.local_cache.join(arena, &.{ tmp_sub_path, translated_basename });
5053
5054 const out_dep_path: ?[]const u8 = blk: {
5055 if (comp.disable_c_depfile) break :blk null;
5056 const c_src_basename = fs.path.basename(source_path);
5057 const dep_basename = try std.fmt.allocPrint(arena, "{s}.d", .{c_src_basename});
5058 const out_dep_path = try comp.dirs.local_cache.join(arena, &.{ tmp_sub_path, dep_basename });
5059 break :blk out_dep_path;
5060 };
5061
5062 var argv = std.array_list.Managed([]const u8).init(arena);
5063 {
5064 const target = &owner_mod.resolved_target.result;
5065 try argv.appendSlice(&.{ "--zig-integration", "-x", "c" });
5066
5067 const resource_path = try comp.dirs.zig_lib.join(arena, &.{ "compiler", "aro", "include" });
5068 try argv.appendSlice(&.{ "-isystem", resource_path });
5069 try comp.addCommonCCArgs(arena, &argv, ext, out_dep_path, owner_mod, .aro);
5070 try argv.appendSlice(&[_][]const u8{ "-target", try target.zigTriple(arena) });
5071
5072 const mcpu = mcpu: {
5073 var buf: std.ArrayList(u8) = .empty;
5074 defer buf.deinit(gpa);
5075
5076 try buf.print(gpa, "-mcpu={s}", .{target.cpu.model.name});
5077
5078 // TODO better serialization https://github.com/ziglang/zig/issues/4584
5079 const all_features_list = target.cpu.arch.allFeaturesList();
5080 try argv.ensureUnusedCapacity(all_features_list.len * 4);
5081 for (all_features_list, 0..) |feature, index_usize| {
5082 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
5083 const is_enabled = target.cpu.features.isEnabled(index);
5084
5085 const plus_or_minus = "-+"[@intFromBool(is_enabled)];
5086 try buf.print(gpa, "{c}{s}", .{ plus_or_minus, feature.name });
5087 }
5088 break :mcpu try arena.dupe(u8, buf.items);
5089 };
5090 try argv.append(mcpu);
5091
5092 try argv.appendSlice(comp.global_cc_argv);
5093 try argv.appendSlice(owner_mod.cc_argv);
5094 try argv.appendSlice(&.{ source_path, "-o", translated_path });
5095 }
5096
5097 var stdout: []u8 = undefined;
5098 try @import("main.zig").translateC(gpa, arena, io, argv.items, environ_map, prog_node, comp.thread_limit, &stdout);
5099
5100 if (out_dep_path) |dep_file_path| add_deps: {
5101 const dep_basename = fs.path.basename(dep_file_path);
5102 // Add the files depended on to the cache system, if a dep file was emitted
5103 man.addDepFilePost(cache_tmp_dir, dep_basename) catch |err| switch (err) {
5104 error.FileNotFound => break :add_deps,
5105 else => |e| return e,
5106 };
5107
5108 switch (comp.cache_use) {
5109 .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| {
5110 try whole.cache_manifest_mutex.lock(io);
5111 defer whole.cache_manifest_mutex.unlock(io);
5112 try whole_cache_manifest.addDepFilePost(cache_tmp_dir, dep_basename);
5113 },
5114 .incremental, .none => {},
5115 }
5116
5117 // Just to save disk space, we delete the file because it is never needed again.
5118 cache_tmp_dir.deleteFile(io, dep_basename) catch |err| {
5119 log.warn("failed to delete '{s}': {t}", .{ dep_file_path, err });
5120 };
5121 }
5122
5123 if (stdout.len > 0) {
5124 var reader: std.Io.Reader = .fixed(stdout);
5125 const MessageHeader = std.zig.Server.Message.Header;
5126 const header = reader.takeStruct(MessageHeader, .little) catch |err|
5127 fatal("unable to read translate-c MessageHeader: {s}", .{@errorName(err)});
5128 const body = reader.take(header.bytes_len) catch |err|
5129 fatal("unable to read {}-byte translate-c message body: {s}", .{ header.bytes_len, @errorName(err) });
5130 switch (header.tag) {
5131 .error_bundle => {
5132 const error_bundle = try std.zig.Server.allocErrorBundle(gpa, body);
5133 return .{
5134 .digest = undefined,
5135 .cache_hit = false,
5136 .errors = error_bundle,
5137 };
5138 },
5139 else => fatal("unexpected message type received from translate-c: {s}", .{@tagName(header.tag)}),
5140 }
5141 }
5142
5143 const bin_digest = man.finalBin();
5144 const hex_digest = Cache.binToHex(bin_digest);
5145 const o_sub_path = "o" ++ fs.path.sep_str ++ hex_digest;
5146
5147 try renameTmpIntoCache(io, comp.dirs.local_cache, tmp_sub_path, o_sub_path);
5148
5149 return .{
5150 .digest = bin_digest,
5151 .cache_hit = false,
5152 .errors = ErrorBundle.empty,
5153 };
5154}
5155
5156fn workerUpdateCObject(
5157 comp: *Compilation,
5158 c_object: *CObject,
5159 progress_node: std.Progress.Node,
5160) void {
5161 comp.updateCObject(c_object, progress_node) catch |err| switch (err) {
5162 error.AlreadyReported => return,
5163 else => {
5164 comp.reportRetryableCObjectError(c_object, err) catch |oom| switch (oom) {
5165 // Swallowing this error is OK because it's implied to be OOM when
5166 // there is a missing failed_c_objects error message.
5167 error.OutOfMemory => {},
5168 };
5169 },
5170 };
5171}
5172
5173fn workerUpdateWin32Resource(
5174 comp: *Compilation,
5175 win32_resource: *Win32Resource,
5176 progress_node: std.Progress.Node,
5177) void {
5178 comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) {
5179 error.AlreadyReported => return,
5180 else => {
5181 comp.reportRetryableWin32ResourceError(win32_resource, err) catch |oom| switch (oom) {
5182 // Swallowing this error is OK because it's implied to be OOM when
5183 // there is a missing failed_win32_resources error message.
5184 error.OutOfMemory => {},
5185 };
5186 },
5187 };
5188}
5189
5190pub const RtOptions = struct {
5191 checks_valgrind: bool = false,
5192 allow_lto: bool = true,
5193};
5194
5195fn buildRt(
5196 comp: *Compilation,
5197 root_source_name: []const u8,
5198 root_name: []const u8,
5199 output_mode: std.lang.OutputMode,
5200 link_mode: std.lang.LinkMode,
5201 misc_task: MiscTask,
5202 prog_node: std.Progress.Node,
5203 options: RtOptions,
5204 out: *?CrtFile,
5205) void {
5206 comp.buildOutputFromZig(
5207 root_source_name,
5208 root_name,
5209 output_mode,
5210 link_mode,
5211 misc_task,
5212 prog_node,
5213 options,
5214 out,
5215 ) catch |err| switch (err) {
5216 error.AlreadyReported => return,
5217 else => comp.lockAndSetMiscFailure(misc_task, "unable to build {s}: {s}", .{
5218 @tagName(misc_task), @errorName(err),
5219 }),
5220 };
5221}
5222
5223fn buildMuslCrtFile(comp: *Compilation, crt_file: musl.CrtFile, prog_node: std.Progress.Node) void {
5224 if (musl.buildCrtFile(comp, crt_file, prog_node)) |_| {
5225 comp.queued_jobs.musl_crt_file[@backingInt(crt_file)] = false;
5226 } else |err| switch (err) {
5227 error.AlreadyReported => return,
5228 else => comp.lockAndSetMiscFailure(.musl_crt_file, "unable to build musl {s}: {s}", .{
5229 @tagName(crt_file), @errorName(err),
5230 }),
5231 }
5232}
5233
5234fn buildGlibcCrtFile(comp: *Compilation, crt_file: glibc.CrtFile, prog_node: std.Progress.Node) void {
5235 if (glibc.buildCrtFile(comp, crt_file, prog_node)) |_| {
5236 comp.queued_jobs.glibc_crt_file[@backingInt(crt_file)] = false;
5237 } else |err| switch (err) {
5238 error.AlreadyReported => return,
5239 else => comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc {s}: {s}", .{
5240 @tagName(crt_file), @errorName(err),
5241 }),
5242 }
5243}
5244
5245fn buildGlibcSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
5246 if (glibc.buildSharedObjects(comp, prog_node)) |_| {
5247 // The job should no longer be queued up since it succeeded.
5248 comp.queued_jobs.glibc_shared_objects = false;
5249 } else |err| switch (err) {
5250 error.AlreadyReported => return,
5251 else => comp.lockAndSetMiscFailure(.glibc_shared_objects, "unable to build glibc shared objects: {t}", .{err}),
5252 }
5253}
5254
5255fn buildFreeBSDCrtFile(comp: *Compilation, crt_file: freebsd.CrtFile, prog_node: std.Progress.Node) void {
5256 if (freebsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
5257 comp.queued_jobs.freebsd_crt_file[@backingInt(crt_file)] = false;
5258 } else |err| switch (err) {
5259 error.AlreadyReported => return,
5260 else => comp.lockAndSetMiscFailure(.freebsd_crt_file, "unable to build FreeBSD {s}: {s}", .{
5261 @tagName(crt_file), @errorName(err),
5262 }),
5263 }
5264}
5265
5266fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
5267 if (freebsd.buildSharedObjects(comp, prog_node)) |_| {
5268 // The job should no longer be queued up since it succeeded.
5269 comp.queued_jobs.freebsd_shared_objects = false;
5270 } else |err| switch (err) {
5271 error.AlreadyReported => return,
5272 else => comp.lockAndSetMiscFailure(.freebsd_shared_objects, "unable to build FreeBSD libc shared objects: {s}", .{
5273 @errorName(err),
5274 }),
5275 }
5276}
5277
5278fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: std.Progress.Node) void {
5279 if (netbsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
5280 comp.queued_jobs.netbsd_crt_file[@backingInt(crt_file)] = false;
5281 } else |err| switch (err) {
5282 error.AlreadyReported => return,
5283 else => comp.lockAndSetMiscFailure(.netbsd_crt_file, "unable to build NetBSD {s}: {s}", .{
5284 @tagName(crt_file), @errorName(err),
5285 }),
5286 }
5287}
5288
5289fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
5290 if (netbsd.buildSharedObjects(comp, prog_node)) |_| {
5291 // The job should no longer be queued up since it succeeded.
5292 comp.queued_jobs.netbsd_shared_objects = false;
5293 } else |err| switch (err) {
5294 error.AlreadyReported => return,
5295 else => comp.lockAndSetMiscFailure(.netbsd_shared_objects, "unable to build NetBSD libc shared objects: {s}", .{
5296 @errorName(err),
5297 }),
5298 }
5299}
5300
5301fn buildOpenBSDCrtFile(comp: *Compilation, crt_file: openbsd.CrtFile, prog_node: std.Progress.Node) void {
5302 if (openbsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
5303 comp.queued_jobs.openbsd_crt_file[@backingInt(crt_file)] = false;
5304 } else |err| switch (err) {
5305 error.AlreadyReported => return,
5306 else => comp.lockAndSetMiscFailure(.openbsd_crt_file, "unable to build OpenBSD {s}: {s}", .{
5307 @tagName(crt_file), @errorName(err),
5308 }),
5309 }
5310}
5311
5312fn buildOpenBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
5313 if (openbsd.buildSharedObjects(comp, prog_node)) |_| {
5314 // The job should no longer be queued up since it succeeded.
5315 comp.queued_jobs.openbsd_shared_objects = false;
5316 } else |err| switch (err) {
5317 error.AlreadyReported => return,
5318 else => comp.lockAndSetMiscFailure(.openbsd_shared_objects, "unable to build OpenBSD libc shared objects: {s}", .{
5319 @errorName(err),
5320 }),
5321 }
5322}
5323
5324fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std.Progress.Node) void {
5325 if (mingw.buildCrtFile(comp, crt_file, prog_node)) |_| {
5326 comp.queued_jobs.mingw_crt_file[@backingInt(crt_file)] = false;
5327 } else |err| switch (err) {
5328 error.AlreadyReported => return,
5329 else => comp.lockAndSetMiscFailure(.mingw_crt_file, "unable to build mingw-w64 {s}: {s}", .{
5330 @tagName(crt_file), @errorName(err),
5331 }),
5332 }
5333}
5334
5335fn buildMingwImportLib(comp: *Compilation, lib_name: []const u8, is_prelink: bool, prog_node: std.Progress.Node) void {
5336 const crt_file_path = mingw.buildImportLib(comp, lib_name, prog_node) catch |err| switch (err) {
5337 error.AlreadyReported => return,
5338 // TODO: This isn't actually true for self-hosted
5339 // In the non-prelink case we will end up putting foo.lib onto the linker line and letting the linker
5340 // use its library paths to look for libraries and report any problems.
5341 error.DefNotFound => return if (is_prelink) {
5342 comp.lockAndSetMiscFailure(
5343 .windows_import_lib,
5344 "definition not found for required mingw DLL import .lib {s}",
5345 .{lib_name},
5346 );
5347 },
5348 // TODO Surface more error details.
5349 else => |e| return comp.lockAndSetMiscFailure(
5350 .windows_import_lib,
5351 "generating mingw DLL import .lib file for {s} failed: {t}",
5352 .{ lib_name, e },
5353 ),
5354 };
5355
5356 if (is_prelink)
5357 comp.queuePrelinkTasks(&.{.{
5358 .load_archive = .{
5359 .path = crt_file_path,
5360 .must_link = false,
5361 },
5362 }}) catch |err| comp.lockAndSetMiscFailure(
5363 .windows_import_lib,
5364 "unable to queue prelink task for mingw import lib {f}: {t}",
5365 .{ crt_file_path, err },
5366 );
5367}
5368
5369fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_node: std.Progress.Node) void {
5370 if (wasi_libc.buildCrtFile(comp, crt_file, prog_node)) |_| {
5371 comp.queued_jobs.wasi_libc_crt_file[@backingInt(crt_file)] = false;
5372 } else |err| switch (err) {
5373 error.AlreadyReported => return,
5374 else => comp.lockAndSetMiscFailure(.wasi_libc_crt_file, "unable to build WASI libc {s}: {s}", .{
5375 @tagName(crt_file), @errorName(err),
5376 }),
5377 }
5378}
5379
5380fn buildLibUnwind(comp: *Compilation, prog_node: std.Progress.Node) void {
5381 if (libunwind.buildStaticLib(comp, prog_node)) |_| {
5382 comp.queued_jobs.libunwind = false;
5383 } else |err| switch (err) {
5384 error.AlreadyReported => return,
5385 else => comp.lockAndSetMiscFailure(.libunwind, "unable to build libunwind: {s}", .{@errorName(err)}),
5386 }
5387}
5388
5389fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) void {
5390 if (libcxx.buildLibCxx(comp, prog_node)) |_| {
5391 comp.queued_jobs.libcxx = false;
5392 } else |err| switch (err) {
5393 error.AlreadyReported => return,
5394 else => comp.lockAndSetMiscFailure(.libcxx, "unable to build libcxx: {s}", .{@errorName(err)}),
5395 }
5396}
5397
5398fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) void {
5399 if (libcxx.buildLibCxxAbi(comp, prog_node)) |_| {
5400 comp.queued_jobs.libcxxabi = false;
5401 } else |err| switch (err) {
5402 error.AlreadyReported => return,
5403 else => comp.lockAndSetMiscFailure(.libcxxabi, "unable to build libcxxabi: {s}", .{@errorName(err)}),
5404 }
5405}
5406
5407fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void {
5408 if (libtsan.buildTsan(comp, prog_node)) |_| {
5409 comp.queued_jobs.libtsan = false;
5410 } else |err| switch (err) {
5411 error.AlreadyReported => return,
5412 else => comp.lockAndSetMiscFailure(.libtsan, "unable to build TSAN library: {s}", .{@errorName(err)}),
5413 }
5414}
5415
5416fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void {
5417 comp.buildOutputFromZig(
5418 "c.zig",
5419 "zigc",
5420 .Lib,
5421 .static,
5422 .libzigc,
5423 prog_node,
5424 .{},
5425 &comp.zigc_static_lib,
5426 ) catch |err| switch (err) {
5427 error.AlreadyReported => return,
5428 else => comp.lockAndSetMiscFailure(.libzigc, "unable to build libzigc: {s}", .{@errorName(err)}),
5429 };
5430}
5431
5432fn reportRetryableCObjectError(comp: *Compilation, c_object: *CObject, err: anyerror) error{OutOfMemory}!void {
5433 c_object.status = .failure_retryable;
5434
5435 switch (comp.failCObj(c_object, "{t}", .{err})) {
5436 error.AlreadyReported => return,
5437 else => |e| return e,
5438 }
5439}
5440
5441fn reportRetryableWin32ResourceError(
5442 comp: *Compilation,
5443 win32_resource: *Win32Resource,
5444 err: anyerror,
5445) error{OutOfMemory}!void {
5446 const io = comp.io;
5447
5448 win32_resource.status = .failure_retryable;
5449
5450 var bundle: ErrorBundle.Wip = undefined;
5451 try bundle.init(comp.gpa);
5452 errdefer bundle.deinit();
5453 try bundle.addRootErrorMessage(.{
5454 .msg = try bundle.printString("{s}", .{@errorName(err)}),
5455 .src_loc = try bundle.addSourceLocation(.{
5456 .src_path = try bundle.addString(switch (win32_resource.src) {
5457 .rc => |rc_src| rc_src.src_path,
5458 .manifest => |manifest_src| manifest_src,
5459 }),
5460 .line = 0,
5461 .column = 0,
5462 .span_start = 0,
5463 .span_main = 0,
5464 .span_end = 0,
5465 }),
5466 });
5467 const finished_bundle = try bundle.toOwnedBundle("");
5468 {
5469 comp.mutex.lockUncancelable(io);
5470 defer comp.mutex.unlock(io);
5471 try comp.failed_win32_resources.putNoClobber(comp.gpa, win32_resource, finished_bundle);
5472 }
5473}
5474
5475fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Progress.Node) !void {
5476 if (comp.config.c_frontend == .aro) {
5477 return comp.failCObj(c_object, "aro does not support compiling C objects yet", .{});
5478 }
5479 if (!build_options.have_llvm) {
5480 return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{});
5481 }
5482 const self_exe_path = comp.self_exe_path orelse
5483 return comp.failCObj(c_object, "clang compilation disabled", .{});
5484
5485 const tracy_trace = trace(@src());
5486 defer tracy_trace.end();
5487
5488 log.debug("updating C object: {s}", .{c_object.src.src_path});
5489
5490 const gpa = comp.gpa;
5491 const io = comp.io;
5492
5493 if (c_object.clearStatus(gpa, io)) {
5494 // There was previous failure.
5495 comp.mutex.lockUncancelable(io);
5496 defer comp.mutex.unlock(io);
5497 // If the failure was OOM, there will not be an entry here, so we do
5498 // not assert discard.
5499 _ = comp.failed_c_objects.swapRemove(c_object);
5500 }
5501
5502 var man = comp.obtainCObjectCacheManifest(c_object.src.owner);
5503 defer man.deinit();
5504
5505 man.hash.add(comp.clang_preprocessor_mode);
5506 man.hash.addOptionalBytes(comp.emit_asm);
5507 man.hash.addOptionalBytes(comp.emit_llvm_ir);
5508 man.hash.addOptionalBytes(comp.emit_llvm_bc);
5509
5510 try cache_helpers.hashCSource(&man, c_object.src);
5511
5512 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
5513 defer arena_allocator.deinit();
5514 const arena = arena_allocator.allocator();
5515
5516 const c_source_basename = fs.path.basename(c_object.src.src_path);
5517
5518 const child_progress_node = c_obj_prog_node.start(c_source_basename, 0);
5519 defer child_progress_node.end();
5520
5521 // Special case when doing build-obj for just one C file. When there are more than one object
5522 // file and building an object we need to link them together, but with just one it should go
5523 // directly to the output file.
5524 const direct_o = comp.c_source_files.len == 1 and comp.zcu == null and
5525 comp.config.output_mode == .Obj and !link.anyObjectInputs(comp.link_inputs);
5526 const o_basename_noext = if (direct_o)
5527 comp.root_name
5528 else
5529 c_source_basename[0 .. c_source_basename.len - fs.path.extension(c_source_basename).len];
5530
5531 const target = comp.getTarget();
5532 assert(target.ofmt != .c);
5533 const o_ext = target.ofmt.fileExt(target.cpu.arch);
5534 const digest = if (!comp.disable_c_depfile and try man.hit(child_progress_node)) man.final() else blk: {
5535 var argv: std.array_list.Managed([]const u8) = .init(gpa);
5536 defer argv.deinit();
5537
5538 // In case we are doing passthrough mode, we need to detect -S and -emit-llvm.
5539 const out_ext = e: {
5540 if (!comp.clang_passthrough_mode)
5541 break :e o_ext;
5542 if (comp.emit_asm != null)
5543 break :e ".s";
5544 if (comp.emit_llvm_ir != null)
5545 break :e ".ll";
5546 if (comp.emit_llvm_bc != null)
5547 break :e ".bc";
5548
5549 break :e o_ext;
5550 };
5551 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext });
5552 const ext = c_object.src.ext orelse classifyFileExt(c_object.src.src_path);
5553
5554 try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" });
5555 // if "ext" is explicit, add "-x <lang>". Otherwise let clang do its thing.
5556 if (c_object.src.ext != null or ext.clangNeedsLanguageOverride()) {
5557 try argv.appendSlice(&[_][]const u8{ "-x", switch (ext) {
5558 .assembly => "assembler",
5559 .assembly_with_cpp => "assembler-with-cpp",
5560 .c => "c",
5561 .h => "c-header",
5562 .cpp => "c++",
5563 .hpp => "c++-header",
5564 .m => "objective-c",
5565 .hm => "objective-c-header",
5566 .mm => "objective-c++",
5567 .hmm => "objective-c++-header",
5568 else => fatal("language '{s}' is unsupported in this context", .{@tagName(ext)}),
5569 } });
5570 }
5571 try argv.append(c_object.src.src_path);
5572
5573 // When all these flags are true, it means that the entire purpose of
5574 // this compilation is to perform a single zig cc operation. This means
5575 // that we could "tail call" clang by doing an execve, and any use of
5576 // the caching system would actually be problematic since the user is
5577 // presumably doing their own caching by using dep file flags.
5578 if (std.process.can_replace and direct_o and
5579 comp.disable_c_depfile and comp.clang_passthrough_mode)
5580 {
5581 try comp.addCCArgs(arena, &argv, ext, null, c_object.src.owner);
5582 try argv.appendSlice(c_object.src.extra_flags);
5583 try argv.appendSlice(c_object.src.cache_exempt_flags);
5584
5585 const out_obj_path = if (comp.bin_file) |lf|
5586 try lf.emit.root_dir.join(arena, &.{lf.emit.sub_path})
5587 else
5588 "/dev/null";
5589
5590 try argv.ensureUnusedCapacity(6);
5591 switch (comp.clang_preprocessor_mode) {
5592 .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
5593 .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
5594 .pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
5595 .stdout => argv.appendAssumeCapacity("-E"),
5596 .version => argv.appendAssumeCapacity("--version"),
5597 }
5598
5599 if (comp.emit_asm != null) {
5600 argv.appendAssumeCapacity("-S");
5601 } else if (comp.emit_llvm_ir != null) {
5602 argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" });
5603 } else if (comp.emit_llvm_bc != null) {
5604 argv.appendAssumeCapacity("-emit-llvm");
5605 }
5606
5607 if (comp.verbose_cc) {
5608 try dumpArgv(io, argv.items);
5609 }
5610
5611 const err = std.process.replace(io, .{ .argv = argv.items });
5612 fatal("unable to replace process with clang: {t}", .{err});
5613 }
5614
5615 // We can't know the digest until we do the C compiler invocation,
5616 // so we need a temporary filename.
5617 const out_obj_path = try comp.tmpFilePath(arena, o_basename);
5618 var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.createDirPathOpen(io, "tmp", .{});
5619 defer zig_cache_tmp_dir.close(io);
5620
5621 const out_diag_path = if (comp.clang_passthrough_mode or !ext.clangSupportsDiagnostics())
5622 null
5623 else
5624 try std.fmt.allocPrint(arena, "{s}.diag", .{out_obj_path});
5625 const out_dep_path = if (comp.disable_c_depfile or !ext.clangSupportsDepFile())
5626 null
5627 else
5628 try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});
5629
5630 try comp.addCCArgs(arena, &argv, ext, out_dep_path, c_object.src.owner);
5631 try argv.appendSlice(c_object.src.extra_flags);
5632 try argv.appendSlice(c_object.src.cache_exempt_flags);
5633
5634 try argv.ensureUnusedCapacity(6);
5635 switch (comp.clang_preprocessor_mode) {
5636 .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
5637 .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
5638 .pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
5639 .stdout => argv.appendAssumeCapacity("-E"),
5640 .version => argv.appendAssumeCapacity("--version"),
5641 }
5642 if (out_diag_path) |diag_file_path| {
5643 argv.appendSliceAssumeCapacity(&.{ "--serialize-diagnostics", diag_file_path });
5644 } else if (comp.clang_passthrough_mode) {
5645 if (comp.emit_asm != null) {
5646 argv.appendAssumeCapacity("-S");
5647 } else if (comp.emit_llvm_ir != null) {
5648 argv.appendSliceAssumeCapacity(&.{ "-emit-llvm", "-S" });
5649 } else if (comp.emit_llvm_bc != null) {
5650 argv.appendAssumeCapacity("-emit-llvm");
5651 }
5652 }
5653
5654 if (comp.verbose_cc) {
5655 try dumpArgv(io, argv.items);
5656 }
5657
5658 // Just to save disk space, we delete the files that are never needed again.
5659 defer if (out_diag_path) |diag_file_path| zig_cache_tmp_dir.deleteFile(io, fs.path.basename(diag_file_path)) catch |err| switch (err) {
5660 error.FileNotFound => {}, // the file wasn't created due to an error we reported
5661 else => log.warn("failed to delete '{s}': {s}", .{ diag_file_path, @errorName(err) }),
5662 };
5663 defer if (out_dep_path) |dep_file_path| zig_cache_tmp_dir.deleteFile(io, fs.path.basename(dep_file_path)) catch |err| switch (err) {
5664 error.FileNotFound => {}, // the file wasn't created due to an error we reported
5665 else => log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }),
5666 };
5667 if (std.process.can_spawn) {
5668 if (comp.clang_passthrough_mode) {
5669 var child = std.process.spawn(io, .{
5670 .argv = argv.items,
5671 .stdin = .inherit,
5672 .stdout = .inherit,
5673 .stderr = .inherit,
5674 }) catch |err| {
5675 return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {t}", .{
5676 argv.items[0], err,
5677 });
5678 };
5679 const term = child.wait(io) catch |err| {
5680 return comp.failCObj(c_object, "failed to wait zig clang (passthrough mode) {s}: {t}", .{
5681 argv.items[0], err,
5682 });
5683 };
5684 switch (term) {
5685 .exited => |code| {
5686 if (code != 0) {
5687 std.process.exit(code);
5688 }
5689 switch (comp.clang_preprocessor_mode) {
5690 .stdout, .version => std.process.exit(0),
5691 else => {},
5692 }
5693 },
5694 else => std.process.abort(),
5695 }
5696 } else {
5697 var child = try std.process.spawn(io, .{
5698 .argv = argv.items,
5699 .stdin = .ignore,
5700 .stdout = .ignore,
5701 .stderr = .pipe,
5702 });
5703
5704 var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
5705 const stderr = try stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32)));
5706
5707 const term = child.wait(io) catch |err|
5708 return comp.failCObj(c_object, "failed to spawn zig clang {s}: {t}", .{ argv.items[0], err });
5709
5710 switch (term) {
5711 .exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| {
5712 const bundle = CObject.Diag.Bundle.parse(gpa, io, diag_file_path) catch |err| {
5713 log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr });
5714 return comp.failCObj(c_object, "clang exited with code {d}", .{code});
5715 };
5716 return comp.failCObjWithOwnedDiagBundle(c_object, bundle);
5717 } else {
5718 log.err("clang failed with stderr: {s}", .{stderr});
5719 return comp.failCObj(c_object, "clang exited with code {d}", .{code});
5720 },
5721 .signal => |sig| {
5722 log.err("clang failed with stderr: {s}", .{stderr});
5723 return comp.failCObj(c_object, "clang terminated with signal {t}", .{sig});
5724 },
5725 .stopped => |sig| {
5726 log.err("clang failed with stderr: {s}", .{stderr});
5727 return comp.failCObj(c_object, "clang stopped with signal {t}", .{sig});
5728 },
5729 .unknown => {
5730 log.err("clang terminated with stderr: {s}", .{stderr});
5731 return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
5732 },
5733 }
5734 }
5735 } else {
5736 const exit_code = try clangMain(arena, argv.items);
5737 if (exit_code != 0) {
5738 if (comp.clang_passthrough_mode) {
5739 std.process.exit(exit_code);
5740 } else {
5741 return comp.failCObj(c_object, "clang exited with code {d}", .{exit_code});
5742 }
5743 }
5744 if (comp.clang_passthrough_mode) switch (comp.clang_preprocessor_mode) {
5745 .stdout, .version => std.process.exit(0),
5746 else => {},
5747 };
5748 }
5749
5750 if (out_dep_path) |dep_file_path| {
5751 const dep_basename = fs.path.basename(dep_file_path);
5752
5753 if (comp.file_system_inputs != null) {
5754 // Use the same file size limit as the cache code does for dependency files.
5755 const dep_file_contents = try zig_cache_tmp_dir.readFileAlloc(io, dep_basename, gpa, .limited(Cache.manifest_file_size_max));
5756 defer gpa.free(dep_file_contents);
5757
5758 var str_buf: std.ArrayList(u8) = .empty;
5759 defer str_buf.deinit(gpa);
5760
5761 var it: std.Build.Cache.DepTokenizer = .{ .bytes = dep_file_contents };
5762 while (it.next()) |token| {
5763 const input_path: Compilation.Path = switch (token) {
5764 .target, .target_must_resolve => continue,
5765 .prereq => |file_path| try .fromUnresolved(arena, comp.dirs, &.{file_path}),
5766 .prereq_must_resolve => p: {
5767 try token.resolve(gpa, &str_buf);
5768 break :p try .fromUnresolved(arena, comp.dirs, &.{str_buf.items});
5769 },
5770 else => |err| {
5771 try err.printError(gpa, &str_buf);
5772 log.err("failed parsing {s}: {s}", .{ dep_basename, str_buf.items });
5773 return error.InvalidDepFile;
5774 },
5775 };
5776 try comp.appendFileSystemInput(input_path);
5777 }
5778 }
5779
5780 // Add the files depended on to the cache system.
5781 try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
5782 switch (comp.cache_use) {
5783 .whole => |whole| {
5784 if (whole.cache_manifest) |whole_cache_manifest| {
5785 try whole.cache_manifest_mutex.lock(io);
5786 defer whole.cache_manifest_mutex.unlock(io);
5787 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
5788 }
5789 },
5790 .incremental, .none => {},
5791 }
5792 }
5793
5794 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
5795 if (comp.disable_c_depfile) _ = try man.hit(child_progress_node);
5796
5797 // Rename into place.
5798 const digest = man.final();
5799 const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });
5800 var o_dir = try comp.dirs.local_cache.handle.createDirPathOpen(io, o_sub_path, .{});
5801 defer o_dir.close(io);
5802 const tmp_basename = fs.path.basename(out_obj_path);
5803 try Io.Dir.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename, io);
5804 break :blk digest;
5805 };
5806
5807 if (man.have_exclusive_lock) {
5808 // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
5809 // possible we had a hit and the manifest is dirty, for example if the file mtime changed but
5810 // the contents were the same, we hit the cache but the manifest is dirty and we need to update
5811 // it to prevent doing a full file content comparison the next time around.
5812 man.writeManifest() catch |err| {
5813 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{
5814 c_object.src.src_path, @errorName(err),
5815 });
5816 };
5817 }
5818
5819 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext });
5820
5821 c_object.status = .{
5822 .success = .{
5823 .object_path = .{
5824 .root_dir = comp.dirs.local_cache,
5825 .sub_path = try fs.path.join(gpa, &.{ "o", &digest, o_basename }),
5826 },
5827 .lock = man.toOwnedLock(),
5828 },
5829 };
5830
5831 try comp.queuePrelinkTasks(&.{.{ .load_object = c_object.status.success.object_path }});
5832}
5833
5834fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void {
5835 if (!std.process.can_spawn) {
5836 return comp.failWin32Resource(win32_resource, "{s} does not support spawning a child process", .{@tagName(builtin.os.tag)});
5837 }
5838
5839 const self_exe_path = comp.self_exe_path orelse
5840 return comp.failWin32Resource(win32_resource, "unable to find self exe path", .{});
5841
5842 const tracy_trace = trace(@src());
5843 defer tracy_trace.end();
5844
5845 const src_path = switch (win32_resource.src) {
5846 .rc => |rc_src| rc_src.src_path,
5847 .manifest => |src_path| src_path,
5848 };
5849 const src_basename = fs.path.basename(src_path);
5850
5851 log.debug("updating win32 resource: {s}", .{src_path});
5852
5853 const io = comp.io;
5854
5855 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
5856 defer arena_allocator.deinit();
5857 const arena = arena_allocator.allocator();
5858
5859 if (win32_resource.clearStatus(comp.gpa, io)) {
5860 // There was previous failure.
5861 comp.mutex.lockUncancelable(io);
5862 defer comp.mutex.unlock(io);
5863 // If the failure was OOM, there will not be an entry here, so we do
5864 // not assert discard.
5865 _ = comp.failed_win32_resources.swapRemove(win32_resource);
5866 }
5867
5868 const child_progress_node = win32_resource_prog_node.start(src_basename, 0);
5869 defer child_progress_node.end();
5870
5871 var man = comp.obtainWin32ResourceCacheManifest();
5872 defer man.deinit();
5873
5874 // For .manifest files, we ultimately just want to generate a .res with
5875 // the XML data as a RT_MANIFEST resource. This means we can skip preprocessing,
5876 // include paths, CLI options, etc.
5877 if (win32_resource.src == .manifest) {
5878 _ = try man.addFilePath(.initCwd(src_path), null);
5879
5880 const rc_basename = try std.fmt.allocPrint(arena, "{s}.rc", .{src_basename});
5881 const res_basename = try std.fmt.allocPrint(arena, "{s}.res", .{src_basename});
5882
5883 const digest = if (try man.hit(child_progress_node)) man.final() else blk: {
5884 // The digest only depends on the .manifest file, so we can
5885 // get the digest now and write the .res directly to the cache
5886 const digest = man.final();
5887
5888 const o_sub_path = try fs.path.join(arena, &.{ "o", &digest });
5889 var o_dir = try comp.dirs.local_cache.handle.createDirPathOpen(io, o_sub_path, .{});
5890 defer o_dir.close(io);
5891
5892 const in_rc_path = try comp.dirs.local_cache.join(comp.gpa, &.{
5893 o_sub_path, rc_basename,
5894 });
5895 const out_res_path = try comp.dirs.local_cache.join(comp.gpa, &.{
5896 o_sub_path, res_basename,
5897 });
5898
5899 // In .rc files, a " within a quoted string is escaped as ""
5900 const fmtRcEscape = struct {
5901 fn formatRcEscape(bytes: []const u8, writer: *Writer) Writer.Error!void {
5902 for (bytes) |byte| switch (byte) {
5903 '"' => try writer.writeAll("\"\""),
5904 '\\' => try writer.writeAll("\\\\"),
5905 else => try writer.writeByte(byte),
5906 };
5907 }
5908
5909 pub fn fmtRcEscape(bytes: []const u8) std.fmt.Alt([]const u8, formatRcEscape) {
5910 return .{ .data = bytes };
5911 }
5912 }.fmtRcEscape;
5913
5914 // https://learn.microsoft.com/en-us/windows/win32/sbscs/using-side-by-side-assemblies-as-a-resource
5915 // WinUser.h defines:
5916 // CREATEPROCESS_MANIFEST_RESOURCE_ID to 1, which is the default
5917 // ISOLATIONAWARE_MANIFEST_RESOURCE_ID to 2, which must be used for .dlls
5918 const resource_id: u32 = if (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic) 2 else 1;
5919
5920 // 24 is RT_MANIFEST
5921 const resource_type = 24;
5922
5923 const input = try std.fmt.allocPrint(arena, "{d} {d} \"{f}\"", .{
5924 resource_id, resource_type, fmtRcEscape(src_path),
5925 });
5926
5927 try o_dir.writeFile(io, .{ .sub_path = rc_basename, .data = input });
5928
5929 var argv = std.array_list.Managed([]const u8).init(comp.gpa);
5930 defer argv.deinit();
5931
5932 try argv.appendSlice(&.{
5933 self_exe_path,
5934 "rc",
5935 "--zig-integration",
5936 "/:target",
5937 @tagName(comp.getTarget().cpu.arch),
5938 "/:no-preprocess",
5939 "/x", // ignore INCLUDE environment variable
5940 "/c65001", // UTF-8 codepage
5941 "/:auto-includes",
5942 "none",
5943 });
5944 try argv.appendSlice(&.{ "--", in_rc_path, out_res_path });
5945
5946 try spawnZigRc(comp, win32_resource, arena, argv.items, child_progress_node);
5947
5948 break :blk digest;
5949 };
5950
5951 if (man.have_exclusive_lock) {
5952 man.writeManifest() catch |err| {
5953 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ src_path, @errorName(err) });
5954 };
5955 }
5956
5957 win32_resource.status = .{
5958 .success = .{
5959 .res_path = try comp.dirs.local_cache.join(comp.gpa, &[_][]const u8{
5960 "o", &digest, res_basename,
5961 }),
5962 .lock = man.toOwnedLock(),
5963 },
5964 };
5965 return;
5966 }
5967
5968 // We now know that we're compiling an .rc file
5969 const rc_src = win32_resource.src.rc;
5970
5971 _ = try man.addFilePath(.initCwd(rc_src.src_path), null);
5972 man.hash.addListOfBytes(rc_src.extra_flags);
5973
5974 const rc_basename_noext = src_basename[0 .. src_basename.len - fs.path.extension(src_basename).len];
5975
5976 const digest = if (try man.hit(child_progress_node)) man.final() else blk: {
5977 var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.createDirPathOpen(io, "tmp", .{});
5978 defer zig_cache_tmp_dir.close(io);
5979
5980 const res_filename = try std.fmt.allocPrint(arena, "{s}.res", .{rc_basename_noext});
5981
5982 // We can't know the digest until we do the compilation,
5983 // so we need a temporary filename.
5984 const out_res_path = try comp.tmpFilePath(arena, res_filename);
5985
5986 var argv = std.array_list.Managed([]const u8).init(comp.gpa);
5987 defer argv.deinit();
5988
5989 const depfile_filename = try std.fmt.allocPrint(arena, "{s}.d.json", .{rc_basename_noext});
5990 const out_dep_path = try comp.tmpFilePath(arena, depfile_filename);
5991 try argv.appendSlice(&.{
5992 self_exe_path,
5993 "rc",
5994 "--zig-integration",
5995 "/:target",
5996 @tagName(comp.getTarget().cpu.arch),
5997 "/:depfile",
5998 out_dep_path,
5999 "/:depfile-fmt",
6000 "json",
6001 "/x", // ignore INCLUDE environment variable
6002 "/:auto-includes",
6003 @tagName(comp.rc_includes),
6004 });
6005 // While these defines are not normally present when calling rc.exe directly,
6006 // them being defined matches the behavior of how MSVC calls rc.exe which is the more
6007 // relevant behavior in this case.
6008 switch (rc_src.owner.optimize_mode) {
6009 .debug, .safe => {},
6010 .fast, .small => try argv.append("-DNDEBUG"),
6011 }
6012 try argv.appendSlice(rc_src.extra_flags);
6013 try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path });
6014
6015 try spawnZigRc(comp, win32_resource, arena, argv.items, child_progress_node);
6016
6017 // Read depfile and update cache manifest
6018 {
6019 const dep_basename = fs.path.basename(out_dep_path);
6020 const dep_file_contents = try zig_cache_tmp_dir.readFileAlloc(io, dep_basename, arena, .limited(50 * 1024 * 1024));
6021 defer arena.free(dep_file_contents);
6022
6023 const value = try std.json.parseFromSliceLeaky(std.json.Value, arena, dep_file_contents, .{});
6024 if (value != .array) {
6025 return comp.failWin32Resource(win32_resource, "depfile from zig rc has unexpected format", .{});
6026 }
6027
6028 for (value.array.items) |element| {
6029 if (element != .string) {
6030 return comp.failWin32Resource(win32_resource, "depfile from zig rc has unexpected format", .{});
6031 }
6032 const dep_file_path = element.string;
6033 try man.addFilePost(dep_file_path);
6034 switch (comp.cache_use) {
6035 .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| {
6036 try whole.cache_manifest_mutex.lock(io);
6037 defer whole.cache_manifest_mutex.unlock(io);
6038 try whole_cache_manifest.addFilePost(dep_file_path);
6039 },
6040 .incremental, .none => {},
6041 }
6042 }
6043 }
6044
6045 // Rename into place.
6046 const digest = man.final();
6047 const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });
6048 var o_dir = try comp.dirs.local_cache.handle.createDirPathOpen(io, o_sub_path, .{});
6049 defer o_dir.close(io);
6050 const tmp_basename = fs.path.basename(out_res_path);
6051 try Io.Dir.rename(zig_cache_tmp_dir, tmp_basename, o_dir, res_filename, io);
6052 break :blk digest;
6053 };
6054
6055 if (man.have_exclusive_lock) {
6056 // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
6057 // possible we had a hit and the manifest is dirty, for example if the file mtime changed but
6058 // the contents were the same, we hit the cache but the manifest is dirty and we need to update
6059 // it to prevent doing a full file content comparison the next time around.
6060 man.writeManifest() catch |err| {
6061 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ rc_src.src_path, @errorName(err) });
6062 };
6063 }
6064
6065 const res_basename = try std.fmt.allocPrint(arena, "{s}.res", .{rc_basename_noext});
6066
6067 win32_resource.status = .{
6068 .success = .{
6069 .res_path = try comp.dirs.local_cache.join(comp.gpa, &[_][]const u8{
6070 "o", &digest, res_basename,
6071 }),
6072 .lock = man.toOwnedLock(),
6073 },
6074 };
6075}
6076
6077fn spawnZigRc(
6078 comp: *Compilation,
6079 win32_resource: *Win32Resource,
6080 arena: Allocator,
6081 argv: []const []const u8,
6082 child_progress_node: std.Progress.Node,
6083) !void {
6084 const io = comp.io;
6085 const gpa = comp.gpa;
6086 var node_name: std.ArrayList(u8) = .empty;
6087 defer node_name.deinit(arena);
6088
6089 var child = std.process.spawn(io, .{
6090 .argv = argv,
6091 .stdin = .ignore,
6092 .stdout = .pipe,
6093 .stderr = .pipe,
6094 .progress_node = child_progress_node,
6095 }) catch |err| return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {t}", .{
6096 argv[0], err,
6097 });
6098 defer child.kill(io);
6099
6100 var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined;
6101 var multi_reader: Io.File.MultiReader = undefined;
6102 multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? });
6103 defer multi_reader.deinit();
6104
6105 const stdout = multi_reader.reader(0);
6106
6107 var eos_err: error{EndOfStream}!void = {};
6108
6109 var client: std.zig.Client = .{
6110 .in = stdout,
6111 .out = undefined,
6112 };
6113
6114 while (true) {
6115 const header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) {
6116 error.Timeout => unreachable,
6117 error.EndOfStream => |e| {
6118 if (client.in.bufferedLen() == 0) break;
6119 // Better to report the crash with stderr below, but we set
6120 // this in case the child exits successfully while violating
6121 // this protocol.
6122 eos_err = e;
6123 break;
6124 },
6125 else => |e| return e,
6126 };
6127 const body = client.in.take(header.bytes_len) catch unreachable;
6128
6129 switch (header.tag) {
6130 // We expect exactly one ErrorBundle, and if any error_bundle header is
6131 // sent then it's a fatal error.
6132 .error_bundle => {
6133 const error_bundle = try std.zig.Server.allocErrorBundle(gpa, body);
6134 return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);
6135 },
6136 else => {}, // ignore other messages
6137 }
6138 }
6139
6140 try multi_reader.fillRemaining(.none);
6141
6142 // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)
6143 const term = child.wait(io) catch |err| {
6144 return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {t}", .{ argv[0], err });
6145 };
6146
6147 const stderr = multi_reader.reader(1).buffered();
6148
6149 switch (term) {
6150 .exited => |code| {
6151 if (code != 0) {
6152 log.err("zig rc failed with stderr:\n{s}", .{stderr});
6153 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
6154 }
6155 },
6156 .signal => |sig| {
6157 log.err("zig rc signaled {t} with stderr:\n{s}", .{ sig, stderr });
6158 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
6159 },
6160 .stopped => |sig| {
6161 log.err("zig rc stopped {t} with stderr:\n{s}", .{ sig, stderr });
6162 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
6163 },
6164 .unknown => {
6165 log.err("zig rc terminated with stderr:\n{s}", .{stderr});
6166 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
6167 },
6168 }
6169
6170 try eos_err;
6171}
6172
6173pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
6174 const io = comp.io;
6175 const rand_int = r: {
6176 var x: u64 = undefined;
6177 io.random(@ptrCast(&x));
6178 break :r x;
6179 };
6180 const s = fs.path.sep_str;
6181 if (comp.dirs.local_cache.path) |p| {
6182 return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
6183 } else {
6184 return std.fmt.allocPrint(ally, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
6185 }
6186}
6187
6188/// Add common C compiler args between translate-c and C object compilation.
6189fn addCommonCCArgs(
6190 comp: *const Compilation,
6191 arena: Allocator,
6192 argv: *std.array_list.Managed([]const u8),
6193 ext: FileExt,
6194 out_dep_path: ?[]const u8,
6195 mod: *Module,
6196 c_frontend: Config.CFrontend,
6197) !void {
6198 const target = &mod.resolved_target.result;
6199 const is_clang = c_frontend == .clang;
6200
6201 if (target_util.supports_fpic(target)) {
6202 // PIE needs to go before PIC because Clang interprets `-fno-PIE` to imply `-fno-PIC`, which
6203 // we don't necessarily want.
6204 try argv.append(if (comp.config.pie) "-fPIE" else "-fno-PIE");
6205 try argv.append(if (mod.pic) "-fPIC" else "-fno-PIC");
6206 }
6207
6208 switch (target.os.tag) {
6209 .ios, .maccatalyst, .macos, .tvos, .watchos => |os| if (is_clang) {
6210 try argv.ensureUnusedCapacity(2);
6211 // Pass the proper -m<os>-version-min argument for darwin.
6212 const ver = target.os.version_range.semver.min;
6213 argv.appendAssumeCapacity(try std.fmt.allocPrint(arena, "-m{s}{s}-version-min={d}.{d}.{d}", .{
6214 switch (os) {
6215 .maccatalyst => "ios",
6216 else => @tagName(os),
6217 },
6218 switch (target.abi) {
6219 .simulator => "-simulator",
6220 else => "",
6221 },
6222 ver.major,
6223 ver.minor,
6224 ver.patch,
6225 }));
6226 // This avoids a warning that sometimes occurs when
6227 // providing both a -target argument that contains a
6228 // version as well as the -mmacosx-version-min argument.
6229 // Zig provides the correct value in both places, so it
6230 // doesn't matter which one gets overridden.
6231 argv.appendAssumeCapacity("-Wno-overriding-option");
6232 },
6233 else => {},
6234 }
6235
6236 if (comp.mingw_unicode_entry_point) {
6237 try argv.append("-municode");
6238 }
6239
6240 try argv.ensureUnusedCapacity(2);
6241 switch (comp.config.debug_format) {
6242 .strip => {},
6243 .code_view => {
6244 // -g is required here because -gcodeview doesn't trigger debug info
6245 // generation, it only changes the type of information generated.
6246 argv.appendSliceAssumeCapacity(&.{ "-g", "-gcodeview" });
6247 },
6248 .dwarf => |f| {
6249 argv.appendAssumeCapacity("-gdwarf-4");
6250 switch (f) {
6251 .@"32" => argv.appendAssumeCapacity("-gdwarf32"),
6252 .@"64" => argv.appendAssumeCapacity("-gdwarf64"),
6253 }
6254 },
6255 }
6256
6257 switch (comp.config.lto) {
6258 .none => try argv.append("-fno-lto"),
6259 .full => try argv.append("-flto=full"),
6260 .thin => try argv.append("-flto=thin"),
6261 }
6262
6263 // This only works for preprocessed files. Guarded by `FileExt.clangSupportsDepFile`.
6264 if (out_dep_path) |p| {
6265 try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p });
6266 }
6267
6268 // Non-preprocessed assembly files don't support these flags.
6269 if (ext != .assembly) {
6270 try argv.append(if (target.os.tag == .freestanding) "-ffreestanding" else "-fhosted");
6271
6272 try argv.append("-nostdinc");
6273
6274 if (ext == .cpp or ext == .hpp) {
6275 try argv.append("-nostdinc++");
6276 }
6277
6278 // LLVM IR files don't support these flags.
6279 if (ext != .ll and ext != .bc) {
6280 switch (mod.optimize_mode) {
6281 .debug => {},
6282 .safe => {
6283 try argv.append("-D_FORTIFY_SOURCE=2");
6284 },
6285 .fast, .small => {
6286 try argv.append("-DNDEBUG");
6287 },
6288 }
6289
6290 switch (target.os.tag) {
6291 // LLVM doesn't distinguish between Solaris and illumos, but the illumos GCC fork
6292 // defines this macro.
6293 .illumos => try argv.append("__illumos__"),
6294 // This macro has not yet been upstreamed by SerenityOS to Clang.
6295 .serenity => try argv.append("__serenity__"),
6296 // Homebrew targets without LLVM support; use communities's preferred macros.
6297 .@"3ds" => try argv.append("-D__3DS__"),
6298 .wiiu => try argv.append("-D__WIIU__"),
6299 .@"switch" => try argv.append("-D__SWITCH__"),
6300 .gba => try argv.append("-D__GBA__"),
6301 .psx => try argv.append("-D__psx__"),
6302 .psp => try argv.append("-D__PSP__"),
6303 .vita => try argv.append("-D__vita__"),
6304 else => {},
6305 }
6306
6307 if (comp.config.link_libc) {
6308 if (target.isGnuLibC()) {
6309 const target_version = target.os.versionRange().gnuLibCVersion().?;
6310 const glibc_minor_define = try std.fmt.allocPrint(arena, "-D__GLIBC_MINOR__={d}", .{
6311 target_version.minor,
6312 });
6313 try argv.append(glibc_minor_define);
6314 } else if (target.isMinGW()) {
6315 try argv.append("-D__MSVCRT_VERSION__=0xE00"); // use ucrt
6316
6317 const minver: u16 = @truncate(@backingInt(target.os.versionRange().windows.min) >> 16);
6318 try argv.append(
6319 try std.fmt.allocPrint(arena, "-D_WIN32_WINNT=0x{x:0>4}", .{minver}),
6320 );
6321
6322 // MinGW-w64's inline functions in headers (e.g. `fabs`), which are emitted with `linkonce_odr`
6323 // linkage, sometimes cause duplicate symbol errors due to us providing the same symbols with
6324 // `weak` linkage in compiler-rt or libzigc. So just disable them. Besides, they undermine the
6325 // goal of moving more libc code to Zig, and they're also just kind of unnecessary since LLVM is
6326 // perfectly capable of recognizing and optimizing libcalls.
6327 try argv.append("-D__CRT__NO_INLINE");
6328 } else if (target.isFreeBSDLibC()) {
6329 // https://docs.freebsd.org/en/books/porters-handbook/versions
6330 const min_ver = target.os.version_range.semver.min;
6331 try argv.append(try std.fmt.allocPrint(arena, "-D__FreeBSD_version={d}", .{
6332 // We don't currently respect the minor and patch components. This wouldn't be particularly
6333 // helpful because our abilists file only tracks major FreeBSD releases, so the link-time stub
6334 // symbols would be inconsistent with header declarations.
6335 min_ver.major * 100_000 + 500,
6336 }));
6337 } else if (target.isNetBSDLibC()) {
6338 const min_ver = target.os.version_range.semver.min;
6339 try argv.append(try std.fmt.allocPrint(arena, "-D__NetBSD_Version__={d}", .{
6340 // We don't currently respect the patch component. This wouldn't be particularly helpful because
6341 // our abilists file only tracks major and minor NetBSD releases, so the link-time stub symbols
6342 // would be inconsistent with header declarations.
6343 (min_ver.major * 100_000_000) + (min_ver.minor * 1_000_000),
6344 }));
6345 } else if (target.isOpenBSDLibC()) {
6346 const min_ver = target.os.version_range.semver.min;
6347 // The macro in sys/param.h doesn't have the leading underscores, but we don't want to pollute the
6348 // global namespace in all compilation units. So we use leading underscores and modify sys/param.h
6349 // to just alias this one.
6350 try argv.append(try std.fmt.allocPrint(arena, "-D___OpenBSD={d}", .{
6351 // Brilliantly, OpenBSD defines this macro to the year and month of the release, so we need to
6352 // maintain a manual mapping here whenever we update the headers.
6353 202510,
6354 }));
6355 // We can't avoid pollution for this one...
6356 try argv.append(try std.fmt.allocPrint(arena, "-DOpenBSD{d}_{d}", .{
6357 min_ver.major,
6358 min_ver.minor,
6359 }));
6360 }
6361 }
6362
6363 if (comp.config.link_libcpp) {
6364 try argv.append("-isystem");
6365 try argv.append(try fs.path.join(arena, &[_][]const u8{
6366 comp.dirs.zig_lib.path.?, "libcxx", "include",
6367 }));
6368
6369 try argv.append("-isystem");
6370 try argv.append(try fs.path.join(arena, &[_][]const u8{
6371 comp.dirs.zig_lib.path.?, "libcxxabi", "include",
6372 }));
6373
6374 try libcxx.addCxxArgs(comp, arena, argv);
6375 }
6376
6377 // According to Rich Felker libc headers are supposed to go before C language headers.
6378 // However as noted by @dimenus, appending libc headers before compiler headers breaks
6379 // intrinsics and other compiler specific items.
6380 try argv.append("-isystem");
6381 try argv.append(try fs.path.join(arena, &.{ comp.dirs.zig_lib.path.?, "include" }));
6382
6383 try argv.ensureUnusedCapacity(comp.libc_include_dir_list.len * 2);
6384 for (comp.libc_include_dir_list) |include_dir| {
6385 try argv.append("-isystem");
6386 try argv.append(include_dir);
6387 }
6388
6389 if (mod.resolved_target.is_native_os and mod.resolved_target.is_native_abi) {
6390 try argv.ensureUnusedCapacity(comp.native_system_include_paths.len * 2);
6391 for (comp.native_system_include_paths) |include_path| {
6392 argv.appendAssumeCapacity("-isystem");
6393 argv.appendAssumeCapacity(include_path);
6394 }
6395 }
6396
6397 if (comp.config.link_libunwind) {
6398 try argv.append("-isystem");
6399 try argv.append(try fs.path.join(arena, &[_][]const u8{
6400 comp.dirs.zig_lib.path.?, "libunwind", "include",
6401 }));
6402 }
6403
6404 try argv.ensureUnusedCapacity(comp.libc_framework_dir_list.len * 2);
6405 for (comp.libc_framework_dir_list) |framework_dir| {
6406 try argv.appendSlice(&.{ "-iframework", framework_dir });
6407 }
6408
6409 try argv.ensureUnusedCapacity(comp.framework_dirs.len * 2);
6410 for (comp.framework_dirs) |framework_dir| {
6411 try argv.appendSlice(&.{ "-F", framework_dir });
6412 }
6413 }
6414 }
6415
6416 // Only C-family files support these flags.
6417 switch (ext) {
6418 .c,
6419 .h,
6420 .cpp,
6421 .hpp,
6422 .m,
6423 .hm,
6424 .mm,
6425 .hmm,
6426 => {
6427 if (is_clang) {
6428 try argv.append("-fno-spell-checking");
6429
6430 if (target.os.tag == .windows and target.abi.isGnu()) {
6431 // windows.h has files such as pshpack1.h which do #pragma packing,
6432 // triggering a clang warning. So for this target, we disable this warning.
6433 try argv.append("-Wno-pragma-pack");
6434 }
6435 }
6436
6437 if (mod.optimize_mode != .debug) {
6438 try argv.append("-Werror=date-time");
6439 }
6440 },
6441 else => {},
6442 }
6443
6444 // Only compiled files support these flags.
6445 switch (ext) {
6446 .c,
6447 .h,
6448 .cpp,
6449 .hpp,
6450 .m,
6451 .hm,
6452 .mm,
6453 .hmm,
6454 .ll,
6455 .bc,
6456 => {
6457 if (mod.code_model != .default) {
6458 try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mod.code_model)}));
6459 }
6460
6461 if (is_clang) {
6462 var san_arg: std.ArrayList(u8) = .empty;
6463 const prefix = "-fsanitize=";
6464 if (mod.sanitize_c != .off) {
6465 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
6466 try san_arg.appendSlice(arena, "undefined,");
6467 }
6468 if (mod.sanitize_thread) {
6469 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
6470 try san_arg.appendSlice(arena, "thread,");
6471 }
6472 if (mod.fuzz) {
6473 if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
6474 try san_arg.appendSlice(arena, "fuzzer-no-link,");
6475 }
6476 // Chop off the trailing comma and append to argv.
6477 if (san_arg.pop()) |_| {
6478 try argv.append(san_arg.items);
6479
6480 switch (mod.sanitize_c) {
6481 .off => {},
6482 .trap => {
6483 try argv.append("-fsanitize-trap=undefined");
6484 },
6485 .full => {
6486 // This check requires implementing the Itanium C++ ABI.
6487 // We would make it `-fsanitize-trap=vptr`, however this check requires
6488 // a full runtime due to the type hashing involved.
6489 try argv.append("-fno-sanitize=vptr");
6490
6491 // It is very common, and well-defined, for a pointer on one side of a C ABI
6492 // to have a different but compatible element type. Examples include:
6493 // `char*` vs `uint8_t*` on a system with 8-bit bytes
6494 // `const char*` vs `char*`
6495 // `char*` vs `unsigned char*`
6496 // Without this flag, Clang would invoke UBSAN when such an extern
6497 // function was called.
6498 try argv.append("-fno-sanitize=function");
6499
6500 // This is necessary because, by default, Clang instructs LLVM to embed
6501 // a COFF link dependency on `libclang_rt.ubsan_standalone.a` when the
6502 // UBSan runtime is used.
6503 if (target.os.tag == .windows) {
6504 try argv.append("-fno-rtlib-defaultlib");
6505 }
6506 },
6507 }
6508 }
6509
6510 if (comp.config.san_cov_trace_pc_guard) {
6511 try argv.append("-fsanitize-coverage=trace-pc-guard");
6512 }
6513 }
6514
6515 switch (mod.optimize_mode) {
6516 .debug => {
6517 // Clang has -Og for compatibility with GCC, but currently it is just equivalent
6518 // to -O1. Besides potentially impairing debugging, -O1/-Og significantly
6519 // increases compile times.
6520 try argv.append("-O0");
6521 },
6522 .safe => {
6523 // See the comment in the BuildModeFastRelease case for why we pass -O2 rather
6524 // than -O3 here.
6525 try argv.append("-O2");
6526 },
6527 .fast => {
6528 // Here we pass -O2 rather than -O3 because, although we do the equivalent of
6529 // -O3 in Zig code, the justification for the difference here is that Zig
6530 // has better detection and prevention of undefined behavior, so -O3 is safer for
6531 // Zig code than it is for C code. Also, C programmers are used to their code
6532 // running in -O2 and thus the -O3 path has been tested less.
6533 try argv.append("-O2");
6534 },
6535 .small => {
6536 try argv.append("-Os");
6537 },
6538 }
6539 },
6540 else => {},
6541 }
6542}
6543
6544/// Add common C compiler args and Clang specific args.
6545pub fn addCCArgs(
6546 comp: *const Compilation,
6547 arena: Allocator,
6548 argv: *std.array_list.Managed([]const u8),
6549 ext: FileExt,
6550 out_dep_path: ?[]const u8,
6551 mod: *Module,
6552) !void {
6553 const target = &mod.resolved_target.result;
6554
6555 // As of Clang 16.x, it will by default read extra flags from /etc/clang.
6556 // I'm sure the person who implemented this means well, but they have a lot
6557 // to learn about abstractions and where the appropriate boundaries between
6558 // them are. The road to hell is paved with good intentions. Fortunately it
6559 // can be disabled.
6560 try argv.append("--no-default-config");
6561
6562 // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode
6563 // we want Clang to infer it, and in normal mode we always want it off, which will be true since
6564 // clang will detect stderr as a pipe rather than a terminal.
6565 if (!comp.clang_passthrough_mode and ext.clangSupportsDiagnostics()) {
6566 // Make stderr more easily parseable.
6567 try argv.append("-fno-caret-diagnostics");
6568 }
6569
6570 // We never want clang to invoke the system assembler for anything. So we would want
6571 // this option always enabled. However, it only matters for some targets. To avoid
6572 // "unused parameter" warnings, and to keep CLI spam to a minimum, we only put this
6573 // flag on the command line if it is necessary.
6574 if (target_util.clangMightShellOutForAssembly(target)) {
6575 try argv.append("-integrated-as");
6576 }
6577
6578 const llvm_triple = try std.zig.llvm.Builder.tripleForTarget(arena, target);
6579 try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
6580
6581 if (target.cpu.arch.isThumb()) {
6582 try argv.append(switch (ext) {
6583 .assembly, .assembly_with_cpp => "-Wa,-mthumb",
6584 else => "-mthumb",
6585 });
6586 }
6587
6588 if (target_util.llvmMachineAbi(target)) |mabi| {
6589 // Clang's integrated Arm assembler doesn't support `-mabi` yet...
6590 // Clang's FreeBSD driver doesn't support `-mabi` on PPC64 (ELFv2 is used anyway).
6591 if (!(target.cpu.arch.isArm() and (ext == .assembly or ext == .assembly_with_cpp)) and
6592 !(target.cpu.arch.isPowerPC64() and target.os.tag == .freebsd))
6593 {
6594 try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{mabi}));
6595 }
6596 }
6597
6598 if (target.cpu.arch.isPowerPC()) {
6599 // We do not -- and probably never will -- support the IBM 128-bit `long double` format.
6600 // LLVM and Clang also do not have complete support for it, producing wrong values in some
6601 // cases. So just enforce IEEE `long double` everywhere - either binary64 or binary128
6602 // depending on what the OS/ABI requires.
6603 try argv.appendSlice(&.{
6604 "-mabi=ieeelongdouble",
6605 // Clang has some truly goofy logic for emitting warnings about the
6606 // "current library" not supporting IEEE `long double`.
6607 "-Wno-unsupported-abi",
6608 });
6609 }
6610
6611 // We might want to support -mfloat-abi=softfp for Arm and CSKY here in the future.
6612 if (target_util.clangSupportsFloatAbiArg(target)) {
6613 const fabi = @tagName(target.abi.float());
6614
6615 try argv.append(switch (target.cpu.arch) {
6616 // For whatever reason, Clang doesn't support `-mfloat-abi` for s390x.
6617 .s390x => try std.fmt.allocPrint(arena, "-m{s}-float", .{fabi}),
6618 else => try std.fmt.allocPrint(arena, "-mfloat-abi={s}", .{fabi}),
6619 });
6620 }
6621
6622 try comp.addCommonCCArgs(arena, argv, ext, out_dep_path, mod, comp.config.c_frontend);
6623
6624 // Only assembly files support these flags.
6625 switch (ext) {
6626 .assembly,
6627 .assembly_with_cpp,
6628 => {
6629 // The Clang assembler does not accept the list of CPU features like the
6630 // compiler frontend does. Therefore we must hard-code the -m flags for
6631 // all CPU features here.
6632 switch (target.cpu.arch) {
6633 .riscv32, .riscv32be, .riscv64, .riscv64be => {
6634 const RvArchFeat = struct { char: u8, feat: std.Target.riscv.Feature };
6635 const letters = [_]RvArchFeat{
6636 .{ .char = 'm', .feat = .m },
6637 .{ .char = 'a', .feat = .a },
6638 .{ .char = 'f', .feat = .f },
6639 .{ .char = 'd', .feat = .d },
6640 .{ .char = 'c', .feat = .c },
6641 };
6642 const prefix: []const u8 = if (target.cpu.arch == .riscv64) "rv64" else "rv32";
6643 const prefix_len = 4;
6644 assert(prefix.len == prefix_len);
6645 var march_buf: [prefix_len + letters.len + 1]u8 = undefined;
6646 var march_index: usize = prefix_len;
6647 @memcpy(march_buf[0..prefix.len], prefix);
6648
6649 if (target.cpu.has(.riscv, .e)) {
6650 march_buf[march_index] = 'e';
6651 } else {
6652 march_buf[march_index] = 'i';
6653 }
6654 march_index += 1;
6655
6656 for (letters) |letter| {
6657 if (target.cpu.has(.riscv, letter.feat)) {
6658 march_buf[march_index] = letter.char;
6659 march_index += 1;
6660 }
6661 }
6662
6663 const march_arg = try std.fmt.allocPrint(arena, "-march={s}", .{
6664 march_buf[0..march_index],
6665 });
6666 try argv.append(march_arg);
6667
6668 if (target.cpu.has(.riscv, .relax)) {
6669 try argv.append("-mrelax");
6670 } else {
6671 try argv.append("-mno-relax");
6672 }
6673 if (target.cpu.has(.riscv, .save_restore)) {
6674 try argv.append("-msave-restore");
6675 } else {
6676 try argv.append("-mno-save-restore");
6677 }
6678 },
6679 .mips, .mipsel, .mips64, .mips64el => {
6680 if (target.cpu.model.llvm_name) |llvm_name| {
6681 try argv.append(try std.fmt.allocPrint(arena, "-march={s}", .{llvm_name}));
6682 }
6683 },
6684 else => {
6685 // TODO
6686 },
6687 }
6688
6689 if (target_util.clangAssemblerSupportsMcpuArg(target)) {
6690 if (target.cpu.model.llvm_name) |llvm_name| {
6691 try argv.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{llvm_name}));
6692 }
6693 }
6694 },
6695 else => {},
6696 }
6697
6698 // Non-preprocessed assembly files don't support these flags.
6699 if (ext != .assembly) {
6700 if (target_util.clangSupportsNoImplicitFloatArg(target) and target.abi.float() == .soft) {
6701 try argv.append("-mno-implicit-float");
6702 }
6703
6704 if (target_util.hasRedZone(target)) {
6705 try argv.append(if (mod.red_zone) "-mred-zone" else "-mno-red-zone");
6706 }
6707
6708 try argv.append(if (mod.omit_frame_pointer) "-fomit-frame-pointer" else "-fno-omit-frame-pointer");
6709 if (target.cpu.arch == .s390x) {
6710 try argv.append(if (mod.omit_frame_pointer) "-mbackchain" else "-mno-backchain");
6711 }
6712
6713 const ssp_buf_size = mod.stack_protector;
6714 if (ssp_buf_size != 0) {
6715 try argv.appendSlice(&[_][]const u8{
6716 "-fstack-protector-strong",
6717 "--param",
6718 try std.fmt.allocPrint(arena, "ssp-buffer-size={d}", .{ssp_buf_size}),
6719 });
6720 } else {
6721 try argv.append("-fno-stack-protector");
6722 }
6723
6724 try argv.append(if (mod.no_builtin) "-fno-builtin" else "-fbuiltin");
6725
6726 try argv.append(if (comp.function_sections) "-ffunction-sections" else "-fno-function-sections");
6727 try argv.append(if (comp.data_sections) "-fdata-sections" else "-fno-data-sections");
6728
6729 switch (mod.unwind_tables) {
6730 .none => {
6731 try argv.append("-fno-unwind-tables");
6732 try argv.append("-fno-asynchronous-unwind-tables");
6733 },
6734 .sync => {
6735 // Need to override Clang's convoluted default logic.
6736 try argv.append("-fno-asynchronous-unwind-tables");
6737 try argv.append("-funwind-tables");
6738 },
6739 .async => try argv.append("-fasynchronous-unwind-tables"),
6740 }
6741 }
6742
6743 // Only compiled files support these flags.
6744 switch (ext) {
6745 .assembly,
6746 .assembly_with_cpp,
6747 .c,
6748 .h,
6749 .cpp,
6750 .hpp,
6751 .m,
6752 .hm,
6753 .mm,
6754 .hmm,
6755 .ll,
6756 .bc,
6757 => {
6758 const xclang_flag = switch (ext) {
6759 .assembly, .assembly_with_cpp => "-Xclangas",
6760 else => "-Xclang",
6761 };
6762
6763 if (target_util.clangSupportsTargetCpuArg(target)) {
6764 if (target.cpu.model.llvm_name) |llvm_name| {
6765 try argv.appendSlice(&[_][]const u8{
6766 xclang_flag, "-target-cpu", xclang_flag, llvm_name,
6767 });
6768 }
6769 }
6770
6771 // It would be really nice if there was a more compact way to communicate this info to Clang.
6772 const all_features_list = target.cpu.arch.allFeaturesList();
6773 try argv.ensureUnusedCapacity(all_features_list.len * 4);
6774 for (all_features_list, 0..) |feature, index_usize| {
6775 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
6776 const is_enabled = target.cpu.features.isEnabled(index);
6777
6778 if (feature.llvm_name) |llvm_name| {
6779 // We communicate these to Clang through the dedicated options.
6780 if (std.mem.startsWith(u8, llvm_name, "soft-float") or
6781 std.mem.startsWith(u8, llvm_name, "hard-float") or
6782 (target.cpu.arch.isPowerPC() and std.mem.startsWith(u8, llvm_name, "64bit")) or
6783 (target.cpu.arch.isX86() and std.mem.startsWith(u8, llvm_name, "x32")) or
6784 (target.cpu.arch == .s390x and std.mem.eql(u8, llvm_name, "backchain")))
6785 continue;
6786
6787 // Ignore these until we figure out how to handle the concept of omitting features.
6788 // See https://github.com/ziglang/zig/issues/23539
6789 if (target_util.isDynamicAMDGCNFeature(target, feature)) continue;
6790
6791 argv.appendSliceAssumeCapacity(&[_][]const u8{ xclang_flag, "-target-feature", xclang_flag });
6792 const plus_or_minus = "-+"[@intFromBool(is_enabled)];
6793 const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name });
6794 argv.appendAssumeCapacity(arg);
6795 }
6796 }
6797 },
6798 else => {},
6799 }
6800
6801 try argv.appendSlice(comp.global_cc_argv);
6802 try argv.appendSlice(mod.cc_argv);
6803}
6804
6805fn failCObj(
6806 comp: *Compilation,
6807 c_object: *CObject,
6808 comptime format: []const u8,
6809 args: anytype,
6810) error{ OutOfMemory, AlreadyReported } {
6811 @branchHint(.cold);
6812 const diag_bundle = blk: {
6813 const diag_bundle = try comp.gpa.create(CObject.Diag.Bundle);
6814 diag_bundle.* = .{};
6815 errdefer diag_bundle.destroy(comp.gpa);
6816
6817 try diag_bundle.file_names.ensureTotalCapacity(comp.gpa, 1);
6818 diag_bundle.file_names.putAssumeCapacity(1, try comp.gpa.dupe(u8, c_object.src.src_path));
6819
6820 diag_bundle.diags = try comp.gpa.alloc(CObject.Diag, 1);
6821 diag_bundle.diags[0] = .{};
6822 diag_bundle.diags[0].level = 3;
6823 diag_bundle.diags[0].msg = try std.fmt.allocPrint(comp.gpa, format, args);
6824 diag_bundle.diags[0].src_loc.file = 1;
6825 break :blk diag_bundle;
6826 };
6827 return comp.failCObjWithOwnedDiagBundle(c_object, diag_bundle);
6828}
6829
6830fn failCObjWithOwnedDiagBundle(
6831 comp: *Compilation,
6832 c_object: *CObject,
6833 diag_bundle: *CObject.Diag.Bundle,
6834) error{ OutOfMemory, AlreadyReported } {
6835 @branchHint(.cold);
6836 assert(diag_bundle.diags.len > 0);
6837 {
6838 const io = comp.io;
6839 comp.mutex.lockUncancelable(io);
6840 defer comp.mutex.unlock(io);
6841 {
6842 errdefer diag_bundle.destroy(comp.gpa);
6843 try comp.failed_c_objects.ensureUnusedCapacity(comp.gpa, 1);
6844 }
6845 comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, diag_bundle);
6846 }
6847 c_object.status = .failure;
6848 return error.AlreadyReported;
6849}
6850
6851fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } {
6852 @branchHint(.cold);
6853 var bundle: ErrorBundle.Wip = undefined;
6854 try bundle.init(comp.gpa);
6855 errdefer bundle.deinit();
6856 try bundle.addRootErrorMessage(.{
6857 .msg = try bundle.printString(format, args),
6858 .src_loc = try bundle.addSourceLocation(.{
6859 .src_path = try bundle.addString(switch (win32_resource.src) {
6860 .rc => |rc_src| rc_src.src_path,
6861 .manifest => |manifest_src| manifest_src,
6862 }),
6863 .line = 0,
6864 .column = 0,
6865 .span_start = 0,
6866 .span_main = 0,
6867 .span_end = 0,
6868 }),
6869 });
6870 const finished_bundle = try bundle.toOwnedBundle("");
6871 return comp.failWin32ResourceWithOwnedBundle(win32_resource, finished_bundle);
6872}
6873
6874fn failWin32ResourceWithOwnedBundle(
6875 comp: *Compilation,
6876 win32_resource: *Win32Resource,
6877 err_bundle: ErrorBundle,
6878) error{ OutOfMemory, AlreadyReported } {
6879 @branchHint(.cold);
6880 {
6881 const io = comp.io;
6882 comp.mutex.lockUncancelable(io);
6883 defer comp.mutex.unlock(io);
6884 try comp.failed_win32_resources.putNoClobber(comp.gpa, win32_resource, err_bundle);
6885 }
6886 win32_resource.status = .failure;
6887 return error.AlreadyReported;
6888}
6889
6890pub const FileExt = enum {
6891 c,
6892 cpp,
6893 h,
6894 hpp,
6895 hm,
6896 hmm,
6897 m,
6898 mm,
6899 ll,
6900 bc,
6901 assembly,
6902 assembly_with_cpp,
6903 shared_library,
6904 object,
6905 static_library,
6906 zig,
6907 def,
6908 rc,
6909 res,
6910 manifest,
6911 unknown,
6912
6913 pub fn clangNeedsLanguageOverride(ext: FileExt) bool {
6914 return switch (ext) {
6915 .h,
6916 .hpp,
6917 .hm,
6918 .hmm,
6919 => true,
6920
6921 .c,
6922 .cpp,
6923 .m,
6924 .mm,
6925 .ll,
6926 .bc,
6927 .assembly,
6928 .assembly_with_cpp,
6929 .shared_library,
6930 .object,
6931 .static_library,
6932 .zig,
6933 .def,
6934 .rc,
6935 .res,
6936 .manifest,
6937 .unknown,
6938 => false,
6939 };
6940 }
6941
6942 pub fn clangSupportsDiagnostics(ext: FileExt) bool {
6943 return switch (ext) {
6944 .c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm, .ll, .bc => true,
6945
6946 .assembly,
6947 .assembly_with_cpp,
6948 .shared_library,
6949 .object,
6950 .static_library,
6951 .zig,
6952 .def,
6953 .rc,
6954 .res,
6955 .manifest,
6956 .unknown,
6957 => false,
6958 };
6959 }
6960
6961 pub fn clangSupportsDepFile(ext: FileExt) bool {
6962 return switch (ext) {
6963 .assembly_with_cpp, .c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm => true,
6964
6965 .ll,
6966 .bc,
6967 .assembly,
6968 .shared_library,
6969 .object,
6970 .static_library,
6971 .zig,
6972 .def,
6973 .rc,
6974 .res,
6975 .manifest,
6976 .unknown,
6977 => false,
6978 };
6979 }
6980
6981 pub fn canonicalName(ext: FileExt, target: *const Target) [:0]const u8 {
6982 return switch (ext) {
6983 .c => ".c",
6984 .cpp => ".cpp",
6985 .h => ".h",
6986 .hpp => ".hpp",
6987 .hm => ".hm",
6988 .hmm => ".hmm",
6989 .m => ".m",
6990 .mm => ".mm",
6991 .ll => ".ll",
6992 .bc => ".bc",
6993 .assembly => ".s",
6994 .assembly_with_cpp => ".S",
6995 .shared_library => target.dynamicLibSuffix(),
6996 .object => target.ofmt.fileExt(target.cpu.arch),
6997 .static_library => target.staticLibSuffix(),
6998 .zig => ".zig",
6999 .def => ".def",
7000 .rc => ".rc",
7001 .res => ".res",
7002 .manifest => ".manifest",
7003 .unknown => "",
7004 };
7005 }
7006
7007 /// The value accepted by "zig clang -x <lang>" and passed to "clang -x <lang>".
7008 pub fn toLang(ext: FileExt) ?[]const u8 {
7009 return switch (ext) {
7010 else => null,
7011 .c => "c",
7012 .h => "c-header",
7013 .cpp => "c++",
7014 .hpp => "c++-header",
7015 .m => "objective-c",
7016 .hm => "objective-c-header",
7017 .mm => "objective-c++",
7018 .hmm => "objective-c++-header",
7019 .assembly => "assembler",
7020 .assembly_with_cpp => "assembler-with-cpp",
7021 };
7022 }
7023
7024 /// Supported languages for "zig clang -x <lang>".
7025 /// Loosely based on llvm-project/clang/include/clang/Driver/Types.def
7026 pub const from_lang = std.StaticStringMap(FileExt).initComptime(init: {
7027 var init: []const struct { []const u8, FileExt } = &.{};
7028 for (std.enums.values(FileExt)) |file_ext| if (file_ext.toLang()) |lang| {
7029 init = init ++ .{.{ lang, file_ext }};
7030 };
7031 break :init init;
7032 });
7033};
7034
7035pub fn hasObjectExt(filename: []const u8) bool {
7036 return mem.endsWith(u8, filename, ".o") or
7037 mem.endsWith(u8, filename, ".lo") or
7038 mem.endsWith(u8, filename, ".obj") or
7039 mem.endsWith(u8, filename, ".rmeta") or
7040 mem.endsWith(u8, filename, ".spv");
7041}
7042
7043pub fn hasStaticLibraryExt(filename: []const u8) bool {
7044 return mem.endsWith(u8, filename, ".a") or
7045 mem.endsWith(u8, filename, ".lib") or
7046 mem.endsWith(u8, filename, ".rlib");
7047}
7048
7049pub fn hasCExt(filename: []const u8) bool {
7050 return mem.endsWith(u8, filename, ".c");
7051}
7052
7053pub fn hasCHExt(filename: []const u8) bool {
7054 return mem.endsWith(u8, filename, ".h");
7055}
7056
7057pub fn hasCppExt(filename: []const u8) bool {
7058 return mem.endsWith(u8, filename, ".C") or
7059 mem.endsWith(u8, filename, ".cc") or
7060 mem.endsWith(u8, filename, ".cp") or
7061 mem.endsWith(u8, filename, ".CPP") or
7062 mem.endsWith(u8, filename, ".cpp") or
7063 mem.endsWith(u8, filename, ".cxx") or
7064 mem.endsWith(u8, filename, ".c++");
7065}
7066
7067pub fn hasCppHExt(filename: []const u8) bool {
7068 return mem.endsWith(u8, filename, ".hh") or
7069 mem.endsWith(u8, filename, ".hpp") or
7070 mem.endsWith(u8, filename, ".hxx");
7071}
7072
7073pub fn hasObjCExt(filename: []const u8) bool {
7074 return mem.endsWith(u8, filename, ".m");
7075}
7076
7077pub fn hasObjCHExt(filename: []const u8) bool {
7078 return mem.endsWith(u8, filename, ".hm");
7079}
7080
7081pub fn hasObjCppExt(filename: []const u8) bool {
7082 return mem.endsWith(u8, filename, ".M") or
7083 mem.endsWith(u8, filename, ".mm");
7084}
7085
7086pub fn hasObjCppHExt(filename: []const u8) bool {
7087 return mem.endsWith(u8, filename, ".hmm");
7088}
7089
7090pub fn hasSharedLibraryExt(filename: []const u8) bool {
7091 if (mem.endsWith(u8, filename, ".so") or
7092 mem.endsWith(u8, filename, ".dll") or
7093 mem.endsWith(u8, filename, ".dylib") or
7094 mem.endsWith(u8, filename, ".tbd"))
7095 {
7096 return true;
7097 }
7098 // Look for .so.X, .so.X.Y, .so.X.Y.Z.*
7099 var it = mem.splitScalar(u8, filename, '.');
7100 _ = it.first();
7101 var so_txt = it.next() orelse return false;
7102 while (!mem.eql(u8, so_txt, "so")) {
7103 so_txt = it.next() orelse return false;
7104 }
7105 const n1 = it.next() orelse return false;
7106 const n2 = it.next();
7107 const n3 = it.next();
7108
7109 _ = std.fmt.parseInt(u32, n1, 10) catch return false;
7110 if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
7111 if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
7112
7113 return true;
7114}
7115
7116pub fn classifyFileExt(filename: []const u8) FileExt {
7117 if (hasCExt(filename)) {
7118 return .c;
7119 } else if (hasCHExt(filename)) {
7120 return .h;
7121 } else if (hasCppExt(filename)) {
7122 return .cpp;
7123 } else if (hasCppHExt(filename)) {
7124 return .hpp;
7125 } else if (hasObjCExt(filename)) {
7126 return .m;
7127 } else if (hasObjCHExt(filename)) {
7128 return .hm;
7129 } else if (hasObjCppExt(filename)) {
7130 return .mm;
7131 } else if (hasObjCppHExt(filename)) {
7132 return .hmm;
7133 } else if (mem.endsWith(u8, filename, ".ll")) {
7134 return .ll;
7135 } else if (mem.endsWith(u8, filename, ".bc")) {
7136 return .bc;
7137 } else if (mem.endsWith(u8, filename, ".s")) {
7138 return .assembly;
7139 } else if (mem.endsWith(u8, filename, ".S")) {
7140 return .assembly_with_cpp;
7141 } else if (mem.endsWith(u8, filename, ".zig")) {
7142 return .zig;
7143 } else if (hasStaticLibraryExt(filename)) {
7144 return .static_library;
7145 } else if (hasObjectExt(filename)) {
7146 return .object;
7147 } else if (mem.endsWith(u8, filename, ".def")) {
7148 return .def;
7149 } else if (std.ascii.endsWithIgnoreCase(filename, ".rc")) {
7150 return .rc;
7151 } else if (std.ascii.endsWithIgnoreCase(filename, ".res")) {
7152 return .res;
7153 } else if (std.ascii.endsWithIgnoreCase(filename, ".manifest")) {
7154 return .manifest;
7155 } else if (hasSharedLibraryExt(filename)) { // currently the only check that doesn't only look at the end, thus goes last
7156 return .shared_library;
7157 } else {
7158 return .unknown;
7159 }
7160}
7161
7162test "classifyFileExt" {
7163 try std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc"));
7164 try std.testing.expectEqual(FileExt.m, classifyFileExt("foo.m"));
7165 try std.testing.expectEqual(FileExt.mm, classifyFileExt("foo.mm"));
7166 try std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim"));
7167 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so"));
7168 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1"));
7169 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2"));
7170 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2.3"));
7171 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2.3.4"));
7172 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2.3.dev4"));
7173 try std.testing.expectEqual(FileExt.static_library, classifyFileExt("foo.so.1.a"));
7174 try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
7175}
7176
7177fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Cache.Path {
7178 return (try crtFilePath(&comp.crt_files, basename)) orelse {
7179 const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable;
7180 const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir;
7181 const full_path = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
7182 return Cache.Path.initCwd(full_path);
7183 };
7184}
7185
7186pub fn crtFileAsString(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
7187 const path = try get_libc_crt_file(comp, arena, basename);
7188 return path.toString(arena);
7189}
7190
7191fn crtFilePath(crt_files: *std.StringHashMapUnmanaged(CrtFile), basename: []const u8) Allocator.Error!?Cache.Path {
7192 const crt_file = crt_files.get(basename) orelse return null;
7193 return crt_file.full_object_path;
7194}
7195
7196fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
7197 const is_exe_or_dyn_lib = switch (comp.config.output_mode) {
7198 .Obj => false,
7199 .Lib => comp.config.link_mode == .dynamic,
7200 .Exe => true,
7201 };
7202 const ofmt = comp.root_mod.resolved_target.result.ofmt;
7203 return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c;
7204}
7205
7206pub fn setAllocFailure(comp: *Compilation) void {
7207 @branchHint(.cold);
7208 log.debug("memory allocation failure", .{});
7209 comp.alloc_failure_occurred = true;
7210}
7211
7212/// Assumes that Compilation mutex is locked.
7213/// See also `lockAndSetMiscFailure`.
7214pub fn setMiscFailure(
7215 comp: *Compilation,
7216 tag: MiscTask,
7217 comptime format: []const u8,
7218 args: anytype,
7219) void {
7220 comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1) catch return comp.setAllocFailure();
7221 const msg = std.fmt.allocPrint(comp.gpa, format, args) catch return comp.setAllocFailure();
7222 const gop = comp.misc_failures.getOrPutAssumeCapacity(tag);
7223 if (gop.found_existing) {
7224 gop.value_ptr.deinit(comp.gpa);
7225 }
7226 gop.value_ptr.* = .{ .msg = msg };
7227}
7228
7229/// See also `setMiscFailure`.
7230pub fn lockAndSetMiscFailure(
7231 comp: *Compilation,
7232 tag: MiscTask,
7233 comptime format: []const u8,
7234 args: anytype,
7235) void {
7236 const io = comp.io;
7237 comp.mutex.lockUncancelable(io);
7238 defer comp.mutex.unlock(io);
7239 return setMiscFailure(comp, tag, format, args);
7240}
7241
7242pub fn dumpArgv(io: Io, argv: []const []const u8) Io.Cancelable!void {
7243 var buffer: [64]u8 = undefined;
7244 const stderr = try io.lockStderr(&buffer, null);
7245 defer io.unlockStderr();
7246 const w = &stderr.file_writer.interface;
7247 return dumpArgvWriter(w, argv) catch |err| switch (err) {
7248 error.WriteFailed => switch (stderr.file_writer.err.?) {
7249 error.Canceled => |e| return e,
7250 else => return,
7251 },
7252 };
7253}
7254
7255fn dumpArgvWriter(w: *Io.Writer, argv: []const []const u8) Io.Writer.Error!void {
7256 for (argv, 0..) |arg, i| {
7257 if (i != 0) try w.writeByte(' ');
7258 try w.writeAll(arg);
7259 }
7260 try w.writeByte('\n');
7261}
7262
7263pub fn getZigBackend(comp: Compilation) std.lang.CompilerBackend {
7264 const target = &comp.root_mod.resolved_target.result;
7265 return target_util.zigBackend(target, comp.config.use_llvm);
7266}
7267
7268pub const SubUpdateError = UpdateError || error{AlreadyReported};
7269pub fn updateSubCompilation(
7270 parent_comp: *Compilation,
7271 sub_comp: *Compilation,
7272 misc_task: MiscTask,
7273 prog_node: std.Progress.Node,
7274) SubUpdateError!void {
7275 {
7276 const sub_node = prog_node.start(@tagName(misc_task), 0);
7277 defer sub_node.end();
7278
7279 try sub_comp.update(sub_node);
7280 }
7281
7282 // Look for compilation errors in this sub compilation
7283 const gpa = parent_comp.gpa;
7284
7285 var errors = try sub_comp.getAllErrorsAlloc();
7286 defer errors.deinit(gpa);
7287
7288 if (errors.errorMessageCount() > 0) {
7289 parent_comp.mutex.lockUncancelable(parent_comp.io);
7290 defer parent_comp.mutex.unlock(parent_comp.io);
7291 try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1);
7292 parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{
7293 .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {t} failed", .{misc_task}),
7294 .children = errors,
7295 });
7296 errors = .empty; // ownership moved to the failures map
7297 return error.AlreadyReported;
7298 }
7299}
7300
7301fn buildOutputFromZig(
7302 comp: *Compilation,
7303 src_basename: []const u8,
7304 root_name: []const u8,
7305 output_mode: std.lang.OutputMode,
7306 link_mode: std.lang.LinkMode,
7307 misc_task_tag: MiscTask,
7308 prog_node: std.Progress.Node,
7309 options: RtOptions,
7310 out: *?CrtFile,
7311) SubUpdateError!void {
7312 const tracy_trace = trace(@src());
7313 defer tracy_trace.end();
7314
7315 const gpa = comp.gpa;
7316 const io = comp.io;
7317 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
7318 defer arena_allocator.deinit();
7319 const arena = arena_allocator.allocator();
7320
7321 assert(output_mode != .Exe);
7322
7323 const strip = comp.compilerRtStrip();
7324 const optimize_mode = comp.compilerRtOptMode();
7325
7326 const config = Config.resolve(.{
7327 .output_mode = output_mode,
7328 .link_mode = link_mode,
7329 .resolved_target = comp.root_mod.resolved_target,
7330 .is_test = false,
7331 .have_zcu = true,
7332 .emit_bin = true,
7333 .root_optimize_mode = optimize_mode,
7334 .root_strip = strip,
7335 .link_libc = comp.config.link_libc,
7336 .any_unwind_tables = comp.root_mod.unwind_tables != .none,
7337 .any_error_tracing = false,
7338 .root_error_tracing = false,
7339 .lto = if (options.allow_lto) comp.config.lto else .none,
7340 }) catch |err| {
7341 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to resolve compilation config: {t}", .{ misc_task_tag, err });
7342 return error.AlreadyReported;
7343 };
7344
7345 const root_mod = Module.create(arena, .{
7346 .paths = .{
7347 .root = .zig_lib_root,
7348 .root_src_path = src_basename,
7349 },
7350 .fully_qualified_name = "root",
7351 .inherited = .{
7352 .resolved_target = comp.root_mod.resolved_target,
7353 .strip = strip,
7354 .stack_check = false,
7355 .stack_protector = 0,
7356 .red_zone = comp.root_mod.red_zone,
7357 .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
7358 .unwind_tables = comp.root_mod.unwind_tables,
7359 .pic = comp.root_mod.pic,
7360 .optimize_mode = optimize_mode,
7361 .no_builtin = true,
7362 .code_model = comp.root_mod.code_model,
7363 .error_tracing = false,
7364 .valgrind = if (options.checks_valgrind) comp.root_mod.valgrind else null,
7365 },
7366 .global = config,
7367 .cc_argv = &.{},
7368 .parent = null,
7369 }) catch |err| {
7370 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to create module: {t}", .{ misc_task_tag, err });
7371 return error.AlreadyReported;
7372 };
7373
7374 const parent_whole_cache: ?ParentWholeCache = switch (comp.cache_use) {
7375 .whole => |whole| .{
7376 .manifest = whole.cache_manifest.?,
7377 .mutex = &whole.cache_manifest_mutex,
7378 .prefix_map = .{
7379 0, // cwd is the same
7380 1, // zig lib dir is the same
7381 3, // local cache is mapped to global cache
7382 3, // global cache is the same
7383 0, // build root is not provided
7384 },
7385 },
7386 .incremental, .none => null,
7387 };
7388
7389 var sub_create_diag: CreateDiagnostic = undefined;
7390 const sub_compilation = Compilation.create(gpa, arena, io, &sub_create_diag, .{
7391 .thread_limit = comp.thread_limit,
7392 .dirs = comp.dirs.withoutLocalCache(),
7393 .cache_mode = .whole,
7394 .parent_whole_cache = parent_whole_cache,
7395 .self_exe_path = comp.self_exe_path,
7396 .config = config,
7397 .root_mod = root_mod,
7398 .root_name = root_name,
7399 .libc_installation = comp.libc_installation,
7400 .emit_bin = .yes_cache,
7401 .function_sections = true,
7402 .data_sections = true,
7403 .verbose_cc = comp.verbose_cc,
7404 .verbose_link = comp.verbose_link,
7405 .verbose_air = comp.verbose_air,
7406 .verbose_intern_pool = comp.verbose_intern_pool,
7407 .verbose_generic_instances = comp.verbose_intern_pool,
7408 .verbose_llvm_ir = comp.verbose_llvm_ir,
7409 .verbose_llvm_bc = comp.verbose_llvm_bc,
7410 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
7411 .clang_passthrough_mode = comp.clang_passthrough_mode,
7412 .skip_linker_dependencies = true,
7413 .environ_map = comp.environ_map,
7414 }) catch |err| switch (err) {
7415 error.CreateFail => {
7416 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag });
7417 return error.AlreadyReported;
7418 },
7419 else => |e| return e,
7420 };
7421 defer sub_compilation.destroy();
7422
7423 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
7424
7425 const crt_file = try sub_compilation.toCrtFile();
7426 assert(out.* == null);
7427 out.* = crt_file;
7428
7429 try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config);
7430}
7431
7432pub const CrtFileOptions = struct {
7433 function_sections: bool = true,
7434 data_sections: bool = true,
7435 pic: ?bool = null,
7436 no_builtin: ?bool = null,
7437
7438 allow_lto: bool = true,
7439};
7440
7441pub fn build_crt_file(
7442 comp: *Compilation,
7443 root_name: []const u8,
7444 output_mode: std.lang.OutputMode,
7445 misc_task_tag: MiscTask,
7446 prog_node: std.Progress.Node,
7447 /// These elements have to get mutated to add the owner module after it is
7448 /// created within this function.
7449 c_source_files: []CSourceFile,
7450 options: CrtFileOptions,
7451) SubUpdateError!void {
7452 const tracy_trace = trace(@src());
7453 defer tracy_trace.end();
7454
7455 const gpa = comp.gpa;
7456 const io = comp.io;
7457 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
7458 defer arena_allocator.deinit();
7459 const arena = arena_allocator.allocator();
7460
7461 const target = &comp.root_mod.resolved_target.result;
7462
7463 const basename = try std.zig.binNameAlloc(gpa, .{
7464 .root_name = root_name,
7465 .cpu_arch = target.cpu.arch,
7466 .os_tag = target.os.tag,
7467 .ofmt = target.ofmt,
7468 .abi = target.abi,
7469 .output_mode = output_mode,
7470 });
7471
7472 const config = Config.resolve(.{
7473 .output_mode = output_mode,
7474 .resolved_target = comp.root_mod.resolved_target,
7475 .is_test = false,
7476 .have_zcu = false,
7477 .emit_bin = true,
7478 .root_optimize_mode = comp.compilerRtOptMode(),
7479 .root_strip = comp.compilerRtStrip(),
7480 .link_libc = false,
7481 .any_unwind_tables = comp.root_mod.unwind_tables != .none,
7482 .lto = switch (output_mode) {
7483 .Lib => if (options.allow_lto) comp.config.lto else .none,
7484 .Obj, .Exe => .none,
7485 },
7486 }) catch |err| {
7487 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to resolve compilation config: {t}", .{ misc_task_tag, err });
7488 return error.AlreadyReported;
7489 };
7490 const root_mod = Module.create(arena, .{
7491 .paths = .{
7492 .root = .zig_lib_root,
7493 .root_src_path = "",
7494 },
7495 .fully_qualified_name = "root",
7496 .inherited = .{
7497 .resolved_target = comp.root_mod.resolved_target,
7498 .strip = comp.compilerRtStrip(),
7499 .stack_check = false,
7500 .stack_protector = 0,
7501 .sanitize_c = .off,
7502 .sanitize_thread = false,
7503 .red_zone = comp.root_mod.red_zone,
7504 .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
7505 .valgrind = false,
7506 .unwind_tables = comp.root_mod.unwind_tables,
7507 // Some CRT objects (e.g. musl's rcrt1.o and Scrt1.o) are opinionated about PIC.
7508 .pic = options.pic orelse comp.root_mod.pic,
7509 .optimize_mode = comp.compilerRtOptMode(),
7510 // Some libcs (e.g. musl) are opinionated about -fno-builtin.
7511 .no_builtin = options.no_builtin orelse comp.root_mod.no_builtin,
7512 .code_model = comp.root_mod.code_model,
7513 },
7514 .global = config,
7515 .cc_argv = &.{},
7516 .parent = null,
7517 }) catch |err| {
7518 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to create module: {t}", .{ misc_task_tag, err });
7519 return error.AlreadyReported;
7520 };
7521
7522 for (c_source_files) |*item| {
7523 item.owner = root_mod;
7524 }
7525
7526 var sub_create_diag: CreateDiagnostic = undefined;
7527 const sub_compilation = Compilation.create(gpa, arena, io, &sub_create_diag, .{
7528 .thread_limit = comp.thread_limit,
7529 .dirs = comp.dirs.withoutLocalCache(),
7530 .self_exe_path = comp.self_exe_path,
7531 .cache_mode = .whole,
7532 .config = config,
7533 .root_mod = root_mod,
7534 .root_name = root_name,
7535 .libc_installation = comp.libc_installation,
7536 .emit_bin = .yes_cache,
7537 .function_sections = options.function_sections,
7538 .data_sections = options.data_sections,
7539 .c_source_files = c_source_files,
7540 .verbose_cc = comp.verbose_cc,
7541 .verbose_link = comp.verbose_link,
7542 .verbose_air = comp.verbose_air,
7543 .verbose_intern_pool = comp.verbose_intern_pool,
7544 .verbose_generic_instances = comp.verbose_generic_instances,
7545 .verbose_llvm_ir = comp.verbose_llvm_ir,
7546 .verbose_llvm_bc = comp.verbose_llvm_bc,
7547 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
7548 .clang_passthrough_mode = comp.clang_passthrough_mode,
7549 .skip_linker_dependencies = true,
7550 .environ_map = comp.environ_map,
7551 }) catch |err| switch (err) {
7552 error.CreateFail => {
7553 comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag });
7554 return error.AlreadyReported;
7555 },
7556 else => |e| return e,
7557 };
7558 defer sub_compilation.destroy();
7559
7560 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
7561
7562 const crt_file = try sub_compilation.toCrtFile();
7563 try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config);
7564
7565 {
7566 comp.mutex.lockUncancelable(io);
7567 defer comp.mutex.unlock(io);
7568 try comp.crt_files.ensureUnusedCapacity(gpa, 1);
7569 comp.crt_files.putAssumeCapacityNoClobber(basename, crt_file);
7570 }
7571}
7572
7573/// If `must_link` is set, then static library inputs will have all member objects linked into the
7574/// output, instead of only those required to resolve symbol references.
7575pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, must_link: bool, config: *const Compilation.Config) Io.Cancelable!void {
7576 try comp.queuePrelinkTasks(switch (config.output_mode) {
7577 .Exe => unreachable,
7578 .Obj => &.{.{ .load_object = path }},
7579 .Lib => &.{switch (config.link_mode) {
7580 .static => .{ .load_archive = .{ .path = path, .must_link = must_link } },
7581 .dynamic => .{ .load_dso = path },
7582 }},
7583 });
7584}
7585
7586/// Only valid to call during `update`.
7587pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) Io.Cancelable!void {
7588 if (tasks.len > 0) {
7589 if (comp.bin_file) |lf| assert(!lf.post_prelink);
7590 }
7591 comp.link_prog_node.increaseEstimatedTotalItems(tasks.len);
7592 try comp.link_queue.enqueuePrelink(comp, tasks);
7593}
7594
7595pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {
7596 return .{
7597 .full_object_path = .{
7598 .root_dir = comp.dirs.local_cache,
7599 .sub_path = try fs.path.join(comp.gpa, &.{
7600 "o",
7601 &Cache.binToHex(comp.digest.?),
7602 comp.emit_bin.?,
7603 }),
7604 },
7605 .lock = comp.cache_use.whole.moveLock(),
7606 };
7607}
7608
7609pub fn getCrtPaths(
7610 comp: *Compilation,
7611 arena: Allocator,
7612) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
7613 const target = &comp.root_mod.resolved_target.result;
7614 return getCrtPathsInner(arena, target, comp.config, comp.libc_installation, &comp.crt_files);
7615}
7616
7617fn getCrtPathsInner(
7618 arena: Allocator,
7619 target: *const std.Target,
7620 config: Config,
7621 libc_installation: ?*const LibCInstallation,
7622 crt_files: *std.StringHashMapUnmanaged(CrtFile),
7623) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
7624 const basenames = LibCInstallation.CrtBasenames.get(.{
7625 .target = target,
7626 .link_libc = config.link_libc,
7627 .output_mode = config.output_mode,
7628 .link_mode = config.link_mode,
7629 .pie = config.pie,
7630 });
7631 if (libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target);
7632
7633 return .{
7634 .crt0 = if (basenames.crt0) |basename| try crtFilePath(crt_files, basename) else null,
7635 .crti = if (basenames.crti) |basename| try crtFilePath(crt_files, basename) else null,
7636 .crtbegin = if (basenames.crtbegin) |basename| try crtFilePath(crt_files, basename) else null,
7637 .crtend = if (basenames.crtend) |basename| try crtFilePath(crt_files, basename) else null,
7638 .crtn = if (basenames.crtn) |basename| try crtFilePath(crt_files, basename) else null,
7639 };
7640}
7641
7642pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
7643 // Avoid deadlocking on building import libs such as kernel32.lib
7644 // This can happen when the user uses `build-exe foo.obj -lkernel32` and
7645 // then when we create a sub-Compilation for zig libc, it also tries to
7646 // build kernel32.lib.
7647 if (comp.skip_linker_dependencies) return;
7648 const target = &comp.root_mod.resolved_target.result;
7649 if (target.os.tag != .windows or target.ofmt == .c) return;
7650
7651 // This happens when an `extern "foo"` function is referenced.
7652 // If we haven't seen this library yet and we're targeting Windows, we need
7653 // to queue up a work item to produce the DLL import library for this.
7654 const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name);
7655 if (!gop.found_existing) {
7656 errdefer _ = comp.windows_libs.pop();
7657 gop.key_ptr.* = try comp.gpa.dupe(u8, lib_name);
7658 }
7659}
7660
7661/// This decides the optimization mode for all zig-provided libraries, including
7662/// compiler-rt, libcxx, libc, libunwind, etc.
7663pub fn compilerRtOptMode(comp: Compilation) std.lang.Optimize {
7664 if (comp.debug_compiler_runtime_libs) |mode| {
7665 return mode;
7666 }
7667 const target = &comp.root_mod.resolved_target.result;
7668 switch (comp.root_mod.optimize_mode) {
7669 .debug, .safe => return target_util.defaultCompilerRtOptimizeMode(target),
7670 .fast => return .fast,
7671 .small => return .small,
7672 }
7673}
7674
7675/// This decides whether to strip debug info for all zig-provided libraries, including
7676/// compiler-rt, libcxx, libc, libunwind, etc.
7677pub fn compilerRtStrip(comp: Compilation) bool {
7678 return comp.root_mod.strip;
7679}