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