blob 57504d97 (261344B) - Raw
1 const Compilation = @This(); 2 3 const std = @import("std"); 4 const builtin = @import("builtin"); 5 const mem = std.mem; 6 const Allocator = std.mem.Allocator; 7 const assert = std.debug.assert; 8 const log = std.log.scoped(.compilation); 9 const Target = std.Target; 10 const ThreadPool = std.Thread.Pool; 11 const WaitGroup = std.Thread.WaitGroup; 12 const ErrorBundle = std.zig.ErrorBundle; 13 14 const Value = @import("value.zig").Value; 15 const Type = @import("type.zig").Type; 16 const target_util = @import("target.zig"); 17 const Package = @import("Package.zig"); 18 const link = @import("link.zig"); 19 const tracy = @import("tracy.zig"); 20 const trace = tracy.trace; 21 const build_options = @import("build_options"); 22 const LibCInstallation = @import("libc_installation.zig").LibCInstallation; 23 const glibc = @import("glibc.zig"); 24 const musl = @import("musl.zig"); 25 const mingw = @import("mingw.zig"); 26 const libunwind = @import("libunwind.zig"); 27 const libcxx = @import("libcxx.zig"); 28 const wasi_libc = @import("wasi_libc.zig"); 29 const fatal = @import("main.zig").fatal; 30 const clangMain = @import("main.zig").clangMain; 31 const Module = @import("Module.zig"); 32 const InternPool = @import("InternPool.zig"); 33 const BuildId = std.Build.CompileStep.BuildId; 34 const Cache = std.Build.Cache; 35 const translate_c = @import("translate_c.zig"); 36 const clang = @import("clang.zig"); 37 const c_codegen = @import("codegen/c.zig"); 38 const libtsan = @import("libtsan.zig"); 39 const Zir = @import("Zir.zig"); 40 const Autodoc = @import("Autodoc.zig"); 41 const Color = @import("main.zig").Color; 42 const resinator = @import("resinator.zig"); 43 44 /// General-purpose allocator. Used for both temporary and long-term storage. 45 gpa: Allocator, 46 /// Arena-allocated memory used during initialization. Should be untouched until deinit. 47 arena_state: std.heap.ArenaAllocator.State, 48 bin_file: *link.File, 49 c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, 50 win32_resource_table: std.AutoArrayHashMapUnmanaged(*Win32Resource, void) = .{}, 51 /// This is a pointer to a local variable inside `update()`. 52 whole_cache_manifest: ?*Cache.Manifest = null, 53 whole_cache_manifest_mutex: std.Thread.Mutex = .{}, 54 55 link_error_flags: link.File.ErrorFlags = .{}, 56 lld_errors: std.ArrayListUnmanaged(LldError) = .{}, 57 58 work_queue: std.fifo.LinearFifo(Job, .Dynamic), 59 anon_work_queue: std.fifo.LinearFifo(Job, .Dynamic), 60 61 /// These jobs are to invoke the Clang compiler to create an object file, which 62 /// gets linked with the Compilation. 63 c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic), 64 65 /// These jobs are to invoke the RC compiler to create a compiled resource file (.res), which 66 /// gets linked with the Compilation. 67 win32_resource_work_queue: std.fifo.LinearFifo(*Win32Resource, .Dynamic), 68 69 /// These jobs are to tokenize, parse, and astgen files, which may be outdated 70 /// since the last compilation, as well as scan for `@import` and queue up 71 /// additional jobs corresponding to those new files. 72 astgen_work_queue: std.fifo.LinearFifo(*Module.File, .Dynamic), 73 /// These jobs are to inspect the file system stat() and if the embedded file has changed 74 /// on disk, mark the corresponding Decl outdated and queue up an `analyze_decl` 75 /// task for it. 76 embed_file_work_queue: std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic), 77 78 /// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator. 79 /// This data is accessed by multiple threads and is protected by `mutex`. 80 failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.ErrorMsg) = .{}, 81 82 /// The ErrorBundle memory is owned by the `Win32Resource`, using Compilation's general purpose allocator. 83 /// This data is accessed by multiple threads and is protected by `mutex`. 84 failed_win32_resources: std.AutoArrayHashMapUnmanaged(*Win32Resource, ErrorBundle) = .{}, 85 86 /// Miscellaneous things that can fail. 87 misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .{}, 88 89 keep_source_files_loaded: bool, 90 use_clang: bool, 91 sanitize_c: bool, 92 /// When this is `true` it means invoking clang as a sub-process is expected to inherit 93 /// stdin, stdout, stderr, and if it returns non success, to forward the exit code. 94 /// Otherwise we attempt to parse the error messages and expose them via the Compilation API. 95 /// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`. 96 clang_passthrough_mode: bool, 97 clang_preprocessor_mode: ClangPreprocessorMode, 98 /// Whether to print clang argvs to stdout. 99 verbose_cc: bool, 100 verbose_air: bool, 101 verbose_intern_pool: bool, 102 verbose_generic_instances: bool, 103 verbose_llvm_ir: ?[]const u8, 104 verbose_llvm_bc: ?[]const u8, 105 verbose_cimport: bool, 106 verbose_llvm_cpu_features: bool, 107 disable_c_depfile: bool, 108 time_report: bool, 109 stack_report: bool, 110 unwind_tables: bool, 111 test_evented_io: bool, 112 debug_compiler_runtime_libs: bool, 113 debug_compile_errors: bool, 114 job_queued_compiler_rt_lib: bool = false, 115 job_queued_compiler_rt_obj: bool = false, 116 alloc_failure_occurred: bool = false, 117 formatted_panics: bool = false, 118 last_update_was_cache_hit: bool = false, 119 120 c_source_files: []const CSourceFile, 121 clang_argv: []const []const u8, 122 rc_source_files: []const RcSourceFile, 123 cache_parent: *Cache, 124 /// Path to own executable for invoking `zig clang`. 125 self_exe_path: ?[]const u8, 126 /// null means -fno-emit-bin. 127 /// This is mutable memory allocated into the Compilation-lifetime arena (`arena_state`) 128 /// of exactly the correct size for "o/[digest]/[basename]". 129 /// The basename is of the outputted binary file in case we don't know the directory yet. 130 whole_bin_sub_path: ?[]u8, 131 /// Same as `whole_bin_sub_path` but for implibs. 132 whole_implib_sub_path: ?[]u8, 133 whole_docs_sub_path: ?[]u8, 134 zig_lib_directory: Directory, 135 local_cache_directory: Directory, 136 global_cache_directory: Directory, 137 libc_include_dir_list: []const []const u8, 138 libc_framework_dir_list: []const []const u8, 139 rc_include_dir_list: []const []const u8, 140 thread_pool: *ThreadPool, 141 142 /// Populated when we build the libc++ static library. A Job to build this is placed in the queue 143 /// and resolved before calling linker.flush(). 144 libcxx_static_lib: ?CRTFile = null, 145 /// Populated when we build the libc++abi static library. A Job to build this is placed in the queue 146 /// and resolved before calling linker.flush(). 147 libcxxabi_static_lib: ?CRTFile = null, 148 /// Populated when we build the libunwind static library. A Job to build this is placed in the queue 149 /// and resolved before calling linker.flush(). 150 libunwind_static_lib: ?CRTFile = null, 151 /// Populated when we build the TSAN static library. A Job to build this is placed in the queue 152 /// and resolved before calling linker.flush(). 153 tsan_static_lib: ?CRTFile = null, 154 /// Populated when we build the libssp static library. A Job to build this is placed in the queue 155 /// and resolved before calling linker.flush(). 156 libssp_static_lib: ?CRTFile = null, 157 /// Populated when we build the libc static library. A Job to build this is placed in the queue 158 /// and resolved before calling linker.flush(). 159 libc_static_lib: ?CRTFile = null, 160 /// Populated when we build the libcompiler_rt static library. A Job to build this is indicated 161 /// by setting `job_queued_compiler_rt_lib` and resolved before calling linker.flush(). 162 compiler_rt_lib: ?CRTFile = null, 163 /// Populated when we build the compiler_rt_obj object. A Job to build this is indicated 164 /// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush(). 165 compiler_rt_obj: ?CRTFile = null, 166 167 glibc_so_files: ?glibc.BuiltSharedObjects = null, 168 169 /// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source, 170 /// The set of needed CRT (C runtime) files differs depending on the target and compilation settings. 171 /// The key is the basename, and the value is the absolute path to the completed build artifact. 172 crt_files: std.StringHashMapUnmanaged(CRTFile) = .{}, 173 174 /// Keeping track of this possibly open resource so we can close it later. 175 owned_link_dir: ?std.fs.Dir, 176 177 /// This is for stage1 and should be deleted upon completion of self-hosting. 178 /// Don't use this for anything other than stage1 compatibility. 179 color: Color = .auto, 180 181 /// How many lines of reference trace should be included per compile error. 182 /// Null means only show snippet on first error. 183 reference_trace: ?u32 = null, 184 185 libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default, 186 187 /// This mutex guards all `Compilation` mutable state. 188 mutex: std.Thread.Mutex = .{}, 189 190 test_filter: ?[]const u8, 191 test_name_prefix: ?[]const u8, 192 193 emit_asm: ?EmitLoc, 194 emit_llvm_ir: ?EmitLoc, 195 emit_llvm_bc: ?EmitLoc, 196 197 work_queue_wait_group: WaitGroup = .{}, 198 astgen_wait_group: WaitGroup = .{}, 199 200 pub const default_stack_protector_buffer_size = 4; 201 pub const SemaError = Module.SemaError; 202 203 pub const CRTFile = struct { 204 lock: Cache.Lock, 205 full_object_path: []const u8, 206 207 pub fn deinit(self: *CRTFile, gpa: Allocator) void { 208 self.lock.release(); 209 gpa.free(self.full_object_path); 210 self.* = undefined; 211 } 212 }; 213 214 // supported languages for "zig clang -x <lang>". 215 // Loosely based on llvm-project/clang/include/clang/Driver/Types.def 216 pub const LangToExt = std.ComptimeStringMap(FileExt, .{ 217 .{ "c", .c }, 218 .{ "c-header", .h }, 219 .{ "c++", .cpp }, 220 .{ "c++-header", .h }, 221 .{ "objective-c", .m }, 222 .{ "objective-c-header", .h }, 223 .{ "objective-c++", .mm }, 224 .{ "objective-c++-header", .h }, 225 .{ "assembler", .assembly }, 226 .{ "assembler-with-cpp", .assembly_with_cpp }, 227 .{ "cuda", .cu }, 228 }); 229 230 /// For passing to a C compiler. 231 pub const CSourceFile = struct { 232 src_path: []const u8, 233 extra_flags: []const []const u8 = &.{}, 234 /// Same as extra_flags except they are not added to the Cache hash. 235 cache_exempt_flags: []const []const u8 = &.{}, 236 // this field is non-null iff language was explicitly set with "-x lang". 237 ext: ?FileExt = null, 238 }; 239 240 /// For passing to resinator. 241 pub const RcSourceFile = struct { 242 src_path: []const u8, 243 extra_flags: []const []const u8 = &.{}, 244 }; 245 246 pub const RcIncludes = enum { 247 /// Use MSVC if available, fall back to MinGW. 248 any, 249 /// Use MSVC include paths (MSVC install + Windows SDK, must be present on the system). 250 msvc, 251 /// Use MinGW include paths (distributed with Zig). 252 gnu, 253 /// Do not use any autodetected include paths. 254 none, 255 }; 256 257 const Job = union(enum) { 258 /// Write the constant value for a Decl to the output file. 259 codegen_decl: Module.Decl.Index, 260 /// Write the machine code for a function to the output file. 261 /// This will either be a non-generic `func_decl` or a `func_instance`. 262 codegen_func: InternPool.Index, 263 /// Render the .h file snippet for the Decl. 264 emit_h_decl: Module.Decl.Index, 265 /// The Decl needs to be analyzed and possibly export itself. 266 /// It may have already be analyzed, or it may have been determined 267 /// to be outdated; in this case perform semantic analysis again. 268 analyze_decl: Module.Decl.Index, 269 /// The file that was loaded with `@embedFile` has changed on disk 270 /// and has been re-loaded into memory. All Decls that depend on it 271 /// need to be re-analyzed. 272 update_embed_file: *Module.EmbedFile, 273 /// The source file containing the Decl has been updated, and so the 274 /// Decl may need its line number information updated in the debug info. 275 update_line_number: Module.Decl.Index, 276 /// The main source file for the package needs to be analyzed. 277 analyze_pkg: *Package, 278 279 /// one of the glibc static objects 280 glibc_crt_file: glibc.CRTFile, 281 /// all of the glibc shared objects 282 glibc_shared_objects, 283 /// one of the musl static objects 284 musl_crt_file: musl.CRTFile, 285 /// one of the mingw-w64 static objects 286 mingw_crt_file: mingw.CRTFile, 287 /// libunwind.a, usually needed when linking libc 288 libunwind: void, 289 libcxx: void, 290 libcxxabi: void, 291 libtsan: void, 292 libssp: void, 293 /// needed when not linking libc and using LLVM for code generation because it generates 294 /// calls to, for example, memcpy and memset. 295 zig_libc: void, 296 /// one of WASI libc static objects 297 wasi_libc_crt_file: wasi_libc.CRTFile, 298 299 /// The value is the index into `link.File.Options.system_libs`. 300 windows_import_lib: usize, 301 }; 302 303 pub const CObject = struct { 304 /// Relative to cwd. Owned by arena. 305 src: CSourceFile, 306 status: union(enum) { 307 new, 308 success: struct { 309 /// The outputted result. Owned by gpa. 310 object_path: []u8, 311 /// This is a file system lock on the cache hash manifest representing this 312 /// object. It prevents other invocations of the Zig compiler from interfering 313 /// with this object until released. 314 lock: Cache.Lock, 315 }, 316 /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects. 317 failure, 318 /// A transient failure happened when trying to compile the C Object; it may 319 /// succeed if we try again. There may be a corresponding ErrorMsg in 320 /// Compilation.failed_c_objects. If there is not, the failure is out of memory. 321 failure_retryable, 322 }, 323 324 pub const ErrorMsg = struct { 325 msg: []const u8, 326 line: u32, 327 column: u32, 328 329 pub fn destroy(em: *ErrorMsg, gpa: Allocator) void { 330 gpa.free(em.msg); 331 gpa.destroy(em); 332 } 333 }; 334 335 /// Returns if there was failure. 336 pub fn clearStatus(self: *CObject, gpa: Allocator) bool { 337 switch (self.status) { 338 .new => return false, 339 .failure, .failure_retryable => { 340 self.status = .new; 341 return true; 342 }, 343 .success => |*success| { 344 gpa.free(success.object_path); 345 success.lock.release(); 346 self.status = .new; 347 return false; 348 }, 349 } 350 } 351 352 pub fn destroy(self: *CObject, gpa: Allocator) void { 353 _ = self.clearStatus(gpa); 354 gpa.destroy(self); 355 } 356 }; 357 358 pub const Win32Resource = struct { 359 /// Relative to cwd. Owned by arena. 360 src: RcSourceFile, 361 status: union(enum) { 362 new, 363 success: struct { 364 /// The outputted result. Owned by gpa. 365 res_path: []u8, 366 /// This is a file system lock on the cache hash manifest representing this 367 /// object. It prevents other invocations of the Zig compiler from interfering 368 /// with this object until released. 369 lock: Cache.Lock, 370 }, 371 /// There will be a corresponding ErrorMsg in Compilation.failed_win32_resources. 372 failure, 373 /// A transient failure happened when trying to compile the resource file; it may 374 /// succeed if we try again. There may be a corresponding ErrorMsg in 375 /// Compilation.failed_win32_resources. If there is not, the failure is out of memory. 376 failure_retryable, 377 }, 378 379 /// Returns true if there was failure. 380 pub fn clearStatus(self: *Win32Resource, gpa: Allocator) bool { 381 switch (self.status) { 382 .new => return false, 383 .failure, .failure_retryable => { 384 self.status = .new; 385 return true; 386 }, 387 .success => |*success| { 388 gpa.free(success.res_path); 389 success.lock.release(); 390 self.status = .new; 391 return false; 392 }, 393 } 394 } 395 396 pub fn destroy(self: *Win32Resource, gpa: Allocator) void { 397 _ = self.clearStatus(gpa); 398 gpa.destroy(self); 399 } 400 }; 401 402 pub const MiscTask = enum { 403 write_builtin_zig, 404 glibc_crt_file, 405 glibc_shared_objects, 406 musl_crt_file, 407 mingw_crt_file, 408 windows_import_lib, 409 libunwind, 410 libcxx, 411 libcxxabi, 412 libtsan, 413 wasi_libc_crt_file, 414 compiler_rt, 415 libssp, 416 zig_libc, 417 analyze_pkg, 418 419 @"musl crti.o", 420 @"musl crtn.o", 421 @"musl crt1.o", 422 @"musl rcrt1.o", 423 @"musl Scrt1.o", 424 @"musl libc.a", 425 @"musl libc.so", 426 427 @"wasi crt1-reactor.o", 428 @"wasi crt1-command.o", 429 @"wasi libc.a", 430 @"libwasi-emulated-process-clocks.a", 431 @"libwasi-emulated-getpid.a", 432 @"libwasi-emulated-mman.a", 433 @"libwasi-emulated-signal.a", 434 435 @"glibc crti.o", 436 @"glibc crtn.o", 437 @"glibc Scrt1.o", 438 @"glibc libc_nonshared.a", 439 @"glibc shared object", 440 441 @"mingw-w64 crt2.o", 442 @"mingw-w64 dllcrt2.o", 443 @"mingw-w64 mingw32.lib", 444 @"mingw-w64 msvcrt-os.lib", 445 @"mingw-w64 mingwex.lib", 446 @"mingw-w64 uuid.lib", 447 }; 448 449 pub const MiscError = struct { 450 /// Allocated with gpa. 451 msg: []u8, 452 children: ?ErrorBundle = null, 453 454 pub fn deinit(misc_err: *MiscError, gpa: Allocator) void { 455 gpa.free(misc_err.msg); 456 if (misc_err.children) |*children| { 457 children.deinit(gpa); 458 } 459 misc_err.* = undefined; 460 } 461 }; 462 463 pub const LldError = struct { 464 /// Allocated with gpa. 465 msg: []const u8, 466 context_lines: []const []const u8 = &.{}, 467 468 pub fn deinit(self: *LldError, gpa: Allocator) void { 469 for (self.context_lines) |line| { 470 gpa.free(line); 471 } 472 473 gpa.free(self.context_lines); 474 gpa.free(self.msg); 475 } 476 }; 477 478 pub const Directory = Cache.Directory; 479 480 pub const EmitLoc = struct { 481 /// If this is `null` it means the file will be output to the cache directory. 482 /// When provided, both the open file handle and the path name must outlive the `Compilation`. 483 directory: ?Compilation.Directory, 484 /// This may not have sub-directories in it. 485 basename: []const u8, 486 }; 487 488 pub const cache_helpers = struct { 489 pub fn addEmitLoc(hh: *Cache.HashHelper, emit_loc: EmitLoc) void { 490 hh.addBytes(emit_loc.basename); 491 } 492 493 pub fn addOptionalEmitLoc(hh: *Cache.HashHelper, optional_emit_loc: ?EmitLoc) void { 494 hh.add(optional_emit_loc != null); 495 addEmitLoc(hh, optional_emit_loc orelse return); 496 } 497 498 pub fn hashCSource(self: *Cache.Manifest, c_source: Compilation.CSourceFile) !void { 499 _ = try self.addFile(c_source.src_path, null); 500 // Hash the extra flags, with special care to call addFile for file parameters. 501 // TODO this logic can likely be improved by utilizing clang_options_data.zig. 502 const file_args = [_][]const u8{"-include"}; 503 var arg_i: usize = 0; 504 while (arg_i < c_source.extra_flags.len) : (arg_i += 1) { 505 const arg = c_source.extra_flags[arg_i]; 506 self.hash.addBytes(arg); 507 for (file_args) |file_arg| { 508 if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) { 509 arg_i += 1; 510 _ = try self.addFile(c_source.extra_flags[arg_i], null); 511 } 512 } 513 } 514 } 515 }; 516 517 pub const ClangPreprocessorMode = enum { 518 no, 519 /// This means we are doing `zig cc -E -o <path>`. 520 yes, 521 /// This means we are doing `zig cc -E`. 522 stdout, 523 }; 524 525 pub const Framework = link.Framework; 526 pub const SystemLib = link.SystemLib; 527 pub const CacheMode = link.CacheMode; 528 529 pub const LinkObject = struct { 530 path: []const u8, 531 must_link: bool = false, 532 // When the library is passed via a positional argument, it will be 533 // added as a full path. If it's `-l<lib>`, then just the basename. 534 // 535 // Consistent with `withLOption` variable name in lld ELF driver. 536 loption: bool = false, 537 }; 538 539 pub const InitOptions = struct { 540 zig_lib_directory: Directory, 541 local_cache_directory: Directory, 542 global_cache_directory: Directory, 543 target: Target, 544 root_name: []const u8, 545 main_pkg: ?*Package, 546 output_mode: std.builtin.OutputMode, 547 thread_pool: *ThreadPool, 548 dynamic_linker: ?[]const u8 = null, 549 sysroot: ?[]const u8 = null, 550 /// `null` means to not emit a binary file. 551 emit_bin: ?EmitLoc, 552 /// `null` means to not emit a C header file. 553 emit_h: ?EmitLoc = null, 554 /// `null` means to not emit assembly. 555 emit_asm: ?EmitLoc = null, 556 /// `null` means to not emit LLVM IR. 557 emit_llvm_ir: ?EmitLoc = null, 558 /// `null` means to not emit LLVM module bitcode. 559 emit_llvm_bc: ?EmitLoc = null, 560 /// `null` means to not emit docs. 561 emit_docs: ?EmitLoc = null, 562 /// `null` means to not emit an import lib. 563 emit_implib: ?EmitLoc = null, 564 link_mode: ?std.builtin.LinkMode = null, 565 dll_export_fns: ?bool = false, 566 /// Normally when using LLD to link, Zig uses a file named "lld.id" in the 567 /// same directory as the output binary which contains the hash of the link 568 /// operation, allowing Zig to skip linking when the hash would be unchanged. 569 /// In the case that the output binary is being emitted into a directory which 570 /// is externally modified - essentially anything other than zig-cache - then 571 /// this flag would be set to disable this machinery to avoid false positives. 572 disable_lld_caching: bool = false, 573 cache_mode: CacheMode = .incremental, 574 optimize_mode: std.builtin.OptimizeMode = .Debug, 575 keep_source_files_loaded: bool = false, 576 clang_argv: []const []const u8 = &[0][]const u8{}, 577 lib_dirs: []const []const u8 = &[0][]const u8{}, 578 rpath_list: []const []const u8 = &[0][]const u8{}, 579 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{}, 580 c_source_files: []const CSourceFile = &[0]CSourceFile{}, 581 rc_source_files: []const RcSourceFile = &[0]RcSourceFile{}, 582 rc_includes: RcIncludes = .any, 583 link_objects: []LinkObject = &[0]LinkObject{}, 584 framework_dirs: []const []const u8 = &[0][]const u8{}, 585 frameworks: []const Framework = &.{}, 586 system_lib_names: []const []const u8 = &.{}, 587 system_lib_infos: []const SystemLib = &.{}, 588 /// These correspond to the WASI libc emulated subcomponents including: 589 /// * process clocks 590 /// * getpid 591 /// * mman 592 /// * signal 593 wasi_emulated_libs: []const wasi_libc.CRTFile = &[0]wasi_libc.CRTFile{}, 594 link_libc: bool = false, 595 link_libcpp: bool = false, 596 link_libunwind: bool = false, 597 want_pic: ?bool = null, 598 /// This means that if the output mode is an executable it will be a 599 /// Position Independent Executable. If the output mode is not an 600 /// executable this field is ignored. 601 want_pie: ?bool = null, 602 want_sanitize_c: ?bool = null, 603 want_stack_check: ?bool = null, 604 /// null means default. 605 /// 0 means no stack protector. 606 /// other number means stack protection with that buffer size. 607 want_stack_protector: ?u32 = null, 608 want_red_zone: ?bool = null, 609 omit_frame_pointer: ?bool = null, 610 want_valgrind: ?bool = null, 611 want_tsan: ?bool = null, 612 want_compiler_rt: ?bool = null, 613 want_lto: ?bool = null, 614 want_unwind_tables: ?bool = null, 615 use_llvm: ?bool = null, 616 use_lib_llvm: ?bool = null, 617 use_lld: ?bool = null, 618 use_clang: ?bool = null, 619 single_threaded: ?bool = null, 620 strip: ?bool = null, 621 formatted_panics: ?bool = null, 622 rdynamic: bool = false, 623 function_sections: bool = false, 624 no_builtin: bool = false, 625 is_native_os: bool, 626 is_native_abi: bool, 627 time_report: bool = false, 628 stack_report: bool = false, 629 link_eh_frame_hdr: bool = false, 630 link_emit_relocs: bool = false, 631 linker_script: ?[]const u8 = null, 632 version_script: ?[]const u8 = null, 633 soname: ?[]const u8 = null, 634 linker_gc_sections: ?bool = null, 635 linker_allow_shlib_undefined: ?bool = null, 636 linker_bind_global_refs_locally: ?bool = null, 637 linker_import_memory: ?bool = null, 638 linker_export_memory: ?bool = null, 639 linker_import_symbols: bool = false, 640 linker_import_table: bool = false, 641 linker_export_table: bool = false, 642 linker_initial_memory: ?u64 = null, 643 linker_max_memory: ?u64 = null, 644 linker_shared_memory: bool = false, 645 linker_global_base: ?u64 = null, 646 linker_export_symbol_names: []const []const u8 = &.{}, 647 linker_print_gc_sections: bool = false, 648 linker_print_icf_sections: bool = false, 649 linker_print_map: bool = false, 650 linker_opt_bisect_limit: i32 = -1, 651 each_lib_rpath: ?bool = null, 652 build_id: ?BuildId = null, 653 disable_c_depfile: bool = false, 654 linker_z_nodelete: bool = false, 655 linker_z_notext: bool = false, 656 linker_z_defs: bool = false, 657 linker_z_origin: bool = false, 658 linker_z_now: bool = true, 659 linker_z_relro: bool = true, 660 linker_z_nocopyreloc: bool = false, 661 linker_z_common_page_size: ?u64 = null, 662 linker_z_max_page_size: ?u64 = null, 663 linker_tsaware: bool = false, 664 linker_nxcompat: bool = false, 665 linker_dynamicbase: bool = true, 666 linker_optimization: ?u8 = null, 667 linker_compress_debug_sections: ?link.CompressDebugSections = null, 668 linker_module_definition_file: ?[]const u8 = null, 669 linker_sort_section: ?link.SortSection = null, 670 major_subsystem_version: ?u32 = null, 671 minor_subsystem_version: ?u32 = null, 672 clang_passthrough_mode: bool = false, 673 verbose_cc: bool = false, 674 verbose_link: bool = false, 675 verbose_air: bool = false, 676 verbose_intern_pool: bool = false, 677 verbose_generic_instances: bool = false, 678 verbose_llvm_ir: ?[]const u8 = null, 679 verbose_llvm_bc: ?[]const u8 = null, 680 verbose_cimport: bool = false, 681 verbose_llvm_cpu_features: bool = false, 682 is_test: bool = false, 683 test_evented_io: bool = false, 684 debug_compiler_runtime_libs: bool = false, 685 debug_compile_errors: bool = false, 686 /// Normally when you create a `Compilation`, Zig will automatically build 687 /// and link in required dependencies, such as compiler-rt and libc. When 688 /// building such dependencies themselves, this flag must be set to avoid 689 /// infinite recursion. 690 skip_linker_dependencies: bool = false, 691 parent_compilation_link_libc: bool = false, 692 hash_style: link.HashStyle = .both, 693 entry: ?[]const u8 = null, 694 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{}, 695 stack_size_override: ?u64 = null, 696 image_base_override: ?u64 = null, 697 self_exe_path: ?[]const u8 = null, 698 version: ?std.SemanticVersion = null, 699 compatibility_version: ?std.SemanticVersion = null, 700 libc_installation: ?*const LibCInstallation = null, 701 machine_code_model: std.builtin.CodeModel = .default, 702 clang_preprocessor_mode: ClangPreprocessorMode = .no, 703 /// This is for stage1 and should be deleted upon completion of self-hosting. 704 color: Color = .auto, 705 reference_trace: ?u32 = null, 706 error_tracing: ?bool = null, 707 test_filter: ?[]const u8 = null, 708 test_name_prefix: ?[]const u8 = null, 709 test_runner_path: ?[]const u8 = null, 710 subsystem: ?std.Target.SubSystem = null, 711 dwarf_format: ?std.dwarf.Format = null, 712 /// WASI-only. Type of WASI execution model ("command" or "reactor"). 713 wasi_exec_model: ?std.builtin.WasiExecModel = null, 714 /// (Zig compiler development) Enable dumping linker's state as JSON. 715 enable_link_snapshots: bool = false, 716 /// (Darwin) Install name of the dylib 717 install_name: ?[]const u8 = null, 718 /// (Darwin) Path to entitlements file 719 entitlements: ?[]const u8 = null, 720 /// (Darwin) size of the __PAGEZERO segment 721 pagezero_size: ?u64 = null, 722 /// (Darwin) set minimum space for future expansion of the load commands 723 headerpad_size: ?u32 = null, 724 /// (Darwin) set enough space as if all paths were MATPATHLEN 725 headerpad_max_install_names: bool = false, 726 /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols 727 dead_strip_dylibs: bool = false, 728 libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default, 729 /// (Windows) PDB source path prefix to instruct the linker how to resolve relative 730 /// paths when consolidating CodeView streams into a single PDB file. 731 pdb_source_path: ?[]const u8 = null, 732 /// (Windows) PDB output path 733 pdb_out_path: ?[]const u8 = null, 734 }; 735 736 fn addPackageTableToCacheHash( 737 hash: *Cache.HashHelper, 738 arena: *std.heap.ArenaAllocator, 739 pkg_table: Package.Table, 740 seen_table: *std.AutoHashMap(*Package, void), 741 hash_type: union(enum) { path_bytes, files: *Cache.Manifest }, 742 ) (error{OutOfMemory} || std.os.GetCwdError)!void { 743 const allocator = arena.allocator(); 744 745 const packages = try allocator.alloc(Package.Table.KV, pkg_table.count()); 746 { 747 // Copy over the hashmap entries to our slice 748 var table_it = pkg_table.iterator(); 749 var idx: usize = 0; 750 while (table_it.next()) |entry| : (idx += 1) { 751 packages[idx] = .{ 752 .key = entry.key_ptr.*, 753 .value = entry.value_ptr.*, 754 }; 755 } 756 } 757 // Sort the slice by package name 758 mem.sort(Package.Table.KV, packages, {}, struct { 759 fn lessThan(_: void, lhs: Package.Table.KV, rhs: Package.Table.KV) bool { 760 return std.mem.lessThan(u8, lhs.key, rhs.key); 761 } 762 }.lessThan); 763 764 for (packages) |pkg| { 765 if ((try seen_table.getOrPut(pkg.value)).found_existing) continue; 766 767 // Finally insert the package name and path to the cache hash. 768 hash.addBytes(pkg.key); 769 switch (hash_type) { 770 .path_bytes => { 771 hash.addBytes(pkg.value.root_src_path); 772 hash.addOptionalBytes(pkg.value.root_src_directory.path); 773 }, 774 .files => |man| { 775 const pkg_zig_file = try pkg.value.root_src_directory.join(allocator, &[_][]const u8{ 776 pkg.value.root_src_path, 777 }); 778 _ = try man.addFile(pkg_zig_file, null); 779 }, 780 } 781 // Recurse to handle the package's dependencies 782 try addPackageTableToCacheHash(hash, arena, pkg.value.table, seen_table, hash_type); 783 } 784 } 785 786 pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { 787 const is_dyn_lib = switch (options.output_mode) { 788 .Obj, .Exe => false, 789 .Lib => (options.link_mode orelse .Static) == .Dynamic, 790 }; 791 const is_exe_or_dyn_lib = switch (options.output_mode) { 792 .Obj => false, 793 .Lib => is_dyn_lib, 794 .Exe => true, 795 }; 796 797 const needs_c_symbols = !options.skip_linker_dependencies and is_exe_or_dyn_lib; 798 799 // WASI-only. Resolve the optional exec-model option, defaults to command. 800 const wasi_exec_model = if (options.target.os.tag != .wasi) undefined else options.wasi_exec_model orelse .command; 801 802 if (options.linker_export_table and options.linker_import_table) { 803 return error.ExportTableAndImportTableConflict; 804 } 805 806 // The `have_llvm` condition is here only because native backends cannot yet build compiler-rt. 807 // Once they are capable this condition could be removed. When removing this condition, 808 // also test the use case of `build-obj -fcompiler-rt` with the native backends 809 // and make sure the compiler-rt symbols are emitted. 810 const is_p9 = options.target.os.tag == .plan9; 811 const is_spv = options.target.cpu.arch.isSpirV(); 812 const capable_of_building_compiler_rt = build_options.have_llvm and !is_p9 and !is_spv; 813 const capable_of_building_zig_libc = build_options.have_llvm and !is_p9 and !is_spv; 814 const capable_of_building_ssp = build_options.have_llvm and !is_p9 and !is_spv; 815 816 const comp: *Compilation = comp: { 817 // For allocations that have the same lifetime as Compilation. This arena is used only during this 818 // initialization and then is freed in deinit(). 819 var arena_allocator = std.heap.ArenaAllocator.init(gpa); 820 errdefer arena_allocator.deinit(); 821 const arena = arena_allocator.allocator(); 822 823 // We put the `Compilation` itself in the arena. Freeing the arena will free the module. 824 // It's initialized later after we prepare the initialization options. 825 const comp = try arena.create(Compilation); 826 const root_name = try arena.dupeZ(u8, options.root_name); 827 828 // Make a decision on whether to use LLVM or our own backend. 829 const use_lib_llvm = options.use_lib_llvm orelse build_options.have_llvm; 830 const use_llvm = blk: { 831 if (options.use_llvm) |explicit| 832 break :blk explicit; 833 834 // If emitting to LLVM bitcode object format, must use LLVM backend. 835 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) 836 break :blk true; 837 838 // If we have no zig code to compile, no need for LLVM. 839 if (options.main_pkg == null) 840 break :blk false; 841 842 // If LLVM does not support the target, then we can't use it. 843 if (!target_util.hasLlvmSupport(options.target, options.target.ofmt)) 844 break :blk false; 845 846 // Prefer LLVM for release builds. 847 if (options.optimize_mode != .Debug) 848 break :blk true; 849 850 // At this point we would prefer to use our own self-hosted backend, 851 // because the compilation speed is better than LLVM. But only do it if 852 // we are confident in the robustness of the backend. 853 break :blk !target_util.selfHostedBackendIsAsRobustAsLlvm(options.target); 854 }; 855 if (!use_llvm) { 856 if (options.use_llvm == true) { 857 return error.ZigCompilerNotBuiltWithLLVMExtensions; 858 } 859 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) { 860 return error.EmittingLlvmModuleRequiresUsingLlvmBackend; 861 } 862 } 863 864 // TODO: once we support incremental compilation for the LLVM backend via 865 // saving the LLVM module into a bitcode file and restoring it, along with 866 // compiler state, the second clause here can be removed so that incremental 867 // cache mode is used for LLVM backend too. We need some fuzz testing before 868 // that can be enabled. 869 const cache_mode = if ((use_llvm or options.main_pkg == null) and !options.disable_lld_caching) 870 CacheMode.whole 871 else 872 options.cache_mode; 873 874 const tsan = options.want_tsan orelse false; 875 // TSAN is implemented in C++ so it requires linking libc++. 876 const link_libcpp = options.link_libcpp or tsan; 877 const link_libc = link_libcpp or options.link_libc or options.link_libunwind or 878 target_util.osRequiresLibC(options.target); 879 880 const link_libunwind = options.link_libunwind or 881 (link_libcpp and target_util.libcNeedsLibUnwind(options.target)); 882 const unwind_tables = options.want_unwind_tables orelse 883 (link_libunwind or target_util.needUnwindTables(options.target)); 884 const link_eh_frame_hdr = options.link_eh_frame_hdr or unwind_tables; 885 const build_id = options.build_id orelse .none; 886 887 // Make a decision on whether to use LLD or our own linker. 888 const use_lld = options.use_lld orelse blk: { 889 if (options.target.isDarwin()) { 890 break :blk false; 891 } 892 893 if (!build_options.have_llvm) 894 break :blk false; 895 896 if (options.target.ofmt == .c) 897 break :blk false; 898 899 if (options.want_lto) |lto| { 900 if (lto) { 901 break :blk true; 902 } 903 } 904 905 // Our linker can't handle objects or most advanced options yet. 906 if (options.link_objects.len != 0 or 907 options.c_source_files.len != 0 or 908 options.frameworks.len != 0 or 909 options.system_lib_names.len != 0 or 910 options.link_libc or options.link_libcpp or 911 link_eh_frame_hdr or 912 options.link_emit_relocs or 913 options.output_mode == .Lib or 914 options.linker_script != null or options.version_script != null or 915 options.emit_implib != null or 916 build_id != .none or 917 options.symbol_wrap_set.count() > 0) 918 { 919 break :blk true; 920 } 921 922 if (use_llvm) { 923 // If stage1 generates an object file, self-hosted linker is not 924 // yet sophisticated enough to handle that. 925 break :blk options.main_pkg != null; 926 } 927 928 break :blk false; 929 }; 930 931 const lto = blk: { 932 if (options.want_lto) |explicit| { 933 if (!use_lld and !options.target.isDarwin()) 934 return error.LtoUnavailableWithoutLld; 935 break :blk explicit; 936 } else if (!use_lld) { 937 // TODO zig ld LTO support 938 // See https://github.com/ziglang/zig/issues/8680 939 break :blk false; 940 } else if (options.c_source_files.len == 0) { 941 break :blk false; 942 } else if (options.target.cpu.arch.isRISCV()) { 943 // Clang and LLVM currently don't support RISC-V target-abi for LTO. 944 // Compiling with LTO may fail or produce undesired results. 945 // See https://reviews.llvm.org/D71387 946 // See https://reviews.llvm.org/D102582 947 break :blk false; 948 } else switch (options.output_mode) { 949 .Lib, .Obj => break :blk false, 950 .Exe => switch (options.optimize_mode) { 951 .Debug => break :blk false, 952 .ReleaseSafe, .ReleaseFast, .ReleaseSmall => break :blk true, 953 }, 954 } 955 }; 956 957 const must_dynamic_link = dl: { 958 if (target_util.cannotDynamicLink(options.target)) 959 break :dl false; 960 if (is_exe_or_dyn_lib and link_libc and 961 (options.target.isGnuLibC() or target_util.osRequiresLibC(options.target))) 962 { 963 break :dl true; 964 } 965 const any_dyn_libs: bool = x: { 966 if (options.system_lib_names.len != 0) 967 break :x true; 968 for (options.link_objects) |obj| { 969 switch (classifyFileExt(obj.path)) { 970 .shared_library => break :x true, 971 else => continue, 972 } 973 } 974 break :x false; 975 }; 976 if (any_dyn_libs) { 977 // When creating a executable that links to system libraries, 978 // we require dynamic linking, but we must not link static libraries 979 // or object files dynamically! 980 break :dl (options.output_mode == .Exe); 981 } 982 983 break :dl false; 984 }; 985 const default_link_mode: std.builtin.LinkMode = blk: { 986 if (must_dynamic_link) { 987 break :blk .Dynamic; 988 } else if (is_exe_or_dyn_lib and link_libc and 989 options.is_native_abi and options.target.abi.isMusl()) 990 { 991 // If targeting the system's native ABI and the system's 992 // libc is musl, link dynamically by default. 993 break :blk .Dynamic; 994 } else { 995 break :blk .Static; 996 } 997 }; 998 const link_mode: std.builtin.LinkMode = if (options.link_mode) |lm| blk: { 999 if (lm == .Static and must_dynamic_link) { 1000 return error.UnableToStaticLink; 1001 } 1002 break :blk lm; 1003 } else default_link_mode; 1004 1005 const dll_export_fns = options.dll_export_fns orelse (is_dyn_lib or options.rdynamic); 1006 1007 const libc_dirs = try detectLibCIncludeDirs( 1008 arena, 1009 options.zig_lib_directory.path.?, 1010 options.target, 1011 options.is_native_abi, 1012 link_libc, 1013 options.libc_installation, 1014 ); 1015 1016 const rc_dirs = try detectWin32ResourceIncludeDirs( 1017 arena, 1018 options, 1019 ); 1020 1021 const sysroot = options.sysroot orelse libc_dirs.sysroot; 1022 1023 const must_pie = target_util.requiresPIE(options.target); 1024 const pie: bool = if (options.want_pie) |explicit| pie: { 1025 if (!explicit and must_pie) { 1026 return error.TargetRequiresPIE; 1027 } 1028 break :pie explicit; 1029 } else must_pie or tsan; 1030 1031 const must_pic: bool = b: { 1032 if (target_util.requiresPIC(options.target, link_libc)) 1033 break :b true; 1034 break :b link_mode == .Dynamic; 1035 }; 1036 const pic = if (options.want_pic) |explicit| pic: { 1037 if (!explicit) { 1038 if (must_pic) { 1039 return error.TargetRequiresPIC; 1040 } 1041 if (pie) { 1042 return error.PIERequiresPIC; 1043 } 1044 } 1045 break :pic explicit; 1046 } else pie or must_pic; 1047 1048 // Make a decision on whether to use Clang for translate-c and compiling C files. 1049 const use_clang = if (options.use_clang) |explicit| explicit else blk: { 1050 if (build_options.have_llvm) { 1051 // Can't use it if we don't have it! 1052 break :blk false; 1053 } 1054 // It's not planned to do our own translate-c or C compilation. 1055 break :blk true; 1056 }; 1057 1058 const is_safe_mode = switch (options.optimize_mode) { 1059 .Debug, .ReleaseSafe => true, 1060 .ReleaseFast, .ReleaseSmall => false, 1061 }; 1062 1063 const sanitize_c = options.want_sanitize_c orelse is_safe_mode; 1064 1065 const stack_check: bool = options.want_stack_check orelse b: { 1066 if (!target_util.supportsStackProbing(options.target)) break :b false; 1067 break :b is_safe_mode; 1068 }; 1069 if (stack_check and !target_util.supportsStackProbing(options.target)) 1070 return error.StackCheckUnsupportedByTarget; 1071 1072 const stack_protector: u32 = options.want_stack_protector orelse b: { 1073 if (!target_util.supportsStackProtector(options.target)) break :b @as(u32, 0); 1074 1075 // This logic is checking for linking libc because otherwise our start code 1076 // which is trying to set up TLS (i.e. the fs/gs registers) but the stack 1077 // protection code depends on fs/gs registers being already set up. 1078 // If we were able to annotate start code, or perhaps the entire std lib, 1079 // as being exempt from stack protection checks, we could change this logic 1080 // to supporting stack protection even when not linking libc. 1081 // TODO file issue about this 1082 if (!link_libc) break :b 0; 1083 if (!capable_of_building_ssp) break :b 0; 1084 if (is_safe_mode) break :b default_stack_protector_buffer_size; 1085 break :b 0; 1086 }; 1087 if (stack_protector != 0) { 1088 if (!target_util.supportsStackProtector(options.target)) 1089 return error.StackProtectorUnsupportedByTarget; 1090 if (!capable_of_building_ssp) 1091 return error.StackProtectorUnsupportedByBackend; 1092 if (!link_libc) 1093 return error.StackProtectorUnavailableWithoutLibC; 1094 } 1095 1096 const valgrind: bool = b: { 1097 if (!target_util.hasValgrindSupport(options.target)) 1098 break :b false; 1099 break :b options.want_valgrind orelse (options.optimize_mode == .Debug); 1100 }; 1101 1102 const include_compiler_rt = options.want_compiler_rt orelse needs_c_symbols; 1103 1104 const must_single_thread = target_util.isSingleThreaded(options.target); 1105 const single_threaded = options.single_threaded orelse must_single_thread; 1106 if (must_single_thread and !single_threaded) { 1107 return error.TargetRequiresSingleThreaded; 1108 } 1109 1110 const llvm_cpu_features: ?[*:0]const u8 = if (use_llvm) blk: { 1111 var buf = std.ArrayList(u8).init(arena); 1112 for (options.target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| { 1113 const index = @as(Target.Cpu.Feature.Set.Index, @intCast(index_usize)); 1114 const is_enabled = options.target.cpu.features.isEnabled(index); 1115 1116 if (feature.llvm_name) |llvm_name| { 1117 const plus_or_minus = "-+"[@intFromBool(is_enabled)]; 1118 try buf.ensureUnusedCapacity(2 + llvm_name.len); 1119 buf.appendAssumeCapacity(plus_or_minus); 1120 buf.appendSliceAssumeCapacity(llvm_name); 1121 buf.appendSliceAssumeCapacity(","); 1122 } 1123 } 1124 if (buf.items.len == 0) break :blk ""; 1125 assert(mem.endsWith(u8, buf.items, ",")); 1126 buf.items[buf.items.len - 1] = 0; 1127 buf.shrinkAndFree(buf.items.len); 1128 break :blk buf.items[0 .. buf.items.len - 1 :0].ptr; 1129 } else null; 1130 1131 if (options.verbose_llvm_cpu_features) { 1132 if (llvm_cpu_features) |cf| print: { 1133 std.debug.getStderrMutex().lock(); 1134 defer std.debug.getStderrMutex().unlock(); 1135 const stderr = std.io.getStdErr().writer(); 1136 nosuspend stderr.print("compilation: {s}\n", .{options.root_name}) catch break :print; 1137 nosuspend stderr.print(" target: {s}\n", .{try options.target.zigTriple(arena)}) catch break :print; 1138 nosuspend stderr.print(" cpu: {s}\n", .{options.target.cpu.model.name}) catch break :print; 1139 nosuspend stderr.print(" features: {s}\n", .{cf}) catch {}; 1140 } 1141 } 1142 1143 const strip = options.strip orelse !target_util.hasDebugInfo(options.target); 1144 const red_zone = options.want_red_zone orelse target_util.hasRedZone(options.target); 1145 const omit_frame_pointer = options.omit_frame_pointer orelse (options.optimize_mode != .Debug); 1146 const linker_optimization: u8 = options.linker_optimization orelse switch (options.optimize_mode) { 1147 .Debug => @as(u8, 0), 1148 else => @as(u8, 3), 1149 }; 1150 const formatted_panics = options.formatted_panics orelse (options.optimize_mode == .Debug); 1151 1152 // We put everything into the cache hash that *cannot be modified 1153 // during an incremental update*. For example, one cannot change the 1154 // target between updates, but one can change source files, so the 1155 // target goes into the cache hash, but source files do not. This is so 1156 // that we can find the same binary and incrementally update it even if 1157 // there are modified source files. We do this even if outputting to 1158 // the current directory because we need somewhere to store incremental 1159 // compilation metadata. 1160 const cache = try arena.create(Cache); 1161 cache.* = .{ 1162 .gpa = gpa, 1163 .manifest_dir = try options.local_cache_directory.handle.makeOpenPath("h", .{}), 1164 }; 1165 cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); 1166 cache.addPrefix(options.zig_lib_directory); 1167 cache.addPrefix(options.local_cache_directory); 1168 errdefer cache.manifest_dir.close(); 1169 1170 // This is shared hasher state common to zig source and all C source files. 1171 cache.hash.addBytes(build_options.version); 1172 cache.hash.add(builtin.zig_backend); 1173 cache.hash.add(options.optimize_mode); 1174 cache.hash.add(options.target.cpu.arch); 1175 cache.hash.addBytes(options.target.cpu.model.name); 1176 cache.hash.add(options.target.cpu.features.ints); 1177 cache.hash.add(options.target.os.tag); 1178 cache.hash.add(options.target.os.getVersionRange()); 1179 cache.hash.add(options.is_native_os); 1180 cache.hash.add(options.target.abi); 1181 cache.hash.add(options.target.ofmt); 1182 cache.hash.add(pic); 1183 cache.hash.add(pie); 1184 cache.hash.add(lto); 1185 cache.hash.add(unwind_tables); 1186 cache.hash.add(tsan); 1187 cache.hash.add(stack_check); 1188 cache.hash.add(stack_protector); 1189 cache.hash.add(red_zone); 1190 cache.hash.add(omit_frame_pointer); 1191 cache.hash.add(link_mode); 1192 cache.hash.add(options.function_sections); 1193 cache.hash.add(options.no_builtin); 1194 cache.hash.add(strip); 1195 cache.hash.add(link_libc); 1196 cache.hash.add(link_libcpp); 1197 cache.hash.add(link_libunwind); 1198 cache.hash.add(options.output_mode); 1199 cache.hash.add(options.machine_code_model); 1200 cache.hash.addOptional(options.dwarf_format); 1201 cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin); 1202 cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib); 1203 cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_docs); 1204 cache.hash.addBytes(options.root_name); 1205 if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model); 1206 // TODO audit this and make sure everything is in it 1207 1208 const module: ?*Module = if (options.main_pkg) |main_pkg| blk: { 1209 // Options that are specific to zig source files, that cannot be 1210 // modified between incremental updates. 1211 var hash = cache.hash; 1212 1213 switch (cache_mode) { 1214 .incremental => { 1215 // Here we put the root source file path name, but *not* with addFile. 1216 // We want the hash to be the same regardless of the contents of the 1217 // source file, because incremental compilation will handle it, but we 1218 // do want to namespace different source file names because they are 1219 // likely different compilations and therefore this would be likely to 1220 // cause cache hits. 1221 hash.addBytes(main_pkg.root_src_path); 1222 hash.addOptionalBytes(main_pkg.root_src_directory.path); 1223 { 1224 var seen_table = std.AutoHashMap(*Package, void).init(arena); 1225 try addPackageTableToCacheHash(&hash, &arena_allocator, main_pkg.table, &seen_table, .path_bytes); 1226 } 1227 }, 1228 .whole => { 1229 // In this case, we postpone adding the input source file until 1230 // we create the cache manifest, in update(), because we want to 1231 // track it and packages as files. 1232 }, 1233 } 1234 1235 // Synchronize with other matching comments: ZigOnlyHashStuff 1236 hash.add(valgrind); 1237 hash.add(single_threaded); 1238 hash.add(use_llvm); 1239 hash.add(use_lib_llvm); 1240 hash.add(dll_export_fns); 1241 hash.add(options.is_test); 1242 hash.add(options.test_evented_io); 1243 hash.addOptionalBytes(options.test_filter); 1244 hash.addOptionalBytes(options.test_name_prefix); 1245 hash.add(options.skip_linker_dependencies); 1246 hash.add(options.parent_compilation_link_libc); 1247 hash.add(formatted_panics); 1248 1249 // In the case of incremental cache mode, this `zig_cache_artifact_directory` 1250 // is computed based on a hash of non-linker inputs, and it is where all 1251 // build artifacts are stored (even while in-progress). 1252 // 1253 // For whole cache mode, it is still used for builtin.zig so that the file 1254 // path to builtin.zig can remain consistent during a debugging session at 1255 // runtime. However, we don't know where to put outputs from the linker 1256 // until the final cache hash, which is available after the 1257 // compilation is complete. 1258 // 1259 // Therefore, in whole cache mode, we additionally create a temporary cache 1260 // directory for these two kinds of build artifacts, and then rename it 1261 // into place after the final hash is known. However, we don't want 1262 // to create the temporary directory here, because in the case of a cache hit, 1263 // this would have been wasted syscalls to make the directory and then not 1264 // use it (or delete it). 1265 // 1266 // In summary, for whole cache mode, we simulate `-fno-emit-bin` in this 1267 // function, and `zig_cache_artifact_directory` is *wrong* except for builtin.zig, 1268 // and then at the beginning of `update()` when we find out whether we need 1269 // a temporary directory, we patch up all the places that the incorrect 1270 // `zig_cache_artifact_directory` was passed to various components of the compiler. 1271 1272 const digest = hash.final(); 1273 const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); 1274 var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); 1275 errdefer artifact_dir.close(); 1276 const zig_cache_artifact_directory: Directory = .{ 1277 .handle = artifact_dir, 1278 .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), 1279 }; 1280 1281 const builtin_pkg = try Package.createWithDir( 1282 gpa, 1283 zig_cache_artifact_directory, 1284 null, 1285 "builtin.zig", 1286 ); 1287 errdefer builtin_pkg.destroy(gpa); 1288 1289 // When you're testing std, the main module is std. In that case, we'll just set the std 1290 // module to the main one, since avoiding the errors caused by duplicating it is more 1291 // effort than it's worth. 1292 const main_pkg_is_std = m: { 1293 const std_path = try std.fs.path.resolve(arena, &[_][]const u8{ 1294 options.zig_lib_directory.path orelse ".", 1295 "std", 1296 "std.zig", 1297 }); 1298 defer arena.free(std_path); 1299 const main_path = try std.fs.path.resolve(arena, &[_][]const u8{ 1300 main_pkg.root_src_directory.path orelse ".", 1301 main_pkg.root_src_path, 1302 }); 1303 defer arena.free(main_path); 1304 break :m mem.eql(u8, main_path, std_path); 1305 }; 1306 1307 const std_pkg = if (main_pkg_is_std) 1308 main_pkg 1309 else 1310 try Package.createWithDir( 1311 gpa, 1312 options.zig_lib_directory, 1313 "std", 1314 "std.zig", 1315 ); 1316 1317 errdefer if (!main_pkg_is_std) std_pkg.destroy(gpa); 1318 1319 const root_pkg = if (options.is_test) root_pkg: { 1320 const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: { 1321 const test_dir = std.fs.path.dirname(test_runner); 1322 const basename = std.fs.path.basename(test_runner); 1323 const pkg = try Package.create(gpa, test_dir, basename); 1324 1325 // copy package table from main_pkg to root_pkg 1326 pkg.table = try main_pkg.table.clone(gpa); 1327 break :test_pkg pkg; 1328 } else try Package.createWithDir( 1329 gpa, 1330 options.zig_lib_directory, 1331 null, 1332 "test_runner.zig", 1333 ); 1334 errdefer test_pkg.destroy(gpa); 1335 1336 break :root_pkg test_pkg; 1337 } else main_pkg; 1338 errdefer if (options.is_test) root_pkg.destroy(gpa); 1339 1340 const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: { 1341 break :compiler_rt_pkg try Package.createWithDir( 1342 gpa, 1343 options.zig_lib_directory, 1344 null, 1345 "compiler_rt.zig", 1346 ); 1347 } else null; 1348 errdefer if (compiler_rt_pkg) |p| p.destroy(gpa); 1349 1350 try main_pkg.add(gpa, "builtin", builtin_pkg); 1351 try main_pkg.add(gpa, "root", root_pkg); 1352 try main_pkg.add(gpa, "std", std_pkg); 1353 1354 if (compiler_rt_pkg) |p| { 1355 try main_pkg.add(gpa, "compiler_rt", p); 1356 } 1357 1358 // Pre-open the directory handles for cached ZIR code so that it does not need 1359 // to redundantly happen for each AstGen operation. 1360 const zir_sub_dir = "z"; 1361 1362 var local_zir_dir = try options.local_cache_directory.handle.makeOpenPath(zir_sub_dir, .{}); 1363 errdefer local_zir_dir.close(); 1364 const local_zir_cache: Directory = .{ 1365 .handle = local_zir_dir, 1366 .path = try options.local_cache_directory.join(arena, &[_][]const u8{zir_sub_dir}), 1367 }; 1368 var global_zir_dir = try options.global_cache_directory.handle.makeOpenPath(zir_sub_dir, .{}); 1369 errdefer global_zir_dir.close(); 1370 const global_zir_cache: Directory = .{ 1371 .handle = global_zir_dir, 1372 .path = try options.global_cache_directory.join(arena, &[_][]const u8{zir_sub_dir}), 1373 }; 1374 1375 const emit_h: ?*Module.GlobalEmitH = if (options.emit_h) |loc| eh: { 1376 const eh = try gpa.create(Module.GlobalEmitH); 1377 eh.* = .{ .loc = loc }; 1378 break :eh eh; 1379 } else null; 1380 errdefer if (emit_h) |eh| gpa.destroy(eh); 1381 1382 // TODO when we implement serialization and deserialization of incremental 1383 // compilation metadata, this is where we would load it. We have open a handle 1384 // to the directory where the output either already is, or will be. 1385 // However we currently do not have serialization of such metadata, so for now 1386 // we set up an empty Module that does the entire compilation fresh. 1387 1388 const module = try arena.create(Module); 1389 errdefer module.deinit(); 1390 module.* = .{ 1391 .gpa = gpa, 1392 .comp = comp, 1393 .main_pkg = main_pkg, 1394 .root_pkg = root_pkg, 1395 .zig_cache_artifact_directory = zig_cache_artifact_directory, 1396 .global_zir_cache = global_zir_cache, 1397 .local_zir_cache = local_zir_cache, 1398 .emit_h = emit_h, 1399 .tmp_hack_arena = std.heap.ArenaAllocator.init(gpa), 1400 }; 1401 try module.init(); 1402 1403 break :blk module; 1404 } else blk: { 1405 if (options.emit_h != null) return error.NoZigModuleForCHeader; 1406 break :blk null; 1407 }; 1408 errdefer if (module) |zm| zm.deinit(); 1409 1410 const error_return_tracing = !strip and switch (options.optimize_mode) { 1411 .Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and 1412 !options.target.cpu.arch.isBpf() and (options.error_tracing orelse true), 1413 .ReleaseFast => options.error_tracing orelse false, 1414 .ReleaseSmall => false, 1415 }; 1416 1417 // For resource management purposes. 1418 var owned_link_dir: ?std.fs.Dir = null; 1419 errdefer if (owned_link_dir) |*dir| dir.close(); 1420 1421 const bin_file_emit: ?link.Emit = blk: { 1422 const emit_bin = options.emit_bin orelse break :blk null; 1423 1424 if (emit_bin.directory) |directory| { 1425 break :blk link.Emit{ 1426 .directory = directory, 1427 .sub_path = emit_bin.basename, 1428 }; 1429 } 1430 1431 // In case of whole cache mode, `whole_bin_sub_path` is used to distinguish 1432 // between -femit-bin and -fno-emit-bin. 1433 switch (cache_mode) { 1434 .whole => break :blk null, 1435 .incremental => {}, 1436 } 1437 1438 if (module) |zm| { 1439 break :blk link.Emit{ 1440 .directory = zm.zig_cache_artifact_directory, 1441 .sub_path = emit_bin.basename, 1442 }; 1443 } 1444 1445 // We could use the cache hash as is no problem, however, we increase 1446 // the likelihood of cache hits by adding the first C source file 1447 // path name (not contents) to the hash. This way if the user is compiling 1448 // foo.c and bar.c as separate compilations, they get different cache 1449 // directories. 1450 var hash = cache.hash; 1451 if (options.c_source_files.len >= 1) { 1452 hash.addBytes(options.c_source_files[0].src_path); 1453 } else if (options.link_objects.len >= 1) { 1454 hash.addBytes(options.link_objects[0].path); 1455 } 1456 1457 const digest = hash.final(); 1458 const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); 1459 var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); 1460 owned_link_dir = artifact_dir; 1461 const link_artifact_directory: Directory = .{ 1462 .handle = artifact_dir, 1463 .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), 1464 }; 1465 break :blk link.Emit{ 1466 .directory = link_artifact_directory, 1467 .sub_path = emit_bin.basename, 1468 }; 1469 }; 1470 1471 const implib_emit: ?link.Emit = blk: { 1472 const emit_implib = options.emit_implib orelse break :blk null; 1473 1474 if (emit_implib.directory) |directory| { 1475 break :blk link.Emit{ 1476 .directory = directory, 1477 .sub_path = emit_implib.basename, 1478 }; 1479 } 1480 1481 // This is here for the same reason as in `bin_file_emit` above. 1482 switch (cache_mode) { 1483 .whole => break :blk null, 1484 .incremental => {}, 1485 } 1486 1487 // Use the same directory as the bin. The CLI already emits an 1488 // error if -fno-emit-bin is combined with -femit-implib. 1489 break :blk link.Emit{ 1490 .directory = bin_file_emit.?.directory, 1491 .sub_path = emit_implib.basename, 1492 }; 1493 }; 1494 1495 const docs_emit: ?link.Emit = blk: { 1496 const emit_docs = options.emit_docs orelse break :blk null; 1497 1498 if (emit_docs.directory) |directory| { 1499 break :blk .{ 1500 .directory = directory, 1501 .sub_path = emit_docs.basename, 1502 }; 1503 } 1504 1505 // This is here for the same reason as in `bin_file_emit` above. 1506 switch (cache_mode) { 1507 .whole => break :blk null, 1508 .incremental => {}, 1509 } 1510 1511 // Use the same directory as the bin, if possible. 1512 if (bin_file_emit) |x| break :blk .{ 1513 .directory = x.directory, 1514 .sub_path = emit_docs.basename, 1515 }; 1516 1517 break :blk .{ 1518 .directory = module.?.zig_cache_artifact_directory, 1519 .sub_path = emit_docs.basename, 1520 }; 1521 }; 1522 1523 // This is so that when doing `CacheMode.whole`, the mechanism in update() 1524 // can use it for communicating the result directory via `bin_file.emit`. 1525 // This is used to distinguish between -fno-emit-bin and -femit-bin 1526 // for `CacheMode.whole`. 1527 // This memory will be overwritten with the real digest in update() but 1528 // the basename will be preserved. 1529 const whole_bin_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_bin); 1530 // Same thing but for implibs. 1531 const whole_implib_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_implib); 1532 const whole_docs_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_docs); 1533 1534 var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{}; 1535 errdefer system_libs.deinit(gpa); 1536 try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len); 1537 for (options.system_lib_names, 0..) |lib_name, i| { 1538 system_libs.putAssumeCapacity(lib_name, options.system_lib_infos[i]); 1539 } 1540 1541 const bin_file = try link.File.openPath(gpa, .{ 1542 .emit = bin_file_emit, 1543 .implib_emit = implib_emit, 1544 .docs_emit = docs_emit, 1545 .root_name = root_name, 1546 .module = module, 1547 .target = options.target, 1548 .dynamic_linker = options.dynamic_linker, 1549 .sysroot = sysroot, 1550 .output_mode = options.output_mode, 1551 .link_mode = link_mode, 1552 .optimize_mode = options.optimize_mode, 1553 .use_lld = use_lld, 1554 .use_llvm = use_llvm, 1555 .use_lib_llvm = use_lib_llvm, 1556 .link_libc = link_libc, 1557 .link_libcpp = link_libcpp, 1558 .link_libunwind = link_libunwind, 1559 .objects = options.link_objects, 1560 .frameworks = options.frameworks, 1561 .framework_dirs = options.framework_dirs, 1562 .system_libs = system_libs, 1563 .wasi_emulated_libs = options.wasi_emulated_libs, 1564 .lib_dirs = options.lib_dirs, 1565 .rpath_list = options.rpath_list, 1566 .symbol_wrap_set = options.symbol_wrap_set, 1567 .strip = strip, 1568 .is_native_os = options.is_native_os, 1569 .is_native_abi = options.is_native_abi, 1570 .function_sections = options.function_sections, 1571 .no_builtin = options.no_builtin, 1572 .allow_shlib_undefined = options.linker_allow_shlib_undefined, 1573 .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, 1574 .compress_debug_sections = options.linker_compress_debug_sections orelse .none, 1575 .module_definition_file = options.linker_module_definition_file, 1576 .sort_section = options.linker_sort_section, 1577 .import_memory = options.linker_import_memory orelse false, 1578 .export_memory = options.linker_export_memory orelse !(options.linker_import_memory orelse false), 1579 .import_symbols = options.linker_import_symbols, 1580 .import_table = options.linker_import_table, 1581 .export_table = options.linker_export_table, 1582 .initial_memory = options.linker_initial_memory, 1583 .max_memory = options.linker_max_memory, 1584 .shared_memory = options.linker_shared_memory, 1585 .global_base = options.linker_global_base, 1586 .export_symbol_names = options.linker_export_symbol_names, 1587 .print_gc_sections = options.linker_print_gc_sections, 1588 .print_icf_sections = options.linker_print_icf_sections, 1589 .print_map = options.linker_print_map, 1590 .opt_bisect_limit = options.linker_opt_bisect_limit, 1591 .z_nodelete = options.linker_z_nodelete, 1592 .z_notext = options.linker_z_notext, 1593 .z_defs = options.linker_z_defs, 1594 .z_origin = options.linker_z_origin, 1595 .z_nocopyreloc = options.linker_z_nocopyreloc, 1596 .z_now = options.linker_z_now, 1597 .z_relro = options.linker_z_relro, 1598 .z_common_page_size = options.linker_z_common_page_size, 1599 .z_max_page_size = options.linker_z_max_page_size, 1600 .tsaware = options.linker_tsaware, 1601 .nxcompat = options.linker_nxcompat, 1602 .dynamicbase = options.linker_dynamicbase, 1603 .linker_optimization = linker_optimization, 1604 .major_subsystem_version = options.major_subsystem_version, 1605 .minor_subsystem_version = options.minor_subsystem_version, 1606 .entry = options.entry, 1607 .stack_size_override = options.stack_size_override, 1608 .image_base_override = options.image_base_override, 1609 .include_compiler_rt = include_compiler_rt, 1610 .linker_script = options.linker_script, 1611 .version_script = options.version_script, 1612 .gc_sections = options.linker_gc_sections, 1613 .eh_frame_hdr = link_eh_frame_hdr, 1614 .emit_relocs = options.link_emit_relocs, 1615 .rdynamic = options.rdynamic, 1616 .soname = options.soname, 1617 .version = options.version, 1618 .compatibility_version = options.compatibility_version, 1619 .libc_installation = libc_dirs.libc_installation, 1620 .pic = pic, 1621 .pie = pie, 1622 .lto = lto, 1623 .valgrind = valgrind, 1624 .tsan = tsan, 1625 .stack_check = stack_check, 1626 .stack_protector = stack_protector, 1627 .red_zone = red_zone, 1628 .omit_frame_pointer = omit_frame_pointer, 1629 .single_threaded = single_threaded, 1630 .verbose_link = options.verbose_link, 1631 .machine_code_model = options.machine_code_model, 1632 .dll_export_fns = dll_export_fns, 1633 .error_return_tracing = error_return_tracing, 1634 .llvm_cpu_features = llvm_cpu_features, 1635 .skip_linker_dependencies = options.skip_linker_dependencies, 1636 .parent_compilation_link_libc = options.parent_compilation_link_libc, 1637 .each_lib_rpath = options.each_lib_rpath orelse false, 1638 .build_id = build_id, 1639 .cache_mode = cache_mode, 1640 .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, 1641 .subsystem = options.subsystem, 1642 .is_test = options.is_test, 1643 .dwarf_format = options.dwarf_format, 1644 .wasi_exec_model = wasi_exec_model, 1645 .hash_style = options.hash_style, 1646 .enable_link_snapshots = options.enable_link_snapshots, 1647 .install_name = options.install_name, 1648 .entitlements = options.entitlements, 1649 .pagezero_size = options.pagezero_size, 1650 .headerpad_size = options.headerpad_size, 1651 .headerpad_max_install_names = options.headerpad_max_install_names, 1652 .dead_strip_dylibs = options.dead_strip_dylibs, 1653 .force_undefined_symbols = options.force_undefined_symbols, 1654 .pdb_source_path = options.pdb_source_path, 1655 .pdb_out_path = options.pdb_out_path, 1656 }); 1657 errdefer bin_file.destroy(); 1658 comp.* = .{ 1659 .gpa = gpa, 1660 .arena_state = arena_allocator.state, 1661 .zig_lib_directory = options.zig_lib_directory, 1662 .local_cache_directory = options.local_cache_directory, 1663 .global_cache_directory = options.global_cache_directory, 1664 .bin_file = bin_file, 1665 .whole_bin_sub_path = whole_bin_sub_path, 1666 .whole_implib_sub_path = whole_implib_sub_path, 1667 .whole_docs_sub_path = whole_docs_sub_path, 1668 .emit_asm = options.emit_asm, 1669 .emit_llvm_ir = options.emit_llvm_ir, 1670 .emit_llvm_bc = options.emit_llvm_bc, 1671 .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), 1672 .anon_work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), 1673 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa), 1674 .win32_resource_work_queue = std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa), 1675 .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa), 1676 .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa), 1677 .keep_source_files_loaded = options.keep_source_files_loaded, 1678 .use_clang = use_clang, 1679 .clang_argv = options.clang_argv, 1680 .c_source_files = options.c_source_files, 1681 .rc_source_files = options.rc_source_files, 1682 .cache_parent = cache, 1683 .self_exe_path = options.self_exe_path, 1684 .libc_include_dir_list = libc_dirs.libc_include_dir_list, 1685 .libc_framework_dir_list = libc_dirs.libc_framework_dir_list, 1686 .rc_include_dir_list = rc_dirs.libc_include_dir_list, 1687 .sanitize_c = sanitize_c, 1688 .thread_pool = options.thread_pool, 1689 .clang_passthrough_mode = options.clang_passthrough_mode, 1690 .clang_preprocessor_mode = options.clang_preprocessor_mode, 1691 .verbose_cc = options.verbose_cc, 1692 .verbose_air = options.verbose_air, 1693 .verbose_intern_pool = options.verbose_intern_pool, 1694 .verbose_generic_instances = options.verbose_generic_instances, 1695 .verbose_llvm_ir = options.verbose_llvm_ir, 1696 .verbose_llvm_bc = options.verbose_llvm_bc, 1697 .verbose_cimport = options.verbose_cimport, 1698 .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features, 1699 .disable_c_depfile = options.disable_c_depfile, 1700 .owned_link_dir = owned_link_dir, 1701 .color = options.color, 1702 .reference_trace = options.reference_trace, 1703 .formatted_panics = formatted_panics, 1704 .time_report = options.time_report, 1705 .stack_report = options.stack_report, 1706 .unwind_tables = unwind_tables, 1707 .test_filter = options.test_filter, 1708 .test_name_prefix = options.test_name_prefix, 1709 .test_evented_io = options.test_evented_io, 1710 .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs, 1711 .debug_compile_errors = options.debug_compile_errors, 1712 .libcxx_abi_version = options.libcxx_abi_version, 1713 }; 1714 break :comp comp; 1715 }; 1716 errdefer comp.destroy(); 1717 1718 const target = comp.getTarget(); 1719 1720 // Add a `CObject` for each `c_source_files`. 1721 try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len); 1722 for (options.c_source_files) |c_source_file| { 1723 const c_object = try gpa.create(CObject); 1724 errdefer gpa.destroy(c_object); 1725 1726 c_object.* = .{ 1727 .status = .{ .new = {} }, 1728 .src = c_source_file, 1729 }; 1730 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); 1731 } 1732 1733 // Add a `Win32Resource` for each `rc_source_files`. 1734 try comp.win32_resource_table.ensureTotalCapacity(gpa, options.rc_source_files.len); 1735 for (options.rc_source_files) |rc_source_file| { 1736 const win32_resource = try gpa.create(Win32Resource); 1737 errdefer gpa.destroy(win32_resource); 1738 1739 win32_resource.* = .{ 1740 .status = .{ .new = {} }, 1741 .src = rc_source_file, 1742 }; 1743 comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {}); 1744 } 1745 1746 const have_bin_emit = comp.bin_file.options.emit != null or comp.whole_bin_sub_path != null; 1747 1748 if (have_bin_emit and !comp.bin_file.options.skip_linker_dependencies and target.ofmt != .c) { 1749 if (target.isDarwin()) { 1750 switch (target.abi) { 1751 .none, 1752 .simulator, 1753 .macabi, 1754 => {}, 1755 else => return error.LibCUnavailable, 1756 } 1757 } 1758 // If we need to build glibc for the target, add work items for it. 1759 // We go through the work queue so that building can be done in parallel. 1760 if (comp.wantBuildGLibCFromSource()) { 1761 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; 1762 1763 if (glibc.needsCrtiCrtn(target)) { 1764 try comp.work_queue.write(&[_]Job{ 1765 .{ .glibc_crt_file = .crti_o }, 1766 .{ .glibc_crt_file = .crtn_o }, 1767 }); 1768 } 1769 try comp.work_queue.write(&[_]Job{ 1770 .{ .glibc_crt_file = .scrt1_o }, 1771 .{ .glibc_crt_file = .libc_nonshared_a }, 1772 .{ .glibc_shared_objects = {} }, 1773 }); 1774 } 1775 if (comp.wantBuildMuslFromSource()) { 1776 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; 1777 1778 try comp.work_queue.ensureUnusedCapacity(6); 1779 if (musl.needsCrtiCrtn(target)) { 1780 comp.work_queue.writeAssumeCapacity(&[_]Job{ 1781 .{ .musl_crt_file = .crti_o }, 1782 .{ .musl_crt_file = .crtn_o }, 1783 }); 1784 } 1785 comp.work_queue.writeAssumeCapacity(&[_]Job{ 1786 .{ .musl_crt_file = .crt1_o }, 1787 .{ .musl_crt_file = .scrt1_o }, 1788 .{ .musl_crt_file = .rcrt1_o }, 1789 switch (comp.bin_file.options.link_mode) { 1790 .Static => .{ .musl_crt_file = .libc_a }, 1791 .Dynamic => .{ .musl_crt_file = .libc_so }, 1792 }, 1793 }); 1794 } 1795 if (comp.wantBuildWasiLibcFromSource()) { 1796 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; 1797 1798 const wasi_emulated_libs = comp.bin_file.options.wasi_emulated_libs; 1799 try comp.work_queue.ensureUnusedCapacity(wasi_emulated_libs.len + 2); // worst-case we need all components 1800 for (wasi_emulated_libs) |crt_file| { 1801 comp.work_queue.writeItemAssumeCapacity(.{ 1802 .wasi_libc_crt_file = crt_file, 1803 }); 1804 } 1805 comp.work_queue.writeAssumeCapacity(&[_]Job{ 1806 .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(wasi_exec_model) }, 1807 .{ .wasi_libc_crt_file = .libc_a }, 1808 }); 1809 } 1810 if (comp.wantBuildMinGWFromSource()) { 1811 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; 1812 1813 const static_lib_jobs = [_]Job{ 1814 .{ .mingw_crt_file = .mingw32_lib }, 1815 .{ .mingw_crt_file = .msvcrt_os_lib }, 1816 .{ .mingw_crt_file = .mingwex_lib }, 1817 .{ .mingw_crt_file = .uuid_lib }, 1818 }; 1819 const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; 1820 try comp.work_queue.ensureUnusedCapacity(static_lib_jobs.len + 1); 1821 comp.work_queue.writeAssumeCapacity(&static_lib_jobs); 1822 comp.work_queue.writeItemAssumeCapacity(crt_job); 1823 1824 // When linking mingw-w64 there are some import libs we always need. 1825 for (mingw.always_link_libs) |name| { 1826 try comp.bin_file.options.system_libs.put(comp.gpa, name, .{ 1827 .needed = false, 1828 .weak = false, 1829 .path = null, 1830 }); 1831 } 1832 } 1833 // Generate Windows import libs. 1834 if (target.os.tag == .windows) { 1835 const count = comp.bin_file.options.system_libs.count(); 1836 try comp.work_queue.ensureUnusedCapacity(count); 1837 for (0..count) |i| { 1838 comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i }); 1839 } 1840 } 1841 if (comp.wantBuildLibUnwindFromSource()) { 1842 try comp.work_queue.writeItem(.{ .libunwind = {} }); 1843 } 1844 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.bin_file.options.link_libcpp) { 1845 try comp.work_queue.writeItem(.libcxx); 1846 try comp.work_queue.writeItem(.libcxxabi); 1847 } 1848 if (build_options.have_llvm and comp.bin_file.options.tsan) { 1849 try comp.work_queue.writeItem(.libtsan); 1850 } 1851 1852 if (comp.getTarget().isMinGW() and !comp.bin_file.options.single_threaded) { 1853 // LLD might drop some symbols as unused during LTO and GCing, therefore, 1854 // we force mark them for resolution here. 1855 1856 var tls_index_sym = switch (comp.getTarget().cpu.arch) { 1857 .x86 => "__tls_index", 1858 else => "_tls_index", 1859 }; 1860 1861 try comp.bin_file.options.force_undefined_symbols.put(comp.gpa, tls_index_sym, {}); 1862 } 1863 1864 if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) { 1865 if (is_exe_or_dyn_lib) { 1866 log.debug("queuing a job to build compiler_rt_lib", .{}); 1867 comp.job_queued_compiler_rt_lib = true; 1868 } else if (options.output_mode != .Obj) { 1869 log.debug("queuing a job to build compiler_rt_obj", .{}); 1870 // If build-obj with -fcompiler-rt is requested, that is handled specially 1871 // elsewhere. In this case we are making a static library, so we ask 1872 // for a compiler-rt object to put in it. 1873 comp.job_queued_compiler_rt_obj = true; 1874 } 1875 } 1876 if (needs_c_symbols) { 1877 // Related: https://github.com/ziglang/zig/issues/7265. 1878 if (comp.bin_file.options.stack_protector != 0 and 1879 (!comp.bin_file.options.link_libc or 1880 !target_util.libcProvidesStackProtector(target))) 1881 { 1882 try comp.work_queue.writeItem(.{ .libssp = {} }); 1883 } 1884 1885 if (!comp.bin_file.options.link_libc and capable_of_building_zig_libc) { 1886 try comp.work_queue.writeItem(.{ .zig_libc = {} }); 1887 } 1888 } 1889 } 1890 1891 return comp; 1892 } 1893 1894 pub fn destroy(self: *Compilation) void { 1895 const optional_module = self.bin_file.options.module; 1896 self.bin_file.destroy(); 1897 if (optional_module) |module| module.deinit(); 1898 1899 const gpa = self.gpa; 1900 self.work_queue.deinit(); 1901 self.anon_work_queue.deinit(); 1902 self.c_object_work_queue.deinit(); 1903 self.win32_resource_work_queue.deinit(); 1904 self.astgen_work_queue.deinit(); 1905 self.embed_file_work_queue.deinit(); 1906 1907 { 1908 var it = self.crt_files.iterator(); 1909 while (it.next()) |entry| { 1910 gpa.free(entry.key_ptr.*); 1911 entry.value_ptr.deinit(gpa); 1912 } 1913 self.crt_files.deinit(gpa); 1914 } 1915 1916 if (self.libunwind_static_lib) |*crt_file| { 1917 crt_file.deinit(gpa); 1918 } 1919 if (self.libcxx_static_lib) |*crt_file| { 1920 crt_file.deinit(gpa); 1921 } 1922 if (self.libcxxabi_static_lib) |*crt_file| { 1923 crt_file.deinit(gpa); 1924 } 1925 if (self.compiler_rt_lib) |*crt_file| { 1926 crt_file.deinit(gpa); 1927 } 1928 if (self.compiler_rt_obj) |*crt_file| { 1929 crt_file.deinit(gpa); 1930 } 1931 if (self.libssp_static_lib) |*crt_file| { 1932 crt_file.deinit(gpa); 1933 } 1934 if (self.libc_static_lib) |*crt_file| { 1935 crt_file.deinit(gpa); 1936 } 1937 1938 if (self.glibc_so_files) |*glibc_file| { 1939 glibc_file.deinit(gpa); 1940 } 1941 1942 for (self.c_object_table.keys()) |key| { 1943 key.destroy(gpa); 1944 } 1945 self.c_object_table.deinit(gpa); 1946 1947 for (self.failed_c_objects.values()) |value| { 1948 value.destroy(gpa); 1949 } 1950 self.failed_c_objects.deinit(gpa); 1951 1952 for (self.win32_resource_table.keys()) |key| { 1953 key.destroy(gpa); 1954 } 1955 self.win32_resource_table.deinit(gpa); 1956 1957 for (self.failed_win32_resources.values()) |*value| { 1958 value.deinit(gpa); 1959 } 1960 self.failed_win32_resources.deinit(gpa); 1961 1962 for (self.lld_errors.items) |*lld_error| { 1963 lld_error.deinit(gpa); 1964 } 1965 self.lld_errors.deinit(gpa); 1966 1967 self.clearMiscFailures(); 1968 1969 self.cache_parent.manifest_dir.close(); 1970 if (self.owned_link_dir) |*dir| dir.close(); 1971 1972 // This destroys `self`. 1973 self.arena_state.promote(gpa).deinit(); 1974 } 1975 1976 pub fn clearMiscFailures(comp: *Compilation) void { 1977 comp.alloc_failure_occurred = false; 1978 for (comp.misc_failures.values()) |*value| { 1979 value.deinit(comp.gpa); 1980 } 1981 comp.misc_failures.deinit(comp.gpa); 1982 comp.misc_failures = .{}; 1983 } 1984 1985 pub fn getTarget(self: Compilation) Target { 1986 return self.bin_file.options.target; 1987 } 1988 1989 fn restorePrevZigCacheArtifactDirectory(comp: *Compilation, directory: *Directory) void { 1990 if (directory.path) |p| comp.gpa.free(p); 1991 1992 // Restore the Module's previous zig_cache_artifact_directory 1993 // This is only for cleanup purposes; Module.deinit calls close 1994 // on the handle of zig_cache_artifact_directory. 1995 if (comp.bin_file.options.module) |module| { 1996 const builtin_pkg = module.main_pkg.table.get("builtin").?; 1997 module.zig_cache_artifact_directory = builtin_pkg.root_src_directory; 1998 } 1999 } 2000 2001 fn cleanupTmpArtifactDirectory( 2002 comp: *Compilation, 2003 tmp_artifact_directory: *?Directory, 2004 tmp_dir_sub_path: []const u8, 2005 ) void { 2006 comp.gpa.free(tmp_dir_sub_path); 2007 if (tmp_artifact_directory.*) |*directory| { 2008 directory.handle.close(); 2009 restorePrevZigCacheArtifactDirectory(comp, directory); 2010 } 2011 } 2012 2013 pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.ChildProcess.Id) !void { 2014 comp.bin_file.child_pid = pid; 2015 try comp.makeBinFileWritable(); 2016 try comp.update(prog_node); 2017 try comp.makeBinFileExecutable(); 2018 } 2019 2020 /// Detect changes to source files, perform semantic analysis, and update the output files. 2021 pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void { 2022 const tracy_trace = trace(@src()); 2023 defer tracy_trace.end(); 2024 2025 comp.clearMiscFailures(); 2026 comp.last_update_was_cache_hit = false; 2027 2028 var man: Cache.Manifest = undefined; 2029 defer if (comp.whole_cache_manifest != null) man.deinit(); 2030 2031 var tmp_dir_sub_path: []const u8 = &.{}; 2032 var tmp_artifact_directory: ?Directory = null; 2033 defer cleanupTmpArtifactDirectory(comp, &tmp_artifact_directory, tmp_dir_sub_path); 2034 2035 // If using the whole caching strategy, we check for *everything* up front, including 2036 // C source files. 2037 if (comp.bin_file.options.cache_mode == .whole) { 2038 // We are about to obtain this lock, so here we give other processes a chance first. 2039 comp.bin_file.releaseLock(); 2040 2041 man = comp.cache_parent.obtain(); 2042 comp.whole_cache_manifest = &man; 2043 try comp.addNonIncrementalStuffToCacheManifest(&man); 2044 2045 const is_hit = man.hit() catch |err| { 2046 // TODO properly bubble these up instead of emitting a warning 2047 const i = man.failed_file_index orelse return err; 2048 const pp = man.files.items[i].prefixed_path orelse return err; 2049 const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; 2050 std.log.warn("{s}: {s}{s}", .{ @errorName(err), prefix, pp.sub_path }); 2051 return err; 2052 }; 2053 if (is_hit) { 2054 comp.last_update_was_cache_hit = true; 2055 log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name}); 2056 const digest = man.final(); 2057 2058 comp.wholeCacheModeSetBinFilePath(&digest); 2059 2060 assert(comp.bin_file.lock == null); 2061 comp.bin_file.lock = man.toOwnedLock(); 2062 return; 2063 } 2064 log.debug("CacheMode.whole cache miss for {s}", .{comp.bin_file.options.root_name}); 2065 2066 // Initialize `bin_file.emit` with a temporary Directory so that compilation can 2067 // continue on the same path as incremental, using the temporary Directory. 2068 tmp_artifact_directory = d: { 2069 const s = std.fs.path.sep_str; 2070 const rand_int = std.crypto.random.int(u64); 2071 2072 tmp_dir_sub_path = try std.fmt.allocPrint(comp.gpa, "tmp" ++ s ++ "{x}", .{rand_int}); 2073 2074 const path = try comp.local_cache_directory.join(comp.gpa, &.{tmp_dir_sub_path}); 2075 errdefer comp.gpa.free(path); 2076 2077 const handle = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); 2078 errdefer handle.close(); 2079 2080 break :d .{ 2081 .path = path, 2082 .handle = handle, 2083 }; 2084 }; 2085 2086 // This updates the output directory for linker outputs. 2087 if (comp.bin_file.options.module) |module| { 2088 module.zig_cache_artifact_directory = tmp_artifact_directory.?; 2089 } 2090 2091 // This resets the link.File to operate as if we called openPath() in create() 2092 // instead of simulating -fno-emit-bin. 2093 var options = comp.bin_file.options.move(); 2094 if (comp.whole_bin_sub_path) |sub_path| { 2095 options.emit = .{ 2096 .directory = tmp_artifact_directory.?, 2097 .sub_path = std.fs.path.basename(sub_path), 2098 }; 2099 } 2100 if (comp.whole_implib_sub_path) |sub_path| { 2101 options.implib_emit = .{ 2102 .directory = tmp_artifact_directory.?, 2103 .sub_path = std.fs.path.basename(sub_path), 2104 }; 2105 } 2106 if (comp.whole_docs_sub_path) |sub_path| { 2107 options.docs_emit = .{ 2108 .directory = tmp_artifact_directory.?, 2109 .sub_path = std.fs.path.basename(sub_path), 2110 }; 2111 } 2112 var old_bin_file = comp.bin_file; 2113 comp.bin_file = try link.File.openPath(comp.gpa, options); 2114 old_bin_file.destroy(); 2115 } 2116 2117 // For compiling C objects, we rely on the cache hash system to avoid duplicating work. 2118 // Add a Job for each C object. 2119 try comp.c_object_work_queue.ensureUnusedCapacity(comp.c_object_table.count()); 2120 for (comp.c_object_table.keys()) |key| { 2121 comp.c_object_work_queue.writeItemAssumeCapacity(key); 2122 } 2123 2124 // For compiling Win32 resources, we rely on the cache hash system to avoid duplicating work. 2125 // Add a Job for each Win32 resource file. 2126 try comp.win32_resource_work_queue.ensureUnusedCapacity(comp.win32_resource_table.count()); 2127 for (comp.win32_resource_table.keys()) |key| { 2128 comp.win32_resource_work_queue.writeItemAssumeCapacity(key); 2129 } 2130 2131 if (comp.bin_file.options.module) |module| { 2132 module.compile_log_text.shrinkAndFree(module.gpa, 0); 2133 module.generation += 1; 2134 2135 // Make sure std.zig is inside the import_table. We unconditionally need 2136 // it for start.zig. 2137 const std_pkg = module.main_pkg.table.get("std").?; 2138 _ = try module.importPkg(std_pkg); 2139 2140 // Normally we rely on importing std to in turn import the root source file 2141 // in the start code, but when using the stage1 backend that won't happen, 2142 // so in order to run AstGen on the root source file we put it into the 2143 // import_table here. 2144 // Likewise, in the case of `zig test`, the test runner is the root source file, 2145 // and so there is nothing to import the main file. 2146 if (comp.bin_file.options.is_test) { 2147 _ = try module.importPkg(module.main_pkg); 2148 } 2149 2150 if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| { 2151 _ = try module.importPkg(compiler_rt_pkg); 2152 } 2153 2154 // Put a work item in for every known source file to detect if 2155 // it changed, and, if so, re-compute ZIR and then queue the job 2156 // to update it. 2157 // We still want AstGen work items for stage1 so that we expose compile errors 2158 // that are implemented in stage2 but not stage1. 2159 try comp.astgen_work_queue.ensureUnusedCapacity(module.import_table.count()); 2160 for (module.import_table.values()) |value| { 2161 comp.astgen_work_queue.writeItemAssumeCapacity(value); 2162 } 2163 2164 // Put a work item in for checking if any files used with `@embedFile` changed. 2165 { 2166 try comp.embed_file_work_queue.ensureUnusedCapacity(module.embed_table.count()); 2167 var it = module.embed_table.iterator(); 2168 while (it.next()) |entry| { 2169 const embed_file = entry.value_ptr.*; 2170 comp.embed_file_work_queue.writeItemAssumeCapacity(embed_file); 2171 } 2172 } 2173 2174 try comp.work_queue.writeItem(.{ .analyze_pkg = std_pkg }); 2175 if (comp.bin_file.options.is_test) { 2176 try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg }); 2177 } 2178 2179 if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| { 2180 try comp.work_queue.writeItem(.{ .analyze_pkg = compiler_rt_pkg }); 2181 } 2182 } 2183 2184 try comp.performAllTheWork(main_progress_node); 2185 2186 if (comp.bin_file.options.module) |module| { 2187 if (builtin.mode == .Debug and comp.verbose_intern_pool) { 2188 std.debug.print("intern pool stats for '{s}':\n", .{ 2189 comp.bin_file.options.root_name, 2190 }); 2191 module.intern_pool.dump(); 2192 } 2193 2194 if (builtin.mode == .Debug and comp.verbose_generic_instances) { 2195 std.debug.print("generic instances for '{s}:0x{x}':\n", .{ 2196 comp.bin_file.options.root_name, 2197 @as(usize, @intFromPtr(module)), 2198 }); 2199 module.intern_pool.dumpGenericInstances(comp.gpa); 2200 } 2201 2202 if (comp.bin_file.options.is_test and comp.totalErrorCount() == 0) { 2203 // The `test_functions` decl has been intentionally postponed until now, 2204 // at which point we must populate it with the list of test functions that 2205 // have been discovered and not filtered out. 2206 try module.populateTestFunctions(main_progress_node); 2207 } 2208 2209 // Process the deletion set. We use a while loop here because the 2210 // deletion set may grow as we call `clearDecl` within this loop, 2211 // and more unreferenced Decls are revealed. 2212 while (module.deletion_set.count() != 0) { 2213 const decl_index = module.deletion_set.keys()[0]; 2214 const decl = module.declPtr(decl_index); 2215 assert(decl.deletion_flag); 2216 assert(decl.dependants.count() == 0); 2217 assert(decl.zir_decl_index != 0); 2218 2219 try module.clearDecl(decl_index, null); 2220 } 2221 2222 try module.processExports(); 2223 } 2224 2225 if (comp.totalErrorCount() != 0) { 2226 // Skip flushing and keep source files loaded for error reporting. 2227 comp.link_error_flags = .{}; 2228 return; 2229 } 2230 2231 // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and 2232 // -femit-asm to handle, in the case of C objects. 2233 comp.emitOthers(); 2234 2235 if (comp.whole_cache_manifest != null) { 2236 const digest = man.final(); 2237 2238 // Rename the temporary directory into place. 2239 var directory = tmp_artifact_directory.?; 2240 tmp_artifact_directory = null; 2241 2242 directory.handle.close(); 2243 defer restorePrevZigCacheArtifactDirectory(comp, &directory); 2244 2245 const o_sub_path = try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest }); 2246 defer comp.gpa.free(o_sub_path); 2247 2248 // Work around windows `AccessDenied` if any files within this directory are open 2249 // by doing the makeExecutable/makeWritable dance. 2250 const need_writable_dance = builtin.os.tag == .windows and comp.bin_file.file != null; 2251 if (need_writable_dance) { 2252 try comp.bin_file.makeExecutable(); 2253 } 2254 2255 try comp.bin_file.renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path); 2256 comp.wholeCacheModeSetBinFilePath(&digest); 2257 2258 // Has to be after the `wholeCacheModeSetBinFilePath` above. 2259 if (need_writable_dance) { 2260 try comp.bin_file.makeWritable(); 2261 } 2262 2263 // This is intentionally sandwiched between renameTmpIntoCache() and writeManifest(). 2264 if (comp.bin_file.options.module) |module| { 2265 // We need to set the zig_cache_artifact_directory for -femit-asm, -femit-llvm-ir, 2266 // etc to know where to output to. 2267 var artifact_dir = try comp.local_cache_directory.handle.openDir(o_sub_path, .{}); 2268 defer artifact_dir.close(); 2269 2270 var dir_path = try comp.local_cache_directory.join(comp.gpa, &.{o_sub_path}); 2271 defer comp.gpa.free(dir_path); 2272 2273 module.zig_cache_artifact_directory = .{ 2274 .handle = artifact_dir, 2275 .path = dir_path, 2276 }; 2277 2278 try comp.flush(main_progress_node); 2279 if (comp.totalErrorCount() != 0) return; 2280 2281 // Note the placement of this logic is relying on the call to 2282 // `wholeCacheModeSetBinFilePath` above. 2283 try maybeGenerateAutodocs(comp, main_progress_node); 2284 } else { 2285 try comp.flush(main_progress_node); 2286 if (comp.totalErrorCount() != 0) return; 2287 } 2288 2289 // Failure here only means an unnecessary cache miss. 2290 man.writeManifest() catch |err| { 2291 log.warn("failed to write cache manifest: {s}", .{@errorName(err)}); 2292 }; 2293 2294 assert(comp.bin_file.lock == null); 2295 comp.bin_file.lock = man.toOwnedLock(); 2296 } else { 2297 try comp.flush(main_progress_node); 2298 2299 if (comp.totalErrorCount() == 0) { 2300 try maybeGenerateAutodocs(comp, main_progress_node); 2301 } 2302 } 2303 2304 // Unload all source files to save memory. 2305 // The ZIR needs to stay loaded in memory because (1) Decl objects contain references 2306 // to it, and (2) generic instantiations, comptime calls, inline calls will need 2307 // to reference the ZIR. 2308 if (!comp.keep_source_files_loaded) { 2309 if (comp.bin_file.options.module) |module| { 2310 for (module.import_table.values()) |file| { 2311 file.unloadTree(comp.gpa); 2312 file.unloadSource(comp.gpa); 2313 } 2314 } 2315 } 2316 } 2317 2318 fn maybeGenerateAutodocs(comp: *Compilation, prog_node: *std.Progress.Node) !void { 2319 const mod = comp.bin_file.options.module orelse return; 2320 // TODO: do this in a separate job during performAllTheWork(). The 2321 // file copies at the end of generate() can also be extracted to 2322 // separate jobs 2323 if (!build_options.only_c and !build_options.only_core_functionality) { 2324 if (comp.bin_file.options.docs_emit) |emit| { 2325 var dir = try emit.directory.handle.makeOpenPath(emit.sub_path, .{}); 2326 defer dir.close(); 2327 2328 var sub_prog_node = prog_node.start("Generating documentation", 0); 2329 sub_prog_node.activate(); 2330 sub_prog_node.context.refresh(); 2331 defer sub_prog_node.end(); 2332 2333 try Autodoc.generate(mod, dir); 2334 } 2335 } 2336 } 2337 2338 fn flush(comp: *Compilation, prog_node: *std.Progress.Node) !void { 2339 // This is needed before reading the error flags. 2340 comp.bin_file.flush(comp, prog_node) catch |err| switch (err) { 2341 error.FlushFailure => {}, // error reported through link_error_flags 2342 error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr 2343 else => |e| return e, 2344 }; 2345 comp.link_error_flags = comp.bin_file.errorFlags(); 2346 2347 if (comp.bin_file.options.module) |module| { 2348 try link.File.C.flushEmitH(module); 2349 } 2350 } 2351 2352 /// Communicate the output binary location to parent Compilations. 2353 fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_digest_len]u8) void { 2354 const digest_start = 2; // "o/[digest]/[basename]" 2355 2356 if (comp.whole_bin_sub_path) |sub_path| { 2357 @memcpy(sub_path[digest_start..][0..digest.len], digest); 2358 2359 comp.bin_file.options.emit = .{ 2360 .directory = comp.local_cache_directory, 2361 .sub_path = sub_path, 2362 }; 2363 } 2364 2365 if (comp.whole_implib_sub_path) |sub_path| { 2366 @memcpy(sub_path[digest_start..][0..digest.len], digest); 2367 2368 comp.bin_file.options.implib_emit = .{ 2369 .directory = comp.local_cache_directory, 2370 .sub_path = sub_path, 2371 }; 2372 } 2373 2374 if (comp.whole_docs_sub_path) |sub_path| { 2375 @memcpy(sub_path[digest_start..][0..digest.len], digest); 2376 2377 comp.bin_file.options.docs_emit = .{ 2378 .directory = comp.local_cache_directory, 2379 .sub_path = sub_path, 2380 }; 2381 } 2382 } 2383 2384 fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemory}!?[]u8 { 2385 const emit = opt_emit orelse return null; 2386 if (emit.directory != null) return null; 2387 const s = std.fs.path.sep_str; 2388 const format = "o" ++ s ++ ("x" ** Cache.hex_digest_len) ++ s ++ "{s}"; 2389 return try std.fmt.allocPrint(arena, format, .{emit.basename}); 2390 } 2391 2392 /// This is only observed at compile-time and used to emit a compile error 2393 /// to remind the programmer to update multiple related pieces of code that 2394 /// are in different locations. Bump this number when adding or deleting 2395 /// anything from the link cache manifest. 2396 pub const link_hash_implementation_version = 10; 2397 2398 fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { 2399 const gpa = comp.gpa; 2400 const target = comp.getTarget(); 2401 2402 var arena_allocator = std.heap.ArenaAllocator.init(gpa); 2403 defer arena_allocator.deinit(); 2404 const arena = arena_allocator.allocator(); 2405 2406 comptime assert(link_hash_implementation_version == 10); 2407 2408 if (comp.bin_file.options.module) |mod| { 2409 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{ 2410 mod.main_pkg.root_src_path, 2411 }); 2412 _ = try man.addFile(main_zig_file, null); 2413 { 2414 var seen_table = std.AutoHashMap(*Package, void).init(arena); 2415 2416 // Skip builtin.zig; it is useless as an input, and we don't want to have to 2417 // write it before checking for a cache hit. 2418 const builtin_pkg = mod.main_pkg.table.get("builtin").?; 2419 try seen_table.put(builtin_pkg, {}); 2420 2421 try addPackageTableToCacheHash(&man.hash, &arena_allocator, mod.main_pkg.table, &seen_table, .{ .files = man }); 2422 } 2423 2424 // Synchronize with other matching comments: ZigOnlyHashStuff 2425 man.hash.add(comp.bin_file.options.valgrind); 2426 man.hash.add(comp.bin_file.options.single_threaded); 2427 man.hash.add(comp.bin_file.options.use_llvm); 2428 man.hash.add(comp.bin_file.options.dll_export_fns); 2429 man.hash.add(comp.bin_file.options.is_test); 2430 man.hash.add(comp.test_evented_io); 2431 man.hash.addOptionalBytes(comp.test_filter); 2432 man.hash.addOptionalBytes(comp.test_name_prefix); 2433 man.hash.add(comp.bin_file.options.skip_linker_dependencies); 2434 man.hash.add(comp.bin_file.options.parent_compilation_link_libc); 2435 man.hash.add(mod.emit_h != null); 2436 } 2437 2438 try man.addOptionalFile(comp.bin_file.options.linker_script); 2439 try man.addOptionalFile(comp.bin_file.options.version_script); 2440 2441 for (comp.bin_file.options.objects) |obj| { 2442 _ = try man.addFile(obj.path, null); 2443 man.hash.add(obj.must_link); 2444 man.hash.add(obj.loption); 2445 } 2446 2447 for (comp.c_object_table.keys()) |key| { 2448 _ = try man.addFile(key.src.src_path, null); 2449 man.hash.addOptional(key.src.ext); 2450 man.hash.addListOfBytes(key.src.extra_flags); 2451 } 2452 2453 for (comp.win32_resource_table.keys()) |key| { 2454 _ = try man.addFile(key.src.src_path, null); 2455 man.hash.addListOfBytes(key.src.extra_flags); 2456 } 2457 2458 man.hash.addListOfBytes(comp.rc_include_dir_list); 2459 2460 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm); 2461 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir); 2462 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc); 2463 2464 man.hash.addListOfBytes(comp.clang_argv); 2465 2466 man.hash.addOptional(comp.bin_file.options.stack_size_override); 2467 man.hash.addOptional(comp.bin_file.options.image_base_override); 2468 man.hash.addOptional(comp.bin_file.options.gc_sections); 2469 man.hash.add(comp.bin_file.options.eh_frame_hdr); 2470 man.hash.add(comp.bin_file.options.emit_relocs); 2471 man.hash.add(comp.bin_file.options.rdynamic); 2472 man.hash.addListOfBytes(comp.bin_file.options.lib_dirs); 2473 man.hash.addListOfBytes(comp.bin_file.options.rpath_list); 2474 man.hash.addListOfBytes(comp.bin_file.options.symbol_wrap_set.keys()); 2475 man.hash.add(comp.bin_file.options.each_lib_rpath); 2476 man.hash.add(comp.bin_file.options.build_id); 2477 man.hash.add(comp.bin_file.options.skip_linker_dependencies); 2478 man.hash.add(comp.bin_file.options.z_nodelete); 2479 man.hash.add(comp.bin_file.options.z_notext); 2480 man.hash.add(comp.bin_file.options.z_defs); 2481 man.hash.add(comp.bin_file.options.z_origin); 2482 man.hash.add(comp.bin_file.options.z_nocopyreloc); 2483 man.hash.add(comp.bin_file.options.z_now); 2484 man.hash.add(comp.bin_file.options.z_relro); 2485 man.hash.add(comp.bin_file.options.z_common_page_size orelse 0); 2486 man.hash.add(comp.bin_file.options.z_max_page_size orelse 0); 2487 man.hash.add(comp.bin_file.options.hash_style); 2488 man.hash.add(comp.bin_file.options.compress_debug_sections); 2489 man.hash.add(comp.bin_file.options.include_compiler_rt); 2490 man.hash.addOptional(comp.bin_file.options.sort_section); 2491 if (comp.bin_file.options.link_libc) { 2492 man.hash.add(comp.bin_file.options.libc_installation != null); 2493 if (comp.bin_file.options.libc_installation) |libc_installation| { 2494 man.hash.addOptionalBytes(libc_installation.crt_dir); 2495 if (target.abi == .msvc) { 2496 man.hash.addOptionalBytes(libc_installation.msvc_lib_dir); 2497 man.hash.addOptionalBytes(libc_installation.kernel32_lib_dir); 2498 } 2499 } 2500 man.hash.addOptionalBytes(comp.bin_file.options.dynamic_linker); 2501 } 2502 man.hash.addOptionalBytes(comp.bin_file.options.soname); 2503 man.hash.addOptional(comp.bin_file.options.version); 2504 try link.hashAddSystemLibs(man, comp.bin_file.options.system_libs); 2505 man.hash.addListOfBytes(comp.bin_file.options.force_undefined_symbols.keys()); 2506 man.hash.addOptional(comp.bin_file.options.allow_shlib_undefined); 2507 man.hash.add(comp.bin_file.options.bind_global_refs_locally); 2508 man.hash.add(comp.bin_file.options.tsan); 2509 man.hash.addOptionalBytes(comp.bin_file.options.sysroot); 2510 man.hash.add(comp.bin_file.options.linker_optimization); 2511 2512 // WASM specific stuff 2513 man.hash.add(comp.bin_file.options.import_memory); 2514 man.hash.add(comp.bin_file.options.export_memory); 2515 man.hash.addOptional(comp.bin_file.options.initial_memory); 2516 man.hash.addOptional(comp.bin_file.options.max_memory); 2517 man.hash.add(comp.bin_file.options.shared_memory); 2518 man.hash.addOptional(comp.bin_file.options.global_base); 2519 2520 // Mach-O specific stuff 2521 man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); 2522 try link.hashAddFrameworks(man, comp.bin_file.options.frameworks); 2523 try man.addOptionalFile(comp.bin_file.options.entitlements); 2524 man.hash.addOptional(comp.bin_file.options.pagezero_size); 2525 man.hash.addOptional(comp.bin_file.options.headerpad_size); 2526 man.hash.add(comp.bin_file.options.headerpad_max_install_names); 2527 man.hash.add(comp.bin_file.options.dead_strip_dylibs); 2528 2529 // COFF specific stuff 2530 man.hash.addOptional(comp.bin_file.options.subsystem); 2531 man.hash.add(comp.bin_file.options.tsaware); 2532 man.hash.add(comp.bin_file.options.nxcompat); 2533 man.hash.add(comp.bin_file.options.dynamicbase); 2534 man.hash.addOptional(comp.bin_file.options.major_subsystem_version); 2535 man.hash.addOptional(comp.bin_file.options.minor_subsystem_version); 2536 } 2537 2538 fn emitOthers(comp: *Compilation) void { 2539 if (comp.bin_file.options.output_mode != .Obj or comp.bin_file.options.module != null or 2540 comp.c_object_table.count() == 0) 2541 { 2542 return; 2543 } 2544 const obj_path = comp.c_object_table.keys()[0].status.success.object_path; 2545 const cwd = std.fs.cwd(); 2546 const ext = std.fs.path.extension(obj_path); 2547 const basename = obj_path[0 .. obj_path.len - ext.len]; 2548 // This obj path always ends with the object file extension, but if we change the 2549 // extension to .ll, .bc, or .s, then it will be the path to those things. 2550 const outs = [_]struct { 2551 emit: ?EmitLoc, 2552 ext: []const u8, 2553 }{ 2554 .{ .emit = comp.emit_asm, .ext = ".s" }, 2555 .{ .emit = comp.emit_llvm_ir, .ext = ".ll" }, 2556 .{ .emit = comp.emit_llvm_bc, .ext = ".bc" }, 2557 }; 2558 for (outs) |out| { 2559 if (out.emit) |loc| { 2560 if (loc.directory) |directory| { 2561 const src_path = std.fmt.allocPrint(comp.gpa, "{s}{s}", .{ 2562 basename, out.ext, 2563 }) catch |err| { 2564 log.err("unable to copy {s}{s}: {s}", .{ basename, out.ext, @errorName(err) }); 2565 continue; 2566 }; 2567 defer comp.gpa.free(src_path); 2568 cwd.copyFile(src_path, directory.handle, loc.basename, .{}) catch |err| { 2569 log.err("unable to copy {s}: {s}", .{ src_path, @errorName(err) }); 2570 }; 2571 } 2572 } 2573 } 2574 } 2575 2576 fn reportMultiModuleErrors(mod: *Module) !void { 2577 // Some cases can give you a whole bunch of multi-module errors, which it's not helpful to 2578 // print all of, so we'll cap the number of these to emit. 2579 var num_errors: u32 = 0; 2580 const max_errors = 5; 2581 // Attach the "some omitted" note to the final error message 2582 var last_err: ?*Module.ErrorMsg = null; 2583 2584 for (mod.import_table.values()) |file| { 2585 if (!file.multi_pkg) continue; 2586 2587 num_errors += 1; 2588 if (num_errors > max_errors) continue; 2589 2590 const err = err_blk: { 2591 // Like with errors, let's cap the number of notes to prevent a huge error spew. 2592 const max_notes = 5; 2593 const omitted = file.references.items.len -| max_notes; 2594 const num_notes = file.references.items.len - omitted; 2595 2596 const notes = try mod.gpa.alloc(Module.ErrorMsg, if (omitted > 0) num_notes + 1 else num_notes); 2597 errdefer mod.gpa.free(notes); 2598 2599 for (notes[0..num_notes], file.references.items[0..num_notes], 0..) |*note, ref, i| { 2600 errdefer for (notes[0..i]) |*n| n.deinit(mod.gpa); 2601 note.* = switch (ref) { 2602 .import => |loc| blk: { 2603 const name = try loc.file_scope.pkg.getName(mod.gpa, mod.*); 2604 defer mod.gpa.free(name); 2605 break :blk try Module.ErrorMsg.init( 2606 mod.gpa, 2607 loc, 2608 "imported from module {s}", 2609 .{name}, 2610 ); 2611 }, 2612 .root => |pkg| blk: { 2613 const name = try pkg.getName(mod.gpa, mod.*); 2614 defer mod.gpa.free(name); 2615 break :blk try Module.ErrorMsg.init( 2616 mod.gpa, 2617 .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, 2618 "root of module {s}", 2619 .{name}, 2620 ); 2621 }, 2622 }; 2623 } 2624 errdefer for (notes[0..num_notes]) |*n| n.deinit(mod.gpa); 2625 2626 if (omitted > 0) { 2627 notes[num_notes] = try Module.ErrorMsg.init( 2628 mod.gpa, 2629 .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, 2630 "{} more references omitted", 2631 .{omitted}, 2632 ); 2633 } 2634 errdefer if (omitted > 0) notes[num_notes].deinit(mod.gpa); 2635 2636 const err = try Module.ErrorMsg.create( 2637 mod.gpa, 2638 .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, 2639 "file exists in multiple modules", 2640 .{}, 2641 ); 2642 err.notes = notes; 2643 break :err_blk err; 2644 }; 2645 errdefer err.destroy(mod.gpa); 2646 try mod.failed_files.putNoClobber(mod.gpa, file, err); 2647 last_err = err; 2648 } 2649 2650 // If we omitted any errors, add a note saying that 2651 if (num_errors > max_errors) { 2652 const err = last_err.?; 2653 2654 // There isn't really any meaningful place to put this note, so just attach it to the 2655 // last failed file 2656 var note = try Module.ErrorMsg.init( 2657 mod.gpa, 2658 err.src_loc, 2659 "{} more errors omitted", 2660 .{num_errors - max_errors}, 2661 ); 2662 errdefer note.deinit(mod.gpa); 2663 2664 const i = err.notes.len; 2665 err.notes = try mod.gpa.realloc(err.notes, i + 1); 2666 err.notes[i] = note; 2667 } 2668 2669 // Now that we've reported the errors, we need to deal with 2670 // dependencies. Any file referenced by a multi_pkg file should also be 2671 // marked multi_pkg and have its status set to astgen_failure, as it's 2672 // ambiguous which package they should be analyzed as a part of. We need 2673 // to add this flag after reporting the errors however, as otherwise 2674 // we'd get an error for every single downstream file, which wouldn't be 2675 // very useful. 2676 for (mod.import_table.values()) |file| { 2677 if (file.multi_pkg) file.recursiveMarkMultiPkg(mod); 2678 } 2679 } 2680 2681 /// Having the file open for writing is problematic as far as executing the 2682 /// binary is concerned. This will remove the write flag, or close the file, 2683 /// or whatever is needed so that it can be executed. 2684 /// After this, one must call` makeFileWritable` before calling `update`. 2685 pub fn makeBinFileExecutable(self: *Compilation) !void { 2686 return self.bin_file.makeExecutable(); 2687 } 2688 2689 pub fn makeBinFileWritable(self: *Compilation) !void { 2690 return self.bin_file.makeWritable(); 2691 } 2692 2693 /// This function is temporally single-threaded. 2694 pub fn totalErrorCount(self: *Compilation) u32 { 2695 var total: usize = self.failed_c_objects.count() + 2696 self.misc_failures.count() + 2697 @intFromBool(self.alloc_failure_occurred) + 2698 self.lld_errors.items.len; 2699 2700 for (self.failed_win32_resources.values()) |errs| { 2701 total += errs.errorMessageCount(); 2702 } 2703 2704 if (self.bin_file.options.module) |module| { 2705 total += module.failed_exports.count(); 2706 total += module.failed_embed_files.count(); 2707 2708 { 2709 var it = module.failed_files.iterator(); 2710 while (it.next()) |entry| { 2711 if (entry.value_ptr.*) |_| { 2712 total += 1; 2713 } else { 2714 const file = entry.key_ptr.*; 2715 assert(file.zir_loaded); 2716 const payload_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)]; 2717 assert(payload_index != 0); 2718 const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index); 2719 total += header.data.items_len; 2720 } 2721 } 2722 } 2723 2724 // Skip errors for Decls within files that failed parsing. 2725 // When a parse error is introduced, we keep all the semantic analysis for 2726 // the previous parse success, including compile errors, but we cannot 2727 // emit them until the file succeeds parsing. 2728 for (module.failed_decls.keys()) |key| { 2729 if (module.declFileScope(key).okToReportErrors()) { 2730 total += 1; 2731 if (module.cimport_errors.get(key)) |errors| { 2732 total += errors.len; 2733 } 2734 } 2735 } 2736 if (module.emit_h) |emit_h| { 2737 for (emit_h.failed_decls.keys()) |key| { 2738 if (module.declFileScope(key).okToReportErrors()) { 2739 total += 1; 2740 } 2741 } 2742 } 2743 } 2744 2745 // The "no entry point found" error only counts if there are no semantic analysis errors. 2746 if (total == 0) { 2747 total += @intFromBool(self.link_error_flags.no_entry_point_found); 2748 } 2749 total += @intFromBool(self.link_error_flags.missing_libc); 2750 2751 // Misc linker errors 2752 total += self.bin_file.miscErrors().len; 2753 2754 // Compile log errors only count if there are no other errors. 2755 if (total == 0) { 2756 if (self.bin_file.options.module) |module| { 2757 total += @intFromBool(module.compile_log_decls.count() != 0); 2758 } 2759 } 2760 2761 return @as(u32, @intCast(total)); 2762 } 2763 2764 /// This function is temporally single-threaded. 2765 pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { 2766 const gpa = self.gpa; 2767 2768 var bundle: ErrorBundle.Wip = undefined; 2769 try bundle.init(gpa); 2770 defer bundle.deinit(); 2771 2772 { 2773 var it = self.failed_c_objects.iterator(); 2774 while (it.next()) |entry| { 2775 const c_object = entry.key_ptr.*; 2776 const err_msg = entry.value_ptr.*; 2777 // TODO these fields will need to be adjusted when we have proper 2778 // C error reporting bubbling up. 2779 try bundle.addRootErrorMessage(.{ 2780 .msg = try bundle.printString("unable to build C object: {s}", .{err_msg.msg}), 2781 .src_loc = try bundle.addSourceLocation(.{ 2782 .src_path = try bundle.addString(c_object.src.src_path), 2783 .span_start = 0, 2784 .span_main = 0, 2785 .span_end = 1, 2786 .line = err_msg.line, 2787 .column = err_msg.column, 2788 .source_line = 0, // TODO 2789 }), 2790 }); 2791 } 2792 } 2793 2794 { 2795 var it = self.failed_win32_resources.iterator(); 2796 while (it.next()) |entry| { 2797 try bundle.addBundleAsRoots(entry.value_ptr.*); 2798 } 2799 } 2800 2801 for (self.lld_errors.items) |lld_error| { 2802 const notes_len = @as(u32, @intCast(lld_error.context_lines.len)); 2803 2804 try bundle.addRootErrorMessage(.{ 2805 .msg = try bundle.addString(lld_error.msg), 2806 .notes_len = notes_len, 2807 }); 2808 const notes_start = try bundle.reserveNotes(notes_len); 2809 for (notes_start.., lld_error.context_lines) |note, context_line| { 2810 bundle.extra.items[note] = @intFromEnum(bundle.addErrorMessageAssumeCapacity(.{ 2811 .msg = try bundle.addString(context_line), 2812 })); 2813 } 2814 } 2815 for (self.misc_failures.values()) |*value| { 2816 try bundle.addRootErrorMessage(.{ 2817 .msg = try bundle.addString(value.msg), 2818 .notes_len = if (value.children) |b| b.errorMessageCount() else 0, 2819 }); 2820 if (value.children) |b| try bundle.addBundleAsNotes(b); 2821 } 2822 if (self.alloc_failure_occurred) { 2823 try bundle.addRootErrorMessage(.{ 2824 .msg = try bundle.addString("memory allocation failure"), 2825 }); 2826 } 2827 if (self.bin_file.options.module) |module| { 2828 { 2829 var it = module.failed_files.iterator(); 2830 while (it.next()) |entry| { 2831 if (entry.value_ptr.*) |msg| { 2832 try addModuleErrorMsg(module, &bundle, msg.*); 2833 } else { 2834 // Must be ZIR errors. Note that this may include AST errors. 2835 // addZirErrorMessages asserts that the tree is loaded. 2836 _ = try entry.key_ptr.*.getTree(gpa); 2837 try addZirErrorMessages(&bundle, entry.key_ptr.*); 2838 } 2839 } 2840 } 2841 { 2842 var it = module.failed_embed_files.iterator(); 2843 while (it.next()) |entry| { 2844 const msg = entry.value_ptr.*; 2845 try addModuleErrorMsg(module, &bundle, msg.*); 2846 } 2847 } 2848 { 2849 var it = module.failed_decls.iterator(); 2850 while (it.next()) |entry| { 2851 const decl_index = entry.key_ptr.*; 2852 // Skip errors for Decls within files that had a parse failure. 2853 // We'll try again once parsing succeeds. 2854 if (module.declFileScope(decl_index).okToReportErrors()) { 2855 try addModuleErrorMsg(module, &bundle, entry.value_ptr.*.*); 2856 if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| { 2857 try bundle.addRootErrorMessage(.{ 2858 .msg = try bundle.addString(std.mem.span(c_error.msg)), 2859 .src_loc = if (c_error.path) |some| try bundle.addSourceLocation(.{ 2860 .src_path = try bundle.addString(std.mem.span(some)), 2861 .span_start = c_error.offset, 2862 .span_main = c_error.offset, 2863 .span_end = c_error.offset + 1, 2864 .line = c_error.line, 2865 .column = c_error.column, 2866 .source_line = if (c_error.source_line) |line| try bundle.addString(std.mem.span(line)) else 0, 2867 }) else .none, 2868 }); 2869 }; 2870 } 2871 } 2872 } 2873 if (module.emit_h) |emit_h| { 2874 var it = emit_h.failed_decls.iterator(); 2875 while (it.next()) |entry| { 2876 const decl_index = entry.key_ptr.*; 2877 // Skip errors for Decls within files that had a parse failure. 2878 // We'll try again once parsing succeeds. 2879 if (module.declFileScope(decl_index).okToReportErrors()) { 2880 try addModuleErrorMsg(module, &bundle, entry.value_ptr.*.*); 2881 } 2882 } 2883 } 2884 for (module.failed_exports.values()) |value| { 2885 try addModuleErrorMsg(module, &bundle, value.*); 2886 } 2887 } 2888 2889 if (bundle.root_list.items.len == 0) { 2890 if (self.link_error_flags.no_entry_point_found) { 2891 try bundle.addRootErrorMessage(.{ 2892 .msg = try bundle.addString("no entry point found"), 2893 }); 2894 } 2895 } 2896 2897 if (self.link_error_flags.missing_libc) { 2898 try bundle.addRootErrorMessage(.{ 2899 .msg = try bundle.addString("libc not available"), 2900 .notes_len = 2, 2901 }); 2902 const notes_start = try bundle.reserveNotes(2); 2903 bundle.extra.items[notes_start + 0] = @intFromEnum(try bundle.addErrorMessage(.{ 2904 .msg = try bundle.addString("run 'zig libc -h' to learn about libc installations"), 2905 })); 2906 bundle.extra.items[notes_start + 1] = @intFromEnum(try bundle.addErrorMessage(.{ 2907 .msg = try bundle.addString("run 'zig targets' to see the targets for which zig can always provide libc"), 2908 })); 2909 } 2910 2911 for (self.bin_file.miscErrors()) |link_err| { 2912 try bundle.addRootErrorMessage(.{ 2913 .msg = try bundle.addString(link_err.msg), 2914 .notes_len = @intCast(link_err.notes.len), 2915 }); 2916 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len)); 2917 for (link_err.notes, 0..) |note, i| { 2918 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{ 2919 .msg = try bundle.addString(note.msg), 2920 })); 2921 } 2922 } 2923 2924 if (self.bin_file.options.module) |module| { 2925 if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) { 2926 const keys = module.compile_log_decls.keys(); 2927 const values = module.compile_log_decls.values(); 2928 // First one will be the error; subsequent ones will be notes. 2929 const err_decl = module.declPtr(keys[0]); 2930 const src_loc = err_decl.nodeOffsetSrcLoc(values[0], module); 2931 const err_msg = Module.ErrorMsg{ 2932 .src_loc = src_loc, 2933 .msg = "found compile log statement", 2934 .notes = try gpa.alloc(Module.ErrorMsg, module.compile_log_decls.count() - 1), 2935 }; 2936 defer gpa.free(err_msg.notes); 2937 2938 for (keys[1..], 0..) |key, i| { 2939 const note_decl = module.declPtr(key); 2940 err_msg.notes[i] = .{ 2941 .src_loc = note_decl.nodeOffsetSrcLoc(values[i + 1], module), 2942 .msg = "also here", 2943 }; 2944 } 2945 2946 try addModuleErrorMsg(module, &bundle, err_msg); 2947 } 2948 } 2949 2950 assert(self.totalErrorCount() == bundle.root_list.items.len); 2951 2952 const compile_log_text = if (self.bin_file.options.module) |m| m.compile_log_text.items else ""; 2953 return bundle.toOwnedBundle(compile_log_text); 2954 } 2955 2956 pub const ErrorNoteHashContext = struct { 2957 eb: *const ErrorBundle.Wip, 2958 2959 pub fn hash(ctx: ErrorNoteHashContext, key: ErrorBundle.ErrorMessage) u32 { 2960 var hasher = std.hash.Wyhash.init(0); 2961 const eb = ctx.eb.tmpBundle(); 2962 2963 hasher.update(eb.nullTerminatedString(key.msg)); 2964 if (key.src_loc != .none) { 2965 const src = eb.getSourceLocation(key.src_loc); 2966 hasher.update(eb.nullTerminatedString(src.src_path)); 2967 std.hash.autoHash(&hasher, src.line); 2968 std.hash.autoHash(&hasher, src.column); 2969 std.hash.autoHash(&hasher, src.span_main); 2970 } 2971 2972 return @as(u32, @truncate(hasher.final())); 2973 } 2974 2975 pub fn eql( 2976 ctx: ErrorNoteHashContext, 2977 a: ErrorBundle.ErrorMessage, 2978 b: ErrorBundle.ErrorMessage, 2979 b_index: usize, 2980 ) bool { 2981 _ = b_index; 2982 const eb = ctx.eb.tmpBundle(); 2983 const msg_a = eb.nullTerminatedString(a.msg); 2984 const msg_b = eb.nullTerminatedString(b.msg); 2985 if (!std.mem.eql(u8, msg_a, msg_b)) return false; 2986 2987 if (a.src_loc == .none and b.src_loc == .none) return true; 2988 if (a.src_loc == .none or b.src_loc == .none) return false; 2989 const src_a = eb.getSourceLocation(a.src_loc); 2990 const src_b = eb.getSourceLocation(b.src_loc); 2991 2992 const src_path_a = eb.nullTerminatedString(src_a.src_path); 2993 const src_path_b = eb.nullTerminatedString(src_b.src_path); 2994 2995 return std.mem.eql(u8, src_path_a, src_path_b) and 2996 src_a.line == src_b.line and 2997 src_a.column == src_b.column and 2998 src_a.span_main == src_b.span_main; 2999 } 3000 }; 3001 3002 pub fn addModuleErrorMsg(mod: *Module, eb: *ErrorBundle.Wip, module_err_msg: Module.ErrorMsg) !void { 3003 const gpa = eb.gpa; 3004 const ip = &mod.intern_pool; 3005 const err_source = module_err_msg.src_loc.file_scope.getSource(gpa) catch |err| { 3006 const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa); 3007 defer gpa.free(file_path); 3008 try eb.addRootErrorMessage(.{ 3009 .msg = try eb.printString("unable to load '{s}': {s}", .{ 3010 file_path, @errorName(err), 3011 }), 3012 }); 3013 return; 3014 }; 3015 const err_span = try module_err_msg.src_loc.span(gpa); 3016 const err_loc = std.zig.findLineColumn(err_source.bytes, err_span.main); 3017 const file_path = try module_err_msg.src_loc.file_scope.fullPath(gpa); 3018 defer gpa.free(file_path); 3019 3020 var ref_traces: std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace) = .{}; 3021 defer ref_traces.deinit(gpa); 3022 3023 const remaining_references: ?u32 = remaining: { 3024 if (mod.comp.reference_trace) |_| { 3025 if (module_err_msg.hidden_references > 0) break :remaining module_err_msg.hidden_references; 3026 } else { 3027 if (module_err_msg.reference_trace.len > 0) break :remaining 0; 3028 } 3029 break :remaining null; 3030 }; 3031 try ref_traces.ensureTotalCapacityPrecise(gpa, module_err_msg.reference_trace.len + 3032 @intFromBool(remaining_references != null)); 3033 3034 for (module_err_msg.reference_trace) |module_reference| { 3035 const source = try module_reference.src_loc.file_scope.getSource(gpa); 3036 const span = try module_reference.src_loc.span(gpa); 3037 const loc = std.zig.findLineColumn(source.bytes, span.main); 3038 const rt_file_path = try module_reference.src_loc.file_scope.fullPath(gpa); 3039 defer gpa.free(rt_file_path); 3040 ref_traces.appendAssumeCapacity(.{ 3041 .decl_name = try eb.addString(ip.stringToSlice(module_reference.decl)), 3042 .src_loc = try eb.addSourceLocation(.{ 3043 .src_path = try eb.addString(rt_file_path), 3044 .span_start = span.start, 3045 .span_main = span.main, 3046 .span_end = span.end, 3047 .line = @intCast(loc.line), 3048 .column = @intCast(loc.column), 3049 .source_line = 0, 3050 }), 3051 }); 3052 } 3053 if (remaining_references) |remaining| ref_traces.appendAssumeCapacity( 3054 .{ .decl_name = remaining, .src_loc = .none }, 3055 ); 3056 3057 const src_loc = try eb.addSourceLocation(.{ 3058 .src_path = try eb.addString(file_path), 3059 .span_start = err_span.start, 3060 .span_main = err_span.main, 3061 .span_end = err_span.end, 3062 .line = @intCast(err_loc.line), 3063 .column = @intCast(err_loc.column), 3064 .source_line = if (module_err_msg.src_loc.lazy == .entire_file) 3065 0 3066 else 3067 try eb.addString(err_loc.source_line), 3068 .reference_trace_len = @intCast(ref_traces.items.len), 3069 }); 3070 3071 for (ref_traces.items) |rt| { 3072 try eb.addReferenceTrace(rt); 3073 } 3074 3075 // De-duplicate error notes. The main use case in mind for this is 3076 // too many "note: called from here" notes when eval branch quota is reached. 3077 var notes: std.ArrayHashMapUnmanaged(ErrorBundle.ErrorMessage, void, ErrorNoteHashContext, true) = .{}; 3078 defer notes.deinit(gpa); 3079 3080 for (module_err_msg.notes) |module_note| { 3081 const source = try module_note.src_loc.file_scope.getSource(gpa); 3082 const span = try module_note.src_loc.span(gpa); 3083 const loc = std.zig.findLineColumn(source.bytes, span.main); 3084 const note_file_path = try module_note.src_loc.file_scope.fullPath(gpa); 3085 defer gpa.free(note_file_path); 3086 3087 const gop = try notes.getOrPutContext(gpa, .{ 3088 .msg = try eb.addString(module_note.msg), 3089 .src_loc = try eb.addSourceLocation(.{ 3090 .src_path = try eb.addString(note_file_path), 3091 .span_start = span.start, 3092 .span_main = span.main, 3093 .span_end = span.end, 3094 .line = @intCast(loc.line), 3095 .column = @intCast(loc.column), 3096 .source_line = if (err_loc.eql(loc)) 0 else try eb.addString(loc.source_line), 3097 }), 3098 }, .{ .eb = eb }); 3099 if (gop.found_existing) { 3100 gop.key_ptr.count += 1; 3101 } 3102 } 3103 3104 const notes_len: u32 = @intCast(notes.entries.len); 3105 3106 try eb.addRootErrorMessage(.{ 3107 .msg = try eb.addString(module_err_msg.msg), 3108 .src_loc = src_loc, 3109 .notes_len = notes_len, 3110 }); 3111 3112 const notes_start = try eb.reserveNotes(notes_len); 3113 3114 for (notes_start.., notes.keys()) |i, note| { 3115 eb.extra.items[i] = @intFromEnum(try eb.addErrorMessage(note)); 3116 } 3117 } 3118 3119 pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void { 3120 assert(file.zir_loaded); 3121 assert(file.tree_loaded); 3122 assert(file.source_loaded); 3123 const payload_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)]; 3124 assert(payload_index != 0); 3125 const gpa = eb.gpa; 3126 3127 const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index); 3128 const items_len = header.data.items_len; 3129 var extra_index = header.end; 3130 for (0..items_len) |_| { 3131 const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index); 3132 extra_index = item.end; 3133 const err_span = blk: { 3134 if (item.data.node != 0) { 3135 break :blk Module.SrcLoc.nodeToSpan(&file.tree, item.data.node); 3136 } 3137 const token_starts = file.tree.tokens.items(.start); 3138 const start = token_starts[item.data.token] + item.data.byte_offset; 3139 const end = start + @as(u32, @intCast(file.tree.tokenSlice(item.data.token).len)) - item.data.byte_offset; 3140 break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; 3141 }; 3142 const err_loc = std.zig.findLineColumn(file.source, err_span.main); 3143 3144 { 3145 const msg = file.zir.nullTerminatedString(item.data.msg); 3146 const src_path = try file.fullPath(gpa); 3147 defer gpa.free(src_path); 3148 try eb.addRootErrorMessage(.{ 3149 .msg = try eb.addString(msg), 3150 .src_loc = try eb.addSourceLocation(.{ 3151 .src_path = try eb.addString(src_path), 3152 .span_start = err_span.start, 3153 .span_main = err_span.main, 3154 .span_end = err_span.end, 3155 .line = @as(u32, @intCast(err_loc.line)), 3156 .column = @as(u32, @intCast(err_loc.column)), 3157 .source_line = try eb.addString(err_loc.source_line), 3158 }), 3159 .notes_len = item.data.notesLen(file.zir), 3160 }); 3161 } 3162 3163 if (item.data.notes != 0) { 3164 const notes_start = try eb.reserveNotes(item.data.notes); 3165 const block = file.zir.extraData(Zir.Inst.Block, item.data.notes); 3166 const body = file.zir.extra[block.end..][0..block.data.body_len]; 3167 for (notes_start.., body) |note_i, body_elem| { 3168 const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body_elem); 3169 const msg = file.zir.nullTerminatedString(note_item.data.msg); 3170 const span = blk: { 3171 if (note_item.data.node != 0) { 3172 break :blk Module.SrcLoc.nodeToSpan(&file.tree, note_item.data.node); 3173 } 3174 const token_starts = file.tree.tokens.items(.start); 3175 const start = token_starts[note_item.data.token] + note_item.data.byte_offset; 3176 const end = start + @as(u32, @intCast(file.tree.tokenSlice(note_item.data.token).len)) - item.data.byte_offset; 3177 break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start }; 3178 }; 3179 const loc = std.zig.findLineColumn(file.source, span.main); 3180 const src_path = try file.fullPath(gpa); 3181 defer gpa.free(src_path); 3182 3183 eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ 3184 .msg = try eb.addString(msg), 3185 .src_loc = try eb.addSourceLocation(.{ 3186 .src_path = try eb.addString(src_path), 3187 .span_start = span.start, 3188 .span_main = span.main, 3189 .span_end = span.end, 3190 .line = @as(u32, @intCast(loc.line)), 3191 .column = @as(u32, @intCast(loc.column)), 3192 .source_line = if (loc.eql(err_loc)) 3193 0 3194 else 3195 try eb.addString(loc.source_line), 3196 }), 3197 .notes_len = 0, // TODO rework this function to be recursive 3198 })); 3199 } 3200 } 3201 } 3202 } 3203 3204 pub fn performAllTheWork( 3205 comp: *Compilation, 3206 main_progress_node: *std.Progress.Node, 3207 ) error{ TimerUnsupported, OutOfMemory }!void { 3208 // Here we queue up all the AstGen tasks first, followed by C object compilation. 3209 // We wait until the AstGen tasks are all completed before proceeding to the 3210 // (at least for now) single-threaded main work queue. However, C object compilation 3211 // only needs to be finished by the end of this function. 3212 3213 var zir_prog_node = main_progress_node.start("AST Lowering", 0); 3214 defer zir_prog_node.end(); 3215 3216 var c_obj_prog_node = main_progress_node.start("Compile C Objects", comp.c_source_files.len); 3217 defer c_obj_prog_node.end(); 3218 3219 var win32_resource_prog_node = main_progress_node.start("Compile Win32 Resources", comp.rc_source_files.len); 3220 defer win32_resource_prog_node.end(); 3221 3222 var embed_file_prog_node = main_progress_node.start("Detect @embedFile updates", comp.embed_file_work_queue.count); 3223 defer embed_file_prog_node.end(); 3224 3225 comp.work_queue_wait_group.reset(); 3226 defer comp.work_queue_wait_group.wait(); 3227 3228 { 3229 const astgen_frame = tracy.namedFrame("astgen"); 3230 defer astgen_frame.end(); 3231 3232 comp.astgen_wait_group.reset(); 3233 defer comp.astgen_wait_group.wait(); 3234 3235 // builtin.zig is handled specially for two reasons: 3236 // 1. to avoid race condition of zig processes truncating each other's builtin.zig files 3237 // 2. optimization; in the hot path it only incurs a stat() syscall, which happens 3238 // in the `astgen_wait_group`. 3239 if (comp.bin_file.options.module) |mod| { 3240 if (mod.job_queued_update_builtin_zig) { 3241 mod.job_queued_update_builtin_zig = false; 3242 3243 comp.astgen_wait_group.start(); 3244 try comp.thread_pool.spawn(workerUpdateBuiltinZigFile, .{ 3245 comp, mod, &comp.astgen_wait_group, 3246 }); 3247 } 3248 } 3249 3250 while (comp.astgen_work_queue.readItem()) |file| { 3251 comp.astgen_wait_group.start(); 3252 try comp.thread_pool.spawn(workerAstGenFile, .{ 3253 comp, file, &zir_prog_node, &comp.astgen_wait_group, .root, 3254 }); 3255 } 3256 3257 while (comp.embed_file_work_queue.readItem()) |embed_file| { 3258 comp.astgen_wait_group.start(); 3259 try comp.thread_pool.spawn(workerCheckEmbedFile, .{ 3260 comp, embed_file, &embed_file_prog_node, &comp.astgen_wait_group, 3261 }); 3262 } 3263 3264 while (comp.c_object_work_queue.readItem()) |c_object| { 3265 comp.work_queue_wait_group.start(); 3266 try comp.thread_pool.spawn(workerUpdateCObject, .{ 3267 comp, c_object, &c_obj_prog_node, &comp.work_queue_wait_group, 3268 }); 3269 } 3270 3271 while (comp.win32_resource_work_queue.readItem()) |win32_resource| { 3272 comp.work_queue_wait_group.start(); 3273 try comp.thread_pool.spawn(workerUpdateWin32Resource, .{ 3274 comp, win32_resource, &win32_resource_prog_node, &comp.work_queue_wait_group, 3275 }); 3276 } 3277 } 3278 3279 if (comp.bin_file.options.module) |mod| { 3280 try reportMultiModuleErrors(mod); 3281 } 3282 3283 { 3284 const outdated_and_deleted_decls_frame = tracy.namedFrame("outdated_and_deleted_decls"); 3285 defer outdated_and_deleted_decls_frame.end(); 3286 3287 // Iterate over all the files and look for outdated and deleted declarations. 3288 if (comp.bin_file.options.module) |mod| { 3289 try mod.processOutdatedAndDeletedDecls(); 3290 } 3291 } 3292 3293 if (comp.bin_file.options.module) |mod| { 3294 mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); 3295 mod.sema_prog_node.activate(); 3296 } 3297 defer if (comp.bin_file.options.module) |mod| { 3298 mod.sema_prog_node.end(); 3299 mod.sema_prog_node = undefined; 3300 }; 3301 3302 // In this main loop we give priority to non-anonymous Decls in the work queue, so 3303 // that they can establish references to anonymous Decls, setting alive=true in the 3304 // backend, preventing anonymous Decls from being prematurely destroyed. 3305 while (true) { 3306 if (comp.work_queue.readItem()) |work_item| { 3307 try processOneJob(comp, work_item, main_progress_node); 3308 continue; 3309 } 3310 if (comp.anon_work_queue.readItem()) |work_item| { 3311 try processOneJob(comp, work_item, main_progress_node); 3312 continue; 3313 } 3314 break; 3315 } 3316 3317 if (comp.job_queued_compiler_rt_lib) { 3318 comp.job_queued_compiler_rt_lib = false; 3319 buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib, main_progress_node); 3320 } 3321 3322 if (comp.job_queued_compiler_rt_obj) { 3323 comp.job_queued_compiler_rt_obj = false; 3324 buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj, main_progress_node); 3325 } 3326 } 3327 3328 fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !void { 3329 switch (job) { 3330 .codegen_decl => |decl_index| { 3331 const module = comp.bin_file.options.module.?; 3332 const decl = module.declPtr(decl_index); 3333 3334 switch (decl.analysis) { 3335 .unreferenced => unreachable, 3336 .in_progress => unreachable, 3337 .outdated => unreachable, 3338 3339 .file_failure, 3340 .sema_failure, 3341 .liveness_failure, 3342 .codegen_failure, 3343 .dependency_failure, 3344 .sema_failure_retryable, 3345 => return, 3346 3347 .complete, .codegen_failure_retryable => { 3348 const named_frame = tracy.namedFrame("codegen_decl"); 3349 defer named_frame.end(); 3350 3351 assert(decl.has_tv); 3352 3353 if (decl.alive) { 3354 try module.linkerUpdateDecl(decl_index); 3355 return; 3356 } 3357 3358 // Instead of sending this decl to the linker, we actually will delete it 3359 // because we found out that it in fact was never referenced. 3360 module.deleteUnusedDecl(decl_index); 3361 return; 3362 }, 3363 } 3364 }, 3365 .codegen_func => |func| { 3366 const named_frame = tracy.namedFrame("codegen_func"); 3367 defer named_frame.end(); 3368 3369 const module = comp.bin_file.options.module.?; 3370 module.ensureFuncBodyAnalyzed(func) catch |err| switch (err) { 3371 error.OutOfMemory => return error.OutOfMemory, 3372 error.AnalysisFail => return, 3373 }; 3374 }, 3375 .emit_h_decl => |decl_index| { 3376 const module = comp.bin_file.options.module.?; 3377 const decl = module.declPtr(decl_index); 3378 3379 switch (decl.analysis) { 3380 .unreferenced => unreachable, 3381 .in_progress => unreachable, 3382 .outdated => unreachable, 3383 3384 .file_failure, 3385 .sema_failure, 3386 .dependency_failure, 3387 .sema_failure_retryable, 3388 => return, 3389 3390 // emit-h only requires semantic analysis of the Decl to be complete, 3391 // it does not depend on machine code generation to succeed. 3392 .liveness_failure, .codegen_failure, .codegen_failure_retryable, .complete => { 3393 const named_frame = tracy.namedFrame("emit_h_decl"); 3394 defer named_frame.end(); 3395 3396 const gpa = comp.gpa; 3397 const emit_h = module.emit_h.?; 3398 _ = try emit_h.decl_table.getOrPut(gpa, decl_index); 3399 const decl_emit_h = emit_h.declPtr(decl_index); 3400 const fwd_decl = &decl_emit_h.fwd_decl; 3401 fwd_decl.shrinkRetainingCapacity(0); 3402 var ctypes_arena = std.heap.ArenaAllocator.init(gpa); 3403 defer ctypes_arena.deinit(); 3404 3405 var dg: c_codegen.DeclGen = .{ 3406 .gpa = gpa, 3407 .module = module, 3408 .error_msg = null, 3409 .decl_index = decl_index.toOptional(), 3410 .decl = decl, 3411 .fwd_decl = fwd_decl.toManaged(gpa), 3412 .ctypes = .{}, 3413 }; 3414 defer { 3415 dg.ctypes.deinit(gpa); 3416 dg.fwd_decl.deinit(); 3417 } 3418 3419 c_codegen.genHeader(&dg) catch |err| switch (err) { 3420 error.AnalysisFail => { 3421 try emit_h.failed_decls.put(gpa, decl_index, dg.error_msg.?); 3422 return; 3423 }, 3424 else => |e| return e, 3425 }; 3426 3427 fwd_decl.* = dg.fwd_decl.moveToUnmanaged(); 3428 fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len); 3429 }, 3430 } 3431 }, 3432 .analyze_decl => |decl_index| { 3433 const module = comp.bin_file.options.module.?; 3434 module.ensureDeclAnalyzed(decl_index) catch |err| switch (err) { 3435 error.OutOfMemory => return error.OutOfMemory, 3436 error.AnalysisFail => return, 3437 }; 3438 const decl = module.declPtr(decl_index); 3439 if (decl.kind == .@"test" and comp.bin_file.options.is_test) { 3440 // Tests are always emitted in test binaries. The decl_refs are created by 3441 // Module.populateTestFunctions, but this will not queue body analysis, so do 3442 // that now. 3443 try module.ensureFuncBodyAnalysisQueued(decl.val.toIntern()); 3444 } 3445 }, 3446 .update_embed_file => |embed_file| { 3447 const named_frame = tracy.namedFrame("update_embed_file"); 3448 defer named_frame.end(); 3449 3450 const module = comp.bin_file.options.module.?; 3451 module.updateEmbedFile(embed_file) catch |err| switch (err) { 3452 error.OutOfMemory => return error.OutOfMemory, 3453 error.AnalysisFail => return, 3454 }; 3455 }, 3456 .update_line_number => |decl_index| { 3457 const named_frame = tracy.namedFrame("update_line_number"); 3458 defer named_frame.end(); 3459 3460 const gpa = comp.gpa; 3461 const module = comp.bin_file.options.module.?; 3462 const decl = module.declPtr(decl_index); 3463 comp.bin_file.updateDeclLineNumber(module, decl_index) catch |err| { 3464 try module.failed_decls.ensureUnusedCapacity(gpa, 1); 3465 module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create( 3466 gpa, 3467 decl.srcLoc(module), 3468 "unable to update line number: {s}", 3469 .{@errorName(err)}, 3470 )); 3471 decl.analysis = .codegen_failure_retryable; 3472 }; 3473 }, 3474 .analyze_pkg => |pkg| { 3475 const named_frame = tracy.namedFrame("analyze_pkg"); 3476 defer named_frame.end(); 3477 3478 const module = comp.bin_file.options.module.?; 3479 module.semaPkg(pkg) catch |err| switch (err) { 3480 error.OutOfMemory => return error.OutOfMemory, 3481 error.AnalysisFail => return, 3482 }; 3483 }, 3484 .glibc_crt_file => |crt_file| { 3485 const named_frame = tracy.namedFrame("glibc_crt_file"); 3486 defer named_frame.end(); 3487 3488 glibc.buildCRTFile(comp, crt_file, prog_node) catch |err| { 3489 // TODO Surface more error details. 3490 comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ 3491 @errorName(err), 3492 }); 3493 }; 3494 }, 3495 .glibc_shared_objects => { 3496 const named_frame = tracy.namedFrame("glibc_shared_objects"); 3497 defer named_frame.end(); 3498 3499 glibc.buildSharedObjects(comp, prog_node) catch |err| { 3500 // TODO Surface more error details. 3501 comp.lockAndSetMiscFailure( 3502 .glibc_shared_objects, 3503 "unable to build glibc shared objects: {s}", 3504 .{@errorName(err)}, 3505 ); 3506 }; 3507 }, 3508 .musl_crt_file => |crt_file| { 3509 const named_frame = tracy.namedFrame("musl_crt_file"); 3510 defer named_frame.end(); 3511 3512 musl.buildCRTFile(comp, crt_file, prog_node) catch |err| { 3513 // TODO Surface more error details. 3514 comp.lockAndSetMiscFailure( 3515 .musl_crt_file, 3516 "unable to build musl CRT file: {s}", 3517 .{@errorName(err)}, 3518 ); 3519 }; 3520 }, 3521 .mingw_crt_file => |crt_file| { 3522 const named_frame = tracy.namedFrame("mingw_crt_file"); 3523 defer named_frame.end(); 3524 3525 mingw.buildCRTFile(comp, crt_file, prog_node) catch |err| { 3526 // TODO Surface more error details. 3527 comp.lockAndSetMiscFailure( 3528 .mingw_crt_file, 3529 "unable to build mingw-w64 CRT file {s}: {s}", 3530 .{ @tagName(crt_file), @errorName(err) }, 3531 ); 3532 }; 3533 }, 3534 .windows_import_lib => |index| { 3535 const named_frame = tracy.namedFrame("windows_import_lib"); 3536 defer named_frame.end(); 3537 3538 const link_lib = comp.bin_file.options.system_libs.keys()[index]; 3539 mingw.buildImportLib(comp, link_lib) catch |err| { 3540 // TODO Surface more error details. 3541 comp.lockAndSetMiscFailure( 3542 .windows_import_lib, 3543 "unable to generate DLL import .lib file for {s}: {s}", 3544 .{ link_lib, @errorName(err) }, 3545 ); 3546 }; 3547 }, 3548 .libunwind => { 3549 const named_frame = tracy.namedFrame("libunwind"); 3550 defer named_frame.end(); 3551 3552 libunwind.buildStaticLib(comp, prog_node) catch |err| { 3553 // TODO Surface more error details. 3554 comp.lockAndSetMiscFailure( 3555 .libunwind, 3556 "unable to build libunwind: {s}", 3557 .{@errorName(err)}, 3558 ); 3559 }; 3560 }, 3561 .libcxx => { 3562 const named_frame = tracy.namedFrame("libcxx"); 3563 defer named_frame.end(); 3564 3565 libcxx.buildLibCXX(comp, prog_node) catch |err| { 3566 // TODO Surface more error details. 3567 comp.lockAndSetMiscFailure( 3568 .libcxx, 3569 "unable to build libcxx: {s}", 3570 .{@errorName(err)}, 3571 ); 3572 }; 3573 }, 3574 .libcxxabi => { 3575 const named_frame = tracy.namedFrame("libcxxabi"); 3576 defer named_frame.end(); 3577 3578 libcxx.buildLibCXXABI(comp, prog_node) catch |err| { 3579 // TODO Surface more error details. 3580 comp.lockAndSetMiscFailure( 3581 .libcxxabi, 3582 "unable to build libcxxabi: {s}", 3583 .{@errorName(err)}, 3584 ); 3585 }; 3586 }, 3587 .libtsan => { 3588 const named_frame = tracy.namedFrame("libtsan"); 3589 defer named_frame.end(); 3590 3591 libtsan.buildTsan(comp, prog_node) catch |err| { 3592 // TODO Surface more error details. 3593 comp.lockAndSetMiscFailure( 3594 .libtsan, 3595 "unable to build TSAN library: {s}", 3596 .{@errorName(err)}, 3597 ); 3598 }; 3599 }, 3600 .wasi_libc_crt_file => |crt_file| { 3601 const named_frame = tracy.namedFrame("wasi_libc_crt_file"); 3602 defer named_frame.end(); 3603 3604 wasi_libc.buildCRTFile(comp, crt_file, prog_node) catch |err| { 3605 // TODO Surface more error details. 3606 comp.lockAndSetMiscFailure( 3607 .wasi_libc_crt_file, 3608 "unable to build WASI libc CRT file: {s}", 3609 .{@errorName(err)}, 3610 ); 3611 }; 3612 }, 3613 .libssp => { 3614 const named_frame = tracy.namedFrame("libssp"); 3615 defer named_frame.end(); 3616 3617 comp.buildOutputFromZig( 3618 "ssp.zig", 3619 .Lib, 3620 &comp.libssp_static_lib, 3621 .libssp, 3622 prog_node, 3623 ) catch |err| switch (err) { 3624 error.OutOfMemory => return error.OutOfMemory, 3625 error.SubCompilationFailed => return, // error reported already 3626 else => comp.lockAndSetMiscFailure( 3627 .libssp, 3628 "unable to build libssp: {s}", 3629 .{@errorName(err)}, 3630 ), 3631 }; 3632 }, 3633 .zig_libc => { 3634 const named_frame = tracy.namedFrame("zig_libc"); 3635 defer named_frame.end(); 3636 3637 comp.buildOutputFromZig( 3638 "c.zig", 3639 .Lib, 3640 &comp.libc_static_lib, 3641 .zig_libc, 3642 prog_node, 3643 ) catch |err| switch (err) { 3644 error.OutOfMemory => return error.OutOfMemory, 3645 error.SubCompilationFailed => return, // error reported already 3646 else => comp.lockAndSetMiscFailure( 3647 .zig_libc, 3648 "unable to build zig's multitarget libc: {s}", 3649 .{@errorName(err)}, 3650 ), 3651 }; 3652 }, 3653 } 3654 } 3655 3656 const AstGenSrc = union(enum) { 3657 root, 3658 import: struct { 3659 importing_file: *Module.File, 3660 import_tok: std.zig.Ast.TokenIndex, 3661 }, 3662 }; 3663 3664 fn workerAstGenFile( 3665 comp: *Compilation, 3666 file: *Module.File, 3667 prog_node: *std.Progress.Node, 3668 wg: *WaitGroup, 3669 src: AstGenSrc, 3670 ) void { 3671 defer wg.finish(); 3672 3673 var child_prog_node = prog_node.start(file.sub_file_path, 0); 3674 child_prog_node.activate(); 3675 defer child_prog_node.end(); 3676 3677 const mod = comp.bin_file.options.module.?; 3678 mod.astGenFile(file) catch |err| switch (err) { 3679 error.AnalysisFail => return, 3680 else => { 3681 file.status = .retryable_failure; 3682 comp.reportRetryableAstGenError(src, file, err) catch |oom| switch (oom) { 3683 // Swallowing this error is OK because it's implied to be OOM when 3684 // there is a missing `failed_files` error message. 3685 error.OutOfMemory => {}, 3686 }; 3687 return; 3688 }, 3689 }; 3690 3691 // Pre-emptively look for `@import` paths and queue them up. 3692 // If we experience an error preemptively fetching the 3693 // file, just ignore it and let it happen again later during Sema. 3694 assert(file.zir_loaded); 3695 const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)]; 3696 if (imports_index != 0) { 3697 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index); 3698 var import_i: u32 = 0; 3699 var extra_index = extra.end; 3700 3701 while (import_i < extra.data.imports_len) : (import_i += 1) { 3702 const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index); 3703 extra_index = item.end; 3704 3705 const import_path = file.zir.nullTerminatedString(item.data.name); 3706 // `@import("builtin")` is handled specially. 3707 if (mem.eql(u8, import_path, "builtin")) continue; 3708 3709 const import_result = blk: { 3710 comp.mutex.lock(); 3711 defer comp.mutex.unlock(); 3712 3713 const res = mod.importFile(file, import_path) catch continue; 3714 if (!res.is_pkg) { 3715 res.file.addReference(mod.*, .{ .import = .{ 3716 .file_scope = file, 3717 .parent_decl_node = 0, 3718 .lazy = .{ .token_abs = item.data.token }, 3719 } }) catch continue; 3720 } 3721 break :blk res; 3722 }; 3723 if (import_result.is_new) { 3724 log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{ 3725 file.sub_file_path, import_path, import_result.file.sub_file_path, 3726 }); 3727 const sub_src: AstGenSrc = .{ .import = .{ 3728 .importing_file = file, 3729 .import_tok = item.data.token, 3730 } }; 3731 wg.start(); 3732 comp.thread_pool.spawn(workerAstGenFile, .{ 3733 comp, import_result.file, prog_node, wg, sub_src, 3734 }) catch { 3735 wg.finish(); 3736 continue; 3737 }; 3738 } 3739 } 3740 } 3741 } 3742 3743 fn workerUpdateBuiltinZigFile( 3744 comp: *Compilation, 3745 mod: *Module, 3746 wg: *WaitGroup, 3747 ) void { 3748 defer wg.finish(); 3749 3750 mod.populateBuiltinFile() catch |err| { 3751 const dir_path: []const u8 = mod.zig_cache_artifact_directory.path orelse "."; 3752 3753 comp.mutex.lock(); 3754 defer comp.mutex.unlock(); 3755 3756 comp.setMiscFailure(.write_builtin_zig, "unable to write builtin.zig to {s}: {s}", .{ 3757 dir_path, @errorName(err), 3758 }); 3759 }; 3760 } 3761 3762 fn workerCheckEmbedFile( 3763 comp: *Compilation, 3764 embed_file: *Module.EmbedFile, 3765 prog_node: *std.Progress.Node, 3766 wg: *WaitGroup, 3767 ) void { 3768 defer wg.finish(); 3769 3770 var child_prog_node = prog_node.start(embed_file.sub_file_path, 0); 3771 child_prog_node.activate(); 3772 defer child_prog_node.end(); 3773 3774 const mod = comp.bin_file.options.module.?; 3775 mod.detectEmbedFileUpdate(embed_file) catch |err| { 3776 comp.reportRetryableEmbedFileError(embed_file, err) catch |oom| switch (oom) { 3777 // Swallowing this error is OK because it's implied to be OOM when 3778 // there is a missing `failed_embed_files` error message. 3779 error.OutOfMemory => {}, 3780 }; 3781 return; 3782 }; 3783 } 3784 3785 pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest { 3786 var man = comp.cache_parent.obtain(); 3787 3788 // Only things that need to be added on top of the base hash, and only things 3789 // that apply both to @cImport and compiling C objects. No linking stuff here! 3790 // Also nothing that applies only to compiling .zig code. 3791 man.hash.add(comp.sanitize_c); 3792 man.hash.addListOfBytes(comp.clang_argv); 3793 man.hash.add(comp.bin_file.options.link_libcpp); 3794 3795 // When libc_installation is null it means that Zig generated this dir list 3796 // based on the zig library directory alone. The zig lib directory file 3797 // path is purposefully either in the cache or not in the cache. The 3798 // decision should not be overridden here. 3799 if (comp.bin_file.options.libc_installation != null) { 3800 man.hash.addListOfBytes(comp.libc_include_dir_list); 3801 } 3802 3803 return man; 3804 } 3805 3806 pub fn obtainWin32ResourceCacheManifest(comp: *const Compilation) Cache.Manifest { 3807 var man = comp.cache_parent.obtain(); 3808 3809 man.hash.addListOfBytes(comp.rc_include_dir_list); 3810 3811 return man; 3812 } 3813 3814 test "cImport" { 3815 _ = cImport; 3816 } 3817 3818 const CImportResult = struct { 3819 out_zig_path: []u8, 3820 errors: []clang.ErrorMsg, 3821 }; 3822 3823 /// Caller owns returned memory. 3824 /// This API is currently coupled pretty tightly to stage1's needs; it will need to be reworked 3825 /// a bit when we want to start using it from self-hosted. 3826 pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { 3827 if (!build_options.have_llvm) 3828 return error.ZigCompilerNotBuiltWithLLVMExtensions; 3829 3830 const tracy_trace = trace(@src()); 3831 defer tracy_trace.end(); 3832 3833 const cimport_zig_basename = "cimport.zig"; 3834 3835 var man = comp.obtainCObjectCacheManifest(); 3836 defer man.deinit(); 3837 3838 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects 3839 man.hash.addBytes(c_src); 3840 3841 // If the previous invocation resulted in clang errors, we will see a hit 3842 // here with 0 files in the manifest, in which case it is actually a miss. 3843 // We need to "unhit" in this case, to keep the digests matching. 3844 const prev_hash_state = man.hash.peekBin(); 3845 const actual_hit = hit: { 3846 _ = try man.hit(); 3847 if (man.files.items.len == 0) { 3848 man.unhit(prev_hash_state, 0); 3849 break :hit false; 3850 } 3851 break :hit true; 3852 }; 3853 const digest = if (!actual_hit) digest: { 3854 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); 3855 defer arena_allocator.deinit(); 3856 const arena = arena_allocator.allocator(); 3857 3858 const tmp_digest = man.hash.peek(); 3859 const tmp_dir_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &tmp_digest }); 3860 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); 3861 defer zig_cache_tmp_dir.close(); 3862 const cimport_basename = "cimport.h"; 3863 const out_h_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ 3864 tmp_dir_sub_path, cimport_basename, 3865 }); 3866 const out_dep_path = try std.fmt.allocPrint(arena, "{s}.d", .{out_h_path}); 3867 3868 try zig_cache_tmp_dir.writeFile(cimport_basename, c_src); 3869 if (comp.verbose_cimport) { 3870 log.info("C import source: {s}", .{out_h_path}); 3871 } 3872 3873 var argv = std.ArrayList([]const u8).init(comp.gpa); 3874 defer argv.deinit(); 3875 3876 try argv.append(""); // argv[0] is program name, actual args start at [1] 3877 try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path); 3878 3879 try argv.append(out_h_path); 3880 3881 if (comp.verbose_cc) { 3882 dump_argv(argv.items); 3883 } 3884 3885 // Convert to null terminated args. 3886 const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1); 3887 new_argv_with_sentinel[argv.items.len] = null; 3888 const new_argv = new_argv_with_sentinel[0..argv.items.len :null]; 3889 for (argv.items, 0..) |arg, i| { 3890 new_argv[i] = try arena.dupeZ(u8, arg); 3891 } 3892 3893 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); 3894 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); 3895 var clang_errors: []clang.ErrorMsg = &[0]clang.ErrorMsg{}; 3896 var tree = translate_c.translate( 3897 comp.gpa, 3898 new_argv.ptr, 3899 new_argv.ptr + new_argv.len, 3900 &clang_errors, 3901 c_headers_dir_path_z, 3902 ) catch |err| switch (err) { 3903 error.OutOfMemory => return error.OutOfMemory, 3904 error.ASTUnitFailure => { 3905 log.warn("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}); 3906 return error.ASTUnitFailure; 3907 }, 3908 error.SemanticAnalyzeFail => { 3909 return CImportResult{ 3910 .out_zig_path = "", 3911 .errors = clang_errors, 3912 }; 3913 }, 3914 }; 3915 defer tree.deinit(comp.gpa); 3916 3917 if (comp.verbose_cimport) { 3918 log.info("C import .d file: {s}", .{out_dep_path}); 3919 } 3920 3921 const dep_basename = std.fs.path.basename(out_dep_path); 3922 try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); 3923 if (comp.whole_cache_manifest) |whole_cache_manifest| { 3924 comp.whole_cache_manifest_mutex.lock(); 3925 defer comp.whole_cache_manifest_mutex.unlock(); 3926 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); 3927 } 3928 3929 const digest = man.final(); 3930 const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); 3931 var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); 3932 defer o_dir.close(); 3933 3934 var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{}); 3935 defer out_zig_file.close(); 3936 3937 const formatted = try tree.render(comp.gpa); 3938 defer comp.gpa.free(formatted); 3939 3940 try out_zig_file.writeAll(formatted); 3941 3942 break :digest digest; 3943 } else man.final(); 3944 3945 if (man.have_exclusive_lock) { 3946 // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is 3947 // possible we had a hit and the manifest is dirty, for example if the file mtime changed but 3948 // the contents were the same, we hit the cache but the manifest is dirty and we need to update 3949 // it to prevent doing a full file content comparison the next time around. 3950 man.writeManifest() catch |err| { 3951 log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)}); 3952 }; 3953 } 3954 3955 const out_zig_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ 3956 "o", &digest, cimport_zig_basename, 3957 }); 3958 if (comp.verbose_cimport) { 3959 log.info("C import output: {s}", .{out_zig_path}); 3960 } 3961 return CImportResult{ 3962 .out_zig_path = out_zig_path, 3963 .errors = &[0]clang.ErrorMsg{}, 3964 }; 3965 } 3966 3967 fn workerUpdateCObject( 3968 comp: *Compilation, 3969 c_object: *CObject, 3970 progress_node: *std.Progress.Node, 3971 wg: *WaitGroup, 3972 ) void { 3973 defer wg.finish(); 3974 3975 comp.updateCObject(c_object, progress_node) catch |err| switch (err) { 3976 error.AnalysisFail => return, 3977 else => { 3978 comp.reportRetryableCObjectError(c_object, err) catch |oom| switch (oom) { 3979 // Swallowing this error is OK because it's implied to be OOM when 3980 // there is a missing failed_c_objects error message. 3981 error.OutOfMemory => {}, 3982 }; 3983 }, 3984 }; 3985 } 3986 3987 fn workerUpdateWin32Resource( 3988 comp: *Compilation, 3989 win32_resource: *Win32Resource, 3990 progress_node: *std.Progress.Node, 3991 wg: *WaitGroup, 3992 ) void { 3993 defer wg.finish(); 3994 3995 comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) { 3996 error.AnalysisFail => return, 3997 else => { 3998 comp.reportRetryableWin32ResourceError(win32_resource, err) catch |oom| switch (oom) { 3999 // Swallowing this error is OK because it's implied to be OOM when 4000 // there is a missing failed_win32_resources error message. 4001 error.OutOfMemory => {}, 4002 }; 4003 }, 4004 }; 4005 } 4006 4007 fn buildCompilerRtOneShot( 4008 comp: *Compilation, 4009 output_mode: std.builtin.OutputMode, 4010 out: *?CRTFile, 4011 prog_node: *std.Progress.Node, 4012 ) void { 4013 comp.buildOutputFromZig( 4014 "compiler_rt.zig", 4015 output_mode, 4016 out, 4017 .compiler_rt, 4018 prog_node, 4019 ) catch |err| switch (err) { 4020 error.SubCompilationFailed => return, // error reported already 4021 else => comp.lockAndSetMiscFailure( 4022 .compiler_rt, 4023 "unable to build compiler_rt: {s}", 4024 .{@errorName(err)}, 4025 ), 4026 }; 4027 } 4028 4029 fn reportRetryableCObjectError( 4030 comp: *Compilation, 4031 c_object: *CObject, 4032 err: anyerror, 4033 ) error{OutOfMemory}!void { 4034 c_object.status = .failure_retryable; 4035 4036 const c_obj_err_msg = try comp.gpa.create(CObject.ErrorMsg); 4037 errdefer comp.gpa.destroy(c_obj_err_msg); 4038 const msg = try std.fmt.allocPrint(comp.gpa, "{s}", .{@errorName(err)}); 4039 errdefer comp.gpa.free(msg); 4040 c_obj_err_msg.* = .{ 4041 .msg = msg, 4042 .line = 0, 4043 .column = 0, 4044 }; 4045 { 4046 comp.mutex.lock(); 4047 defer comp.mutex.unlock(); 4048 try comp.failed_c_objects.putNoClobber(comp.gpa, c_object, c_obj_err_msg); 4049 } 4050 } 4051 4052 fn reportRetryableWin32ResourceError( 4053 comp: *Compilation, 4054 win32_resource: *Win32Resource, 4055 err: anyerror, 4056 ) error{OutOfMemory}!void { 4057 win32_resource.status = .failure_retryable; 4058 4059 // TODO: something 4060 _ = comp; 4061 _ = @errorName(err); 4062 } 4063 4064 fn reportRetryableAstGenError( 4065 comp: *Compilation, 4066 src: AstGenSrc, 4067 file: *Module.File, 4068 err: anyerror, 4069 ) error{OutOfMemory}!void { 4070 const mod = comp.bin_file.options.module.?; 4071 const gpa = mod.gpa; 4072 4073 file.status = .retryable_failure; 4074 4075 const src_loc: Module.SrcLoc = switch (src) { 4076 .root => .{ 4077 .file_scope = file, 4078 .parent_decl_node = 0, 4079 .lazy = .entire_file, 4080 }, 4081 .import => |info| blk: { 4082 const importing_file = info.importing_file; 4083 4084 break :blk .{ 4085 .file_scope = importing_file, 4086 .parent_decl_node = 0, 4087 .lazy = .{ .token_abs = info.import_tok }, 4088 }; 4089 }, 4090 }; 4091 4092 const err_msg = if (file.pkg.root_src_directory.path) |dir_path| 4093 try Module.ErrorMsg.create( 4094 gpa, 4095 src_loc, 4096 "unable to load '{s}" ++ std.fs.path.sep_str ++ "{s}': {s}", 4097 .{ dir_path, file.sub_file_path, @errorName(err) }, 4098 ) 4099 else 4100 try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{s}': {s}", .{ 4101 file.sub_file_path, @errorName(err), 4102 }); 4103 errdefer err_msg.destroy(gpa); 4104 4105 { 4106 comp.mutex.lock(); 4107 defer comp.mutex.unlock(); 4108 try mod.failed_files.putNoClobber(gpa, file, err_msg); 4109 } 4110 } 4111 4112 fn reportRetryableEmbedFileError( 4113 comp: *Compilation, 4114 embed_file: *Module.EmbedFile, 4115 err: anyerror, 4116 ) error{OutOfMemory}!void { 4117 const mod = comp.bin_file.options.module.?; 4118 const gpa = mod.gpa; 4119 4120 const src_loc: Module.SrcLoc = mod.declPtr(embed_file.owner_decl).srcLoc(mod); 4121 4122 const err_msg = if (embed_file.pkg.root_src_directory.path) |dir_path| 4123 try Module.ErrorMsg.create( 4124 gpa, 4125 src_loc, 4126 "unable to load '{s}" ++ std.fs.path.sep_str ++ "{s}': {s}", 4127 .{ dir_path, embed_file.sub_file_path, @errorName(err) }, 4128 ) 4129 else 4130 try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{s}': {s}", .{ 4131 embed_file.sub_file_path, @errorName(err), 4132 }); 4133 errdefer err_msg.destroy(gpa); 4134 4135 { 4136 comp.mutex.lock(); 4137 defer comp.mutex.unlock(); 4138 try mod.failed_embed_files.putNoClobber(gpa, embed_file, err_msg); 4139 } 4140 } 4141 4142 fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.Progress.Node) !void { 4143 if (!build_options.have_llvm) { 4144 return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{}); 4145 } 4146 const self_exe_path = comp.self_exe_path orelse 4147 return comp.failCObj(c_object, "clang compilation disabled", .{}); 4148 4149 const tracy_trace = trace(@src()); 4150 defer tracy_trace.end(); 4151 4152 log.debug("updating C object: {s}", .{c_object.src.src_path}); 4153 4154 if (c_object.clearStatus(comp.gpa)) { 4155 // There was previous failure. 4156 comp.mutex.lock(); 4157 defer comp.mutex.unlock(); 4158 // If the failure was OOM, there will not be an entry here, so we do 4159 // not assert discard. 4160 _ = comp.failed_c_objects.swapRemove(c_object); 4161 } 4162 4163 var man = comp.obtainCObjectCacheManifest(); 4164 defer man.deinit(); 4165 4166 man.hash.add(comp.clang_preprocessor_mode); 4167 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm); 4168 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir); 4169 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc); 4170 4171 try cache_helpers.hashCSource(&man, c_object.src); 4172 4173 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); 4174 defer arena_allocator.deinit(); 4175 const arena = arena_allocator.allocator(); 4176 4177 const c_source_basename = std.fs.path.basename(c_object.src.src_path); 4178 4179 c_obj_prog_node.activate(); 4180 var child_progress_node = c_obj_prog_node.start(c_source_basename, 0); 4181 child_progress_node.activate(); 4182 defer child_progress_node.end(); 4183 4184 // Special case when doing build-obj for just one C file. When there are more than one object 4185 // file and building an object we need to link them together, but with just one it should go 4186 // directly to the output file. 4187 const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.module == null and 4188 comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0; 4189 const o_basename_noext = if (direct_o) 4190 comp.bin_file.options.root_name 4191 else 4192 c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len]; 4193 4194 const target = comp.getTarget(); 4195 const o_ext = target.ofmt.fileExt(target.cpu.arch); 4196 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: { 4197 var argv = std.ArrayList([]const u8).init(comp.gpa); 4198 defer argv.deinit(); 4199 4200 // In case we are doing passthrough mode, we need to detect -S and -emit-llvm. 4201 const out_ext = e: { 4202 if (!comp.clang_passthrough_mode) 4203 break :e o_ext; 4204 if (comp.emit_asm != null) 4205 break :e ".s"; 4206 if (comp.emit_llvm_ir != null) 4207 break :e ".ll"; 4208 if (comp.emit_llvm_bc != null) 4209 break :e ".bc"; 4210 4211 break :e o_ext; 4212 }; 4213 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext }); 4214 const ext = c_object.src.ext orelse classifyFileExt(c_object.src.src_path); 4215 4216 try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" }); 4217 // if "ext" is explicit, add "-x <lang>". Otherwise let clang do its thing. 4218 if (c_object.src.ext != null) { 4219 try argv.appendSlice(&[_][]const u8{ "-x", switch (ext) { 4220 .assembly => "assembler", 4221 .assembly_with_cpp => "assembler-with-cpp", 4222 .c => "c", 4223 .cpp => "c++", 4224 .cu => "cuda", 4225 .m => "objective-c", 4226 .mm => "objective-c++", 4227 else => fatal("language '{s}' is unsupported in this context", .{@tagName(ext)}), 4228 } }); 4229 } 4230 try argv.append(c_object.src.src_path); 4231 4232 // When all these flags are true, it means that the entire purpose of 4233 // this compilation is to perform a single zig cc operation. This means 4234 // that we could "tail call" clang by doing an execve, and any use of 4235 // the caching system would actually be problematic since the user is 4236 // presumably doing their own caching by using dep file flags. 4237 if (std.process.can_execv and direct_o and 4238 comp.disable_c_depfile and comp.clang_passthrough_mode) 4239 { 4240 try comp.addCCArgs(arena, &argv, ext, null); 4241 try argv.appendSlice(c_object.src.extra_flags); 4242 try argv.appendSlice(c_object.src.cache_exempt_flags); 4243 4244 const out_obj_path = if (comp.bin_file.options.emit) |emit| 4245 try emit.directory.join(arena, &.{emit.sub_path}) 4246 else 4247 "/dev/null"; 4248 4249 try argv.ensureUnusedCapacity(5); 4250 switch (comp.clang_preprocessor_mode) { 4251 .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }), 4252 .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }), 4253 .stdout => argv.appendAssumeCapacity("-E"), 4254 } 4255 4256 if (comp.emit_asm != null) { 4257 argv.appendAssumeCapacity("-S"); 4258 } else if (comp.emit_llvm_ir != null) { 4259 argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" }); 4260 } else if (comp.emit_llvm_bc != null) { 4261 argv.appendAssumeCapacity("-emit-llvm"); 4262 } 4263 4264 if (comp.verbose_cc) { 4265 dump_argv(argv.items); 4266 } 4267 4268 const err = std.process.execv(arena, argv.items); 4269 fatal("unable to execv clang: {s}", .{@errorName(err)}); 4270 } 4271 4272 // We can't know the digest until we do the C compiler invocation, 4273 // so we need a temporary filename. 4274 const out_obj_path = try comp.tmpFilePath(arena, o_basename); 4275 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); 4276 defer zig_cache_tmp_dir.close(); 4277 4278 const out_dep_path: ?[]const u8 = if (comp.disable_c_depfile or !ext.clangSupportsDepFile()) 4279 null 4280 else 4281 try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path}); 4282 try comp.addCCArgs(arena, &argv, ext, out_dep_path); 4283 try argv.appendSlice(c_object.src.extra_flags); 4284 try argv.appendSlice(c_object.src.cache_exempt_flags); 4285 4286 try argv.ensureUnusedCapacity(5); 4287 switch (comp.clang_preprocessor_mode) { 4288 .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }), 4289 .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }), 4290 .stdout => argv.appendAssumeCapacity("-E"), 4291 } 4292 if (comp.clang_passthrough_mode) { 4293 if (comp.emit_asm != null) { 4294 argv.appendAssumeCapacity("-S"); 4295 } else if (comp.emit_llvm_ir != null) { 4296 argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" }); 4297 } else if (comp.emit_llvm_bc != null) { 4298 argv.appendAssumeCapacity("-emit-llvm"); 4299 } 4300 } 4301 4302 if (comp.verbose_cc) { 4303 dump_argv(argv.items); 4304 } 4305 4306 if (std.process.can_spawn) { 4307 var child = std.ChildProcess.init(argv.items, arena); 4308 if (comp.clang_passthrough_mode) { 4309 child.stdin_behavior = .Inherit; 4310 child.stdout_behavior = .Inherit; 4311 child.stderr_behavior = .Inherit; 4312 4313 const term = child.spawnAndWait() catch |err| { 4314 return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); 4315 }; 4316 switch (term) { 4317 .Exited => |code| { 4318 if (code != 0) { 4319 std.process.exit(code); 4320 } 4321 if (comp.clang_preprocessor_mode == .stdout) 4322 std.process.exit(0); 4323 }, 4324 else => std.process.abort(), 4325 } 4326 } else { 4327 child.stdin_behavior = .Ignore; 4328 child.stdout_behavior = .Ignore; 4329 child.stderr_behavior = .Pipe; 4330 4331 try child.spawn(); 4332 4333 const stderr = try child.stderr.?.reader().readAllAlloc(arena, std.math.maxInt(usize)); 4334 4335 const term = child.wait() catch |err| { 4336 return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); 4337 }; 4338 4339 switch (term) { 4340 .Exited => |code| { 4341 if (code != 0) { 4342 // TODO parse clang stderr and turn it into an error message 4343 // and then call failCObjWithOwnedErrorMsg 4344 log.err("clang failed with stderr: {s}", .{stderr}); 4345 return comp.failCObj(c_object, "clang exited with code {d}", .{code}); 4346 } 4347 }, 4348 else => { 4349 log.err("clang terminated with stderr: {s}", .{stderr}); 4350 return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); 4351 }, 4352 } 4353 } 4354 } else { 4355 const exit_code = try clangMain(arena, argv.items); 4356 if (exit_code != 0) { 4357 if (comp.clang_passthrough_mode) { 4358 std.process.exit(exit_code); 4359 } else { 4360 return comp.failCObj(c_object, "clang exited with code {d}", .{exit_code}); 4361 } 4362 } 4363 if (comp.clang_passthrough_mode and 4364 comp.clang_preprocessor_mode == .stdout) 4365 { 4366 std.process.exit(0); 4367 } 4368 } 4369 4370 if (out_dep_path) |dep_file_path| { 4371 const dep_basename = std.fs.path.basename(dep_file_path); 4372 // Add the files depended on to the cache system. 4373 try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); 4374 if (comp.whole_cache_manifest) |whole_cache_manifest| { 4375 comp.whole_cache_manifest_mutex.lock(); 4376 defer comp.whole_cache_manifest_mutex.unlock(); 4377 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); 4378 } 4379 // Just to save disk space, we delete the file because it is never needed again. 4380 zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { 4381 log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }); 4382 }; 4383 } 4384 4385 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. 4386 if (comp.disable_c_depfile) _ = try man.hit(); 4387 4388 // Rename into place. 4389 const digest = man.final(); 4390 const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); 4391 var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); 4392 defer o_dir.close(); 4393 const tmp_basename = std.fs.path.basename(out_obj_path); 4394 try std.fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename); 4395 break :blk digest; 4396 }; 4397 4398 if (man.have_exclusive_lock) { 4399 // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is 4400 // possible we had a hit and the manifest is dirty, for example if the file mtime changed but 4401 // the contents were the same, we hit the cache but the manifest is dirty and we need to update 4402 // it to prevent doing a full file content comparison the next time around. 4403 man.writeManifest() catch |err| { 4404 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) }); 4405 }; 4406 } 4407 4408 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext }); 4409 4410 c_object.status = .{ 4411 .success = .{ 4412 .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ 4413 "o", &digest, o_basename, 4414 }), 4415 .lock = man.toOwnedLock(), 4416 }, 4417 }; 4418 } 4419 4420 fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: *std.Progress.Node) !void { 4421 if (!build_options.have_llvm) { 4422 return comp.failWin32Resource(win32_resource, "clang not available: compiler built without LLVM extensions", .{}); 4423 } 4424 const self_exe_path = comp.self_exe_path orelse 4425 return comp.failWin32Resource(win32_resource, "clang compilation disabled", .{}); 4426 4427 const tracy_trace = trace(@src()); 4428 defer tracy_trace.end(); 4429 4430 log.debug("updating win32 resource: {s}", .{win32_resource.src.src_path}); 4431 4432 if (win32_resource.clearStatus(comp.gpa)) { 4433 // There was previous failure. 4434 comp.mutex.lock(); 4435 defer comp.mutex.unlock(); 4436 // If the failure was OOM, there will not be an entry here, so we do 4437 // not assert discard. 4438 _ = comp.failed_win32_resources.swapRemove(win32_resource); 4439 } 4440 4441 var man = comp.obtainWin32ResourceCacheManifest(); 4442 defer man.deinit(); 4443 4444 _ = try man.addFile(win32_resource.src.src_path, null); 4445 man.hash.addListOfBytes(win32_resource.src.extra_flags); 4446 4447 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); 4448 defer arena_allocator.deinit(); 4449 const arena = arena_allocator.allocator(); 4450 4451 const rc_basename = std.fs.path.basename(win32_resource.src.src_path); 4452 4453 win32_resource_prog_node.activate(); 4454 var child_progress_node = win32_resource_prog_node.start(rc_basename, 0); 4455 child_progress_node.activate(); 4456 defer child_progress_node.end(); 4457 4458 const rc_basename_noext = rc_basename[0 .. rc_basename.len - std.fs.path.extension(rc_basename).len]; 4459 4460 const digest = if (try man.hit()) man.final() else blk: { 4461 const rcpp_filename = try std.fmt.allocPrint(arena, "{s}.rcpp", .{rc_basename_noext}); 4462 4463 const out_rcpp_path = try comp.tmpFilePath(arena, rcpp_filename); 4464 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); 4465 defer zig_cache_tmp_dir.close(); 4466 4467 const res_filename = try std.fmt.allocPrint(arena, "{s}.res", .{rc_basename_noext}); 4468 4469 // We can't know the digest until we do the compilation, 4470 // so we need a temporary filename. 4471 const out_res_path = try comp.tmpFilePath(arena, res_filename); 4472 4473 var options = options: { 4474 var resinator_args = try std.ArrayListUnmanaged([]const u8).initCapacity(comp.gpa, win32_resource.src.extra_flags.len + 4); 4475 defer resinator_args.deinit(comp.gpa); 4476 4477 resinator_args.appendAssumeCapacity(""); // dummy 'process name' arg 4478 resinator_args.appendSliceAssumeCapacity(win32_resource.src.extra_flags); 4479 resinator_args.appendSliceAssumeCapacity(&.{ "--", out_rcpp_path, out_res_path }); 4480 4481 var cli_diagnostics = resinator.cli.Diagnostics.init(comp.gpa); 4482 defer cli_diagnostics.deinit(); 4483 var options = resinator.cli.parse(comp.gpa, resinator_args.items, &cli_diagnostics) catch |err| switch (err) { 4484 error.ParseError => { 4485 return comp.failWin32ResourceCli(win32_resource, &cli_diagnostics); 4486 }, 4487 else => |e| return e, 4488 }; 4489 break :options options; 4490 }; 4491 defer options.deinit(); 4492 4493 var argv = std.ArrayList([]const u8).init(comp.gpa); 4494 defer argv.deinit(); 4495 var temp_strings = std.ArrayList([]const u8).init(comp.gpa); 4496 defer { 4497 for (temp_strings.items) |temp_string| { 4498 comp.gpa.free(temp_string); 4499 } 4500 temp_strings.deinit(); 4501 } 4502 4503 // TODO: support options.preprocess == .no and .only 4504 // alternatively, error if those options are used 4505 try argv.appendSlice(&[_][]const u8{ 4506 self_exe_path, 4507 "clang", 4508 "-E", // preprocessor only 4509 "--comments", 4510 "-fuse-line-directives", // #line <num> instead of # <num> 4511 "-xc", // output c 4512 "-Werror=null-character", // error on null characters instead of converting them to spaces 4513 "-fms-compatibility", // Allow things like "header.h" to be resolved relative to the 'root' .rc file, among other things 4514 "-DRC_INVOKED", // https://learn.microsoft.com/en-us/windows/win32/menurc/predefined-macros 4515 }); 4516 // Using -fms-compatibility and targeting the gnu abi interact in a strange way: 4517 // - Targeting the GNU abi stops _MSC_VER from being defined 4518 // - Passing -fms-compatibility stops __GNUC__ from being defined 4519 // Neither being defined is a problem for things like things like MinGW's 4520 // vadefs.h, which will fail during preprocessing if neither are defined. 4521 // So, when targeting the GNU abi, we need to force __GNUC__ to be defined. 4522 // 4523 // TODO: This is a workaround that should be removed if possible. 4524 if (comp.getTarget().isGnu()) { 4525 // This is the same default gnuc version that Clang uses: 4526 // https://github.com/llvm/llvm-project/blob/4b5366c9512aa273a5272af1d833961e1ed156e7/clang/lib/Driver/ToolChains/Clang.cpp#L6738 4527 try argv.append("-fgnuc-version=4.2.1"); 4528 } 4529 for (options.extra_include_paths.items) |extra_include_path| { 4530 try argv.append("--include-directory"); 4531 try argv.append(extra_include_path); 4532 } 4533 var symbol_it = options.symbols.iterator(); 4534 while (symbol_it.next()) |entry| { 4535 switch (entry.value_ptr.*) { 4536 .define => |value| { 4537 try argv.append("-D"); 4538 const define_arg = arg: { 4539 const arg = try std.fmt.allocPrint(comp.gpa, "{s}={s}", .{ entry.key_ptr.*, value }); 4540 errdefer comp.gpa.free(arg); 4541 try temp_strings.append(arg); 4542 break :arg arg; 4543 }; 4544 try argv.append(define_arg); 4545 }, 4546 .undefine => { 4547 try argv.append("-U"); 4548 try argv.append(entry.key_ptr.*); 4549 }, 4550 } 4551 } 4552 try argv.append(win32_resource.src.src_path); 4553 try argv.appendSlice(&[_][]const u8{ 4554 "-o", 4555 out_rcpp_path, 4556 }); 4557 4558 const out_dep_path = try std.fmt.allocPrint(arena, "{s}.d", .{out_rcpp_path}); 4559 // Note: addCCArgs will implicitly add _DEBUG/NDEBUG depending on the optimization 4560 // mode. While these defines are not normally present when calling rc.exe directly, 4561 // them being defined matches the behavior of how MSVC calls rc.exe which is the more 4562 // relevant behavior in this case. 4563 try comp.addCCArgs(arena, &argv, .rc, out_dep_path); 4564 4565 if (comp.verbose_cc) { 4566 dump_argv(argv.items); 4567 } 4568 4569 if (std.process.can_spawn) { 4570 var child = std.ChildProcess.init(argv.items, arena); 4571 child.stdin_behavior = .Ignore; 4572 child.stdout_behavior = .Ignore; 4573 child.stderr_behavior = .Pipe; 4574 4575 try child.spawn(); 4576 4577 const stderr_reader = child.stderr.?.reader(); 4578 4579 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); 4580 4581 const term = child.wait() catch |err| { 4582 return comp.failWin32Resource(win32_resource, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); 4583 }; 4584 4585 switch (term) { 4586 .Exited => |code| { 4587 if (code != 0) { 4588 // TODO parse clang stderr and turn it into an error message 4589 // and then call failCObjWithOwnedErrorMsg 4590 log.err("clang preprocessor failed with stderr:\n{s}", .{stderr}); 4591 return comp.failWin32Resource(win32_resource, "clang preprocessor exited with code {d}", .{code}); 4592 } 4593 }, 4594 else => { 4595 log.err("clang preprocessor terminated with stderr:\n{s}", .{stderr}); 4596 return comp.failWin32Resource(win32_resource, "clang preprocessor terminated unexpectedly", .{}); 4597 }, 4598 } 4599 } else { 4600 const exit_code = try clangMain(arena, argv.items); 4601 if (exit_code != 0) { 4602 return comp.failWin32Resource(win32_resource, "clang preprocessor exited with code {d}", .{exit_code}); 4603 } 4604 } 4605 4606 const dep_basename = std.fs.path.basename(out_dep_path); 4607 // Add the files depended on to the cache system. 4608 try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); 4609 if (comp.whole_cache_manifest) |whole_cache_manifest| { 4610 comp.whole_cache_manifest_mutex.lock(); 4611 defer comp.whole_cache_manifest_mutex.unlock(); 4612 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); 4613 } 4614 // Just to save disk space, we delete the file because it is never needed again. 4615 zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { 4616 log.warn("failed to delete '{s}': {s}", .{ out_dep_path, @errorName(err) }); 4617 }; 4618 4619 var full_input = std.fs.cwd().readFileAlloc(arena, out_rcpp_path, std.math.maxInt(usize)) catch |err| switch (err) { 4620 error.OutOfMemory => return error.OutOfMemory, 4621 else => |e| { 4622 return comp.failWin32Resource(win32_resource, "failed to read preprocessed file '{s}': {s}", .{ out_rcpp_path, @errorName(e) }); 4623 }, 4624 }; 4625 4626 var mapping_results = try resinator.source_mapping.parseAndRemoveLineCommands(arena, full_input, full_input, .{ .initial_filename = win32_resource.src.src_path }); 4627 defer mapping_results.mappings.deinit(arena); 4628 4629 var final_input = resinator.comments.removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings); 4630 4631 var output_file = zig_cache_tmp_dir.createFile(out_res_path, .{}) catch |err| { 4632 return comp.failWin32Resource(win32_resource, "failed to create output file '{s}': {s}", .{ out_res_path, @errorName(err) }); 4633 }; 4634 var output_file_closed = false; 4635 defer if (!output_file_closed) output_file.close(); 4636 4637 var diagnostics = resinator.errors.Diagnostics.init(arena); 4638 defer diagnostics.deinit(); 4639 4640 var dependencies_list = std.ArrayList([]const u8).init(comp.gpa); 4641 defer { 4642 for (dependencies_list.items) |item| { 4643 comp.gpa.free(item); 4644 } 4645 dependencies_list.deinit(); 4646 } 4647 4648 var output_buffered_stream = std.io.bufferedWriter(output_file.writer()); 4649 4650 resinator.compile.compile(arena, final_input, output_buffered_stream.writer(), .{ 4651 .cwd = std.fs.cwd(), 4652 .diagnostics = &diagnostics, 4653 .source_mappings = &mapping_results.mappings, 4654 .dependencies_list = &dependencies_list, 4655 .system_include_paths = comp.rc_include_dir_list, 4656 .ignore_include_env_var = true, 4657 // options 4658 .extra_include_paths = options.extra_include_paths.items, 4659 .default_language_id = options.default_language_id, 4660 .default_code_page = options.default_code_page orelse .windows1252, 4661 .verbose = options.verbose, 4662 .null_terminate_string_table_strings = options.null_terminate_string_table_strings, 4663 .max_string_literal_codepoints = options.max_string_literal_codepoints, 4664 .silent_duplicate_control_ids = options.silent_duplicate_control_ids, 4665 .warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page, 4666 }) catch |err| switch (err) { 4667 error.ParseError, error.CompileError => { 4668 // Delete the output file on error 4669 output_file.close(); 4670 output_file_closed = true; 4671 // Failing to delete is not really a big deal, so swallow any errors 4672 zig_cache_tmp_dir.deleteFile(out_res_path) catch { 4673 log.warn("failed to delete '{s}': {s}", .{ out_res_path, @errorName(err) }); 4674 }; 4675 return comp.failWin32ResourceCompile(win32_resource, final_input, &diagnostics, mapping_results.mappings); 4676 }, 4677 else => |e| return e, 4678 }; 4679 4680 try output_buffered_stream.flush(); 4681 4682 for (dependencies_list.items) |dep_file_path| { 4683 try man.addFilePost(dep_file_path); 4684 if (comp.whole_cache_manifest) |whole_cache_manifest| { 4685 comp.whole_cache_manifest_mutex.lock(); 4686 defer comp.whole_cache_manifest_mutex.unlock(); 4687 try whole_cache_manifest.addFilePost(dep_file_path); 4688 } 4689 } 4690 4691 // Rename into place. 4692 const digest = man.final(); 4693 const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); 4694 var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); 4695 defer o_dir.close(); 4696 const tmp_basename = std.fs.path.basename(out_res_path); 4697 try std.fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, res_filename); 4698 const tmp_rcpp_basename = std.fs.path.basename(out_rcpp_path); 4699 try std.fs.rename(zig_cache_tmp_dir, tmp_rcpp_basename, o_dir, rcpp_filename); 4700 break :blk digest; 4701 }; 4702 4703 if (man.have_exclusive_lock) { 4704 // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is 4705 // possible we had a hit and the manifest is dirty, for example if the file mtime changed but 4706 // the contents were the same, we hit the cache but the manifest is dirty and we need to update 4707 // it to prevent doing a full file content comparison the next time around. 4708 man.writeManifest() catch |err| { 4709 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ win32_resource.src.src_path, @errorName(err) }); 4710 }; 4711 } 4712 4713 const res_basename = try std.fmt.allocPrint(arena, "{s}.res", .{rc_basename_noext}); 4714 4715 win32_resource.status = .{ 4716 .success = .{ 4717 .res_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ 4718 "o", &digest, res_basename, 4719 }), 4720 .lock = man.toOwnedLock(), 4721 }, 4722 }; 4723 } 4724 4725 pub fn tmpFilePath(comp: *Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { 4726 const s = std.fs.path.sep_str; 4727 const rand_int = std.crypto.random.int(u64); 4728 if (comp.local_cache_directory.path) |p| { 4729 return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); 4730 } else { 4731 return std.fmt.allocPrint(ally, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix }); 4732 } 4733 } 4734 4735 pub fn addTranslateCCArgs( 4736 comp: *Compilation, 4737 arena: Allocator, 4738 argv: *std.ArrayList([]const u8), 4739 ext: FileExt, 4740 out_dep_path: ?[]const u8, 4741 ) !void { 4742 try argv.appendSlice(&[_][]const u8{ "-x", "c" }); 4743 try comp.addCCArgs(arena, argv, ext, out_dep_path); 4744 // This gives us access to preprocessing entities, presumably at the cost of performance. 4745 try argv.appendSlice(&[_][]const u8{ "-Xclang", "-detailed-preprocessing-record" }); 4746 } 4747 4748 /// Add common C compiler args between translate-c and C object compilation. 4749 pub fn addCCArgs( 4750 comp: *const Compilation, 4751 arena: Allocator, 4752 argv: *std.ArrayList([]const u8), 4753 ext: FileExt, 4754 out_dep_path: ?[]const u8, 4755 ) !void { 4756 const target = comp.getTarget(); 4757 4758 // As of Clang 16.x, it will by default read extra flags from /etc/clang. 4759 // I'm sure the person who implemented this means well, but they have a lot 4760 // to learn about abstractions and where the appropriate boundaries between 4761 // them are. The road to hell is paved with good intentions. Fortunately it 4762 // can be disabled. 4763 try argv.append("--no-default-config"); 4764 4765 if (ext == .cpp) { 4766 try argv.append("-nostdinc++"); 4767 } 4768 4769 // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode 4770 // we want Clang to infer it, and in normal mode we always want it off, which will be true since 4771 // clang will detect stderr as a pipe rather than a terminal. 4772 if (!comp.clang_passthrough_mode) { 4773 // Make stderr more easily parseable. 4774 try argv.append("-fno-caret-diagnostics"); 4775 } 4776 4777 if (comp.bin_file.options.function_sections) { 4778 try argv.append("-ffunction-sections"); 4779 } 4780 4781 if (comp.bin_file.options.no_builtin) { 4782 try argv.append("-fno-builtin"); 4783 } 4784 4785 if (comp.bin_file.options.link_libcpp) { 4786 const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{ 4787 comp.zig_lib_directory.path.?, "libcxx", "include", 4788 }); 4789 const libcxxabi_include_path = try std.fs.path.join(arena, &[_][]const u8{ 4790 comp.zig_lib_directory.path.?, "libcxxabi", "include", 4791 }); 4792 4793 try argv.append("-isystem"); 4794 try argv.append(libcxx_include_path); 4795 4796 try argv.append("-isystem"); 4797 try argv.append(libcxxabi_include_path); 4798 4799 if (target.abi.isMusl()) { 4800 try argv.append("-D_LIBCPP_HAS_MUSL_LIBC"); 4801 } 4802 try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); 4803 try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); 4804 try argv.append("-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS"); 4805 4806 if (comp.bin_file.options.single_threaded) { 4807 try argv.append("-D_LIBCPP_HAS_NO_THREADS"); 4808 } 4809 4810 // See the comment in libcxx.zig for more details about this. 4811 try argv.append("-D_LIBCPP_PSTL_CPU_BACKEND_SERIAL"); 4812 4813 try argv.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_VERSION={d}", .{ 4814 @intFromEnum(comp.libcxx_abi_version), 4815 })); 4816 try argv.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_NAMESPACE=__{d}", .{ 4817 @intFromEnum(comp.libcxx_abi_version), 4818 })); 4819 } 4820 4821 if (comp.bin_file.options.link_libunwind) { 4822 const libunwind_include_path = try std.fs.path.join(arena, &[_][]const u8{ 4823 comp.zig_lib_directory.path.?, "libunwind", "include", 4824 }); 4825 4826 try argv.append("-isystem"); 4827 try argv.append(libunwind_include_path); 4828 } 4829 4830 if (comp.bin_file.options.link_libc and target.isGnuLibC()) { 4831 const target_version = target.os.version_range.linux.glibc; 4832 const glibc_minor_define = try std.fmt.allocPrint(arena, "-D__GLIBC_MINOR__={d}", .{ 4833 target_version.minor, 4834 }); 4835 try argv.append(glibc_minor_define); 4836 } 4837 4838 const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target); 4839 try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple }); 4840 4841 switch (ext) { 4842 .c, .cpp, .m, .mm, .h, .cu, .rc => { 4843 try argv.appendSlice(&[_][]const u8{ 4844 "-nostdinc", 4845 "-fno-spell-checking", 4846 }); 4847 if (comp.bin_file.options.lto) { 4848 try argv.append("-flto"); 4849 } 4850 4851 if (ext == .mm) { 4852 try argv.append("-ObjC++"); 4853 } 4854 4855 for (comp.libc_framework_dir_list) |framework_dir| { 4856 try argv.appendSlice(&.{ "-iframework", framework_dir }); 4857 } 4858 4859 for (comp.bin_file.options.framework_dirs) |framework_dir| { 4860 try argv.appendSlice(&.{ "-F", framework_dir }); 4861 } 4862 4863 // According to Rich Felker libc headers are supposed to go before C language headers. 4864 // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics 4865 // and other compiler specific items. 4866 const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "include" }); 4867 try argv.append("-isystem"); 4868 try argv.append(c_headers_dir); 4869 4870 if (ext == .rc) { 4871 for (comp.rc_include_dir_list) |include_dir| { 4872 try argv.append("-isystem"); 4873 try argv.append(include_dir); 4874 } 4875 } else { 4876 for (comp.libc_include_dir_list) |include_dir| { 4877 try argv.append("-isystem"); 4878 try argv.append(include_dir); 4879 } 4880 } 4881 4882 if (target.cpu.model.llvm_name) |llvm_name| { 4883 try argv.appendSlice(&[_][]const u8{ 4884 "-Xclang", "-target-cpu", "-Xclang", llvm_name, 4885 }); 4886 } 4887 4888 // It would be really nice if there was a more compact way to communicate this info to Clang. 4889 const all_features_list = target.cpu.arch.allFeaturesList(); 4890 try argv.ensureUnusedCapacity(all_features_list.len * 4); 4891 for (all_features_list, 0..) |feature, index_usize| { 4892 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); 4893 const is_enabled = target.cpu.features.isEnabled(index); 4894 4895 if (feature.llvm_name) |llvm_name| { 4896 argv.appendSliceAssumeCapacity(&[_][]const u8{ "-Xclang", "-target-feature", "-Xclang" }); 4897 const plus_or_minus = "-+"[@intFromBool(is_enabled)]; 4898 const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name }); 4899 argv.appendAssumeCapacity(arg); 4900 } 4901 } 4902 const mcmodel = comp.bin_file.options.machine_code_model; 4903 if (mcmodel != .default) { 4904 try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mcmodel)})); 4905 } 4906 4907 switch (target.os.tag) { 4908 .windows => { 4909 // windows.h has files such as pshpack1.h which do #pragma packing, 4910 // triggering a clang warning. So for this target, we disable this warning. 4911 if (target.abi.isGnu()) { 4912 try argv.append("-Wno-pragma-pack"); 4913 } 4914 }, 4915 .macos => { 4916 try argv.ensureUnusedCapacity(2); 4917 // Pass the proper -m<os>-version-min argument for darwin. 4918 const ver = target.os.version_range.semver.min; 4919 argv.appendAssumeCapacity(try std.fmt.allocPrint(arena, "-mmacos-version-min={d}.{d}.{d}", .{ 4920 ver.major, ver.minor, ver.patch, 4921 })); 4922 // This avoids a warning that sometimes occurs when 4923 // providing both a -target argument that contains a 4924 // version as well as the -mmacosx-version-min argument. 4925 // Zig provides the correct value in both places, so it 4926 // doesn't matter which one gets overridden. 4927 argv.appendAssumeCapacity("-Wno-overriding-t-option"); 4928 }, 4929 .ios, .tvos, .watchos => switch (target.cpu.arch) { 4930 // Pass the proper -m<os>-version-min argument for darwin. 4931 .x86, .x86_64 => { 4932 const ver = target.os.version_range.semver.min; 4933 try argv.append(try std.fmt.allocPrint( 4934 arena, 4935 "-m{s}-simulator-version-min={d}.{d}.{d}", 4936 .{ @tagName(target.os.tag), ver.major, ver.minor, ver.patch }, 4937 )); 4938 }, 4939 else => { 4940 const ver = target.os.version_range.semver.min; 4941 try argv.append(try std.fmt.allocPrint(arena, "-m{s}-version-min={d}.{d}.{d}", .{ 4942 @tagName(target.os.tag), ver.major, ver.minor, ver.patch, 4943 })); 4944 }, 4945 }, 4946 else => {}, 4947 } 4948 4949 if (target.cpu.arch.isThumb()) { 4950 try argv.append("-mthumb"); 4951 } 4952 4953 if (comp.sanitize_c and !comp.bin_file.options.tsan) { 4954 try argv.append("-fsanitize=undefined"); 4955 try argv.append("-fsanitize-trap=undefined"); 4956 // It is very common, and well-defined, for a pointer on one side of a C ABI 4957 // to have a different but compatible element type. Examples include: 4958 // `char*` vs `uint8_t*` on a system with 8-bit bytes 4959 // `const char*` vs `char*` 4960 // `char*` vs `unsigned char*` 4961 // Without this flag, Clang would invoke UBSAN when such an extern 4962 // function was called. 4963 try argv.append("-fno-sanitize=function"); 4964 } else if (comp.sanitize_c and comp.bin_file.options.tsan) { 4965 try argv.append("-fsanitize=undefined,thread"); 4966 try argv.append("-fsanitize-trap=undefined"); 4967 try argv.append("-fno-sanitize=function"); 4968 } else if (!comp.sanitize_c and comp.bin_file.options.tsan) { 4969 try argv.append("-fsanitize=thread"); 4970 } 4971 4972 if (comp.bin_file.options.red_zone) { 4973 try argv.append("-mred-zone"); 4974 } else if (target_util.hasRedZone(target)) { 4975 try argv.append("-mno-red-zone"); 4976 } 4977 4978 if (comp.bin_file.options.omit_frame_pointer) { 4979 try argv.append("-fomit-frame-pointer"); 4980 } else { 4981 try argv.append("-fno-omit-frame-pointer"); 4982 } 4983 4984 const ssp_buf_size = comp.bin_file.options.stack_protector; 4985 if (ssp_buf_size != 0) { 4986 try argv.appendSlice(&[_][]const u8{ 4987 "-fstack-protector-strong", 4988 "--param", 4989 try std.fmt.allocPrint(arena, "ssp-buffer-size={d}", .{ssp_buf_size}), 4990 }); 4991 } else { 4992 try argv.append("-fno-stack-protector"); 4993 } 4994 4995 switch (comp.bin_file.options.optimize_mode) { 4996 .Debug => { 4997 // windows c runtime requires -D_DEBUG if using debug libraries 4998 try argv.append("-D_DEBUG"); 4999 // Clang has -Og for compatibility with GCC, but currently it is just equivalent 5000 // to -O1. Besides potentially impairing debugging, -O1/-Og significantly 5001 // increases compile times. 5002 try argv.append("-O0"); 5003 }, 5004 .ReleaseSafe => { 5005 // See the comment in the BuildModeFastRelease case for why we pass -O2 rather 5006 // than -O3 here. 5007 try argv.append("-O2"); 5008 try argv.append("-D_FORTIFY_SOURCE=2"); 5009 }, 5010 .ReleaseFast => { 5011 try argv.append("-DNDEBUG"); 5012 // Here we pass -O2 rather than -O3 because, although we do the equivalent of 5013 // -O3 in Zig code, the justification for the difference here is that Zig 5014 // has better detection and prevention of undefined behavior, so -O3 is safer for 5015 // Zig code than it is for C code. Also, C programmers are used to their code 5016 // running in -O2 and thus the -O3 path has been tested less. 5017 try argv.append("-O2"); 5018 }, 5019 .ReleaseSmall => { 5020 try argv.append("-DNDEBUG"); 5021 try argv.append("-Os"); 5022 }, 5023 } 5024 5025 if (target_util.supports_fpic(target) and comp.bin_file.options.pic) { 5026 try argv.append("-fPIC"); 5027 } 5028 5029 if (comp.unwind_tables) { 5030 try argv.append("-funwind-tables"); 5031 } else { 5032 try argv.append("-fno-unwind-tables"); 5033 } 5034 }, 5035 .shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig, .res => {}, 5036 .assembly, .assembly_with_cpp => { 5037 if (ext == .assembly_with_cpp) { 5038 const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "include" }); 5039 try argv.append("-isystem"); 5040 try argv.append(c_headers_dir); 5041 } 5042 5043 // The Clang assembler does not accept the list of CPU features like the 5044 // compiler frontend does. Therefore we must hard-code the -m flags for 5045 // all CPU features here. 5046 switch (target.cpu.arch) { 5047 .riscv32, .riscv64 => { 5048 const RvArchFeat = struct { char: u8, feat: std.Target.riscv.Feature }; 5049 const letters = [_]RvArchFeat{ 5050 .{ .char = 'm', .feat = .m }, 5051 .{ .char = 'a', .feat = .a }, 5052 .{ .char = 'f', .feat = .f }, 5053 .{ .char = 'd', .feat = .d }, 5054 .{ .char = 'c', .feat = .c }, 5055 }; 5056 const prefix: []const u8 = if (target.cpu.arch == .riscv64) "rv64" else "rv32"; 5057 const prefix_len = 4; 5058 assert(prefix.len == prefix_len); 5059 var march_buf: [prefix_len + letters.len + 1]u8 = undefined; 5060 var march_index: usize = prefix_len; 5061 @memcpy(march_buf[0..prefix.len], prefix); 5062 5063 if (std.Target.riscv.featureSetHas(target.cpu.features, .e)) { 5064 march_buf[march_index] = 'e'; 5065 } else { 5066 march_buf[march_index] = 'i'; 5067 } 5068 march_index += 1; 5069 5070 for (letters) |letter| { 5071 if (std.Target.riscv.featureSetHas(target.cpu.features, letter.feat)) { 5072 march_buf[march_index] = letter.char; 5073 march_index += 1; 5074 } 5075 } 5076 5077 const march_arg = try std.fmt.allocPrint(arena, "-march={s}", .{ 5078 march_buf[0..march_index], 5079 }); 5080 try argv.append(march_arg); 5081 5082 if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) { 5083 try argv.append("-mrelax"); 5084 } else { 5085 try argv.append("-mno-relax"); 5086 } 5087 if (std.Target.riscv.featureSetHas(target.cpu.features, .save_restore)) { 5088 try argv.append("-msave-restore"); 5089 } else { 5090 try argv.append("-mno-save-restore"); 5091 } 5092 }, 5093 .mips, .mipsel, .mips64, .mips64el => { 5094 if (target.cpu.model.llvm_name) |llvm_name| { 5095 try argv.append(try std.fmt.allocPrint(arena, "-march={s}", .{llvm_name})); 5096 } 5097 5098 if (std.Target.mips.featureSetHas(target.cpu.features, .soft_float)) { 5099 try argv.append("-msoft-float"); 5100 } 5101 }, 5102 else => { 5103 // TODO 5104 }, 5105 } 5106 if (target_util.clangAssemblerSupportsMcpuArg(target)) { 5107 if (target.cpu.model.llvm_name) |llvm_name| { 5108 try argv.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{llvm_name})); 5109 } 5110 } 5111 }, 5112 } 5113 5114 if (!comp.bin_file.options.strip) { 5115 switch (target.ofmt) { 5116 .coff => { 5117 // -g is required here because -gcodeview doesn't trigger debug info 5118 // generation, it only changes the type of information generated. 5119 try argv.appendSlice(&.{ "-g", "-gcodeview" }); 5120 }, 5121 .elf, .macho => { 5122 try argv.append("-gdwarf-4"); 5123 if (comp.bin_file.options.dwarf_format) |f| switch (f) { 5124 .@"32" => try argv.append("-gdwarf32"), 5125 .@"64" => try argv.append("-gdwarf64"), 5126 }; 5127 }, 5128 else => try argv.append("-g"), 5129 } 5130 } 5131 5132 if (target_util.llvmMachineAbi(target)) |mabi| { 5133 try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{mabi})); 5134 } 5135 5136 if (out_dep_path) |p| { 5137 try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p }); 5138 } 5139 5140 // We never want clang to invoke the system assembler for anything. So we would want 5141 // this option always enabled. However, it only matters for some targets. To avoid 5142 // "unused parameter" warnings, and to keep CLI spam to a minimum, we only put this 5143 // flag on the command line if it is necessary. 5144 if (target_util.clangMightShellOutForAssembly(target)) { 5145 try argv.append("-integrated-as"); 5146 } 5147 5148 if (target.os.tag == .freestanding) { 5149 try argv.append("-ffreestanding"); 5150 } 5151 5152 try argv.appendSlice(comp.clang_argv); 5153 } 5154 5155 fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) SemaError { 5156 @setCold(true); 5157 const err_msg = blk: { 5158 const msg = try std.fmt.allocPrint(comp.gpa, format, args); 5159 errdefer comp.gpa.free(msg); 5160 const err_msg = try comp.gpa.create(CObject.ErrorMsg); 5161 errdefer comp.gpa.destroy(err_msg); 5162 err_msg.* = .{ 5163 .msg = msg, 5164 .line = 0, 5165 .column = 0, 5166 }; 5167 break :blk err_msg; 5168 }; 5169 return comp.failCObjWithOwnedErrorMsg(c_object, err_msg); 5170 } 5171 5172 fn failCObjWithOwnedErrorMsg( 5173 comp: *Compilation, 5174 c_object: *CObject, 5175 err_msg: *CObject.ErrorMsg, 5176 ) SemaError { 5177 @setCold(true); 5178 { 5179 comp.mutex.lock(); 5180 defer comp.mutex.unlock(); 5181 { 5182 errdefer err_msg.destroy(comp.gpa); 5183 try comp.failed_c_objects.ensureUnusedCapacity(comp.gpa, 1); 5184 } 5185 comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg); 5186 } 5187 c_object.status = .failure; 5188 return error.AnalysisFail; 5189 } 5190 5191 /// The include directories used when preprocessing .rc files are separate from the 5192 /// target. Which include directories are used is determined by `options.rc_includes`. 5193 /// 5194 /// Note: It should be okay that the include directories used when compiling .rc 5195 /// files differ from the include directories used when compiling the main 5196 /// binary, since the .res format is not dependent on anything ABI-related. The 5197 /// only relevant differences would be things like `#define` constants being 5198 /// different in the MinGW headers vs the MSVC headers, but any such 5199 /// differences would likely be a MinGW bug. 5200 fn detectWin32ResourceIncludeDirs(arena: Allocator, options: InitOptions) !LibCDirs { 5201 // Set the includes to .none here when there are no rc files to compile 5202 var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none; 5203 if (builtin.target.os.tag != .windows) { 5204 switch (includes) { 5205 // MSVC can't be found when the host isn't Windows, so short-circuit. 5206 .msvc => return error.WindowsSdkNotFound, 5207 // Skip straight to gnu since we won't be able to detect MSVC on non-Windows hosts. 5208 .any => includes = .gnu, 5209 .none, .gnu => {}, 5210 } 5211 } 5212 while (true) { 5213 switch (includes) { 5214 .any, .msvc => return detectLibCIncludeDirs( 5215 arena, 5216 options.zig_lib_directory.path.?, 5217 .{ 5218 .cpu = options.target.cpu, 5219 .os = options.target.os, 5220 .abi = .msvc, 5221 .ofmt = options.target.ofmt, 5222 }, 5223 options.is_native_abi, 5224 // The .rc preprocessor will need to know the libc include dirs even if we 5225 // are not linking libc, so force 'link_libc' to true 5226 true, 5227 options.libc_installation, 5228 ) catch |err| { 5229 if (includes == .any) { 5230 // fall back to mingw 5231 includes = .gnu; 5232 continue; 5233 } 5234 return err; 5235 }, 5236 .gnu => return detectLibCFromBuilding(arena, options.zig_lib_directory.path.?, .{ 5237 .cpu = options.target.cpu, 5238 .os = options.target.os, 5239 .abi = .gnu, 5240 .ofmt = options.target.ofmt, 5241 }), 5242 .none => return LibCDirs{ 5243 .libc_include_dir_list = &[0][]u8{}, 5244 .libc_installation = null, 5245 .libc_framework_dir_list = &.{}, 5246 .sysroot = null, 5247 }, 5248 } 5249 } 5250 } 5251 5252 fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) SemaError { 5253 @setCold(true); 5254 var bundle: ErrorBundle.Wip = undefined; 5255 try bundle.init(comp.gpa); 5256 errdefer bundle.deinit(); 5257 try bundle.addRootErrorMessage(.{ 5258 .msg = try bundle.printString(format, args), 5259 .src_loc = try bundle.addSourceLocation(.{ 5260 .src_path = try bundle.addString(win32_resource.src.src_path), 5261 .line = 0, 5262 .column = 0, 5263 .span_start = 0, 5264 .span_main = 0, 5265 .span_end = 0, 5266 }), 5267 }); 5268 const finished_bundle = try bundle.toOwnedBundle(""); 5269 return comp.failWin32ResourceWithOwnedBundle(win32_resource, finished_bundle); 5270 } 5271 5272 fn failWin32ResourceWithOwnedBundle( 5273 comp: *Compilation, 5274 win32_resource: *Win32Resource, 5275 err_bundle: ErrorBundle, 5276 ) SemaError { 5277 @setCold(true); 5278 { 5279 comp.mutex.lock(); 5280 defer comp.mutex.unlock(); 5281 try comp.failed_win32_resources.putNoClobber(comp.gpa, win32_resource, err_bundle); 5282 } 5283 win32_resource.status = .failure; 5284 return error.AnalysisFail; 5285 } 5286 5287 fn failWin32ResourceCli( 5288 comp: *Compilation, 5289 win32_resource: *Win32Resource, 5290 diagnostics: *resinator.cli.Diagnostics, 5291 ) SemaError { 5292 @setCold(true); 5293 5294 var bundle: ErrorBundle.Wip = undefined; 5295 try bundle.init(comp.gpa); 5296 errdefer bundle.deinit(); 5297 5298 try bundle.addRootErrorMessage(.{ 5299 .msg = try bundle.addString("invalid command line option(s)"), 5300 .src_loc = try bundle.addSourceLocation(.{ 5301 .src_path = try bundle.addString(win32_resource.src.src_path), 5302 .line = 0, 5303 .column = 0, 5304 .span_start = 0, 5305 .span_main = 0, 5306 .span_end = 0, 5307 }), 5308 }); 5309 5310 var cur_err: ?ErrorBundle.ErrorMessage = null; 5311 var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .{}; 5312 defer cur_notes.deinit(comp.gpa); 5313 for (diagnostics.errors.items) |err_details| { 5314 switch (err_details.type) { 5315 .err => { 5316 if (cur_err) |err| { 5317 try win32ResourceFlushErrorMessage(&bundle, err, cur_notes.items); 5318 } 5319 cur_err = .{ 5320 .msg = try bundle.addString(err_details.msg.items), 5321 }; 5322 cur_notes.clearRetainingCapacity(); 5323 }, 5324 .warning => cur_err = null, 5325 .note => { 5326 if (cur_err == null) continue; 5327 cur_err.?.notes_len += 1; 5328 try cur_notes.append(comp.gpa, .{ 5329 .msg = try bundle.addString(err_details.msg.items), 5330 }); 5331 }, 5332 } 5333 } 5334 if (cur_err) |err| { 5335 try win32ResourceFlushErrorMessage(&bundle, err, cur_notes.items); 5336 } 5337 5338 const finished_bundle = try bundle.toOwnedBundle(""); 5339 return comp.failWin32ResourceWithOwnedBundle(win32_resource, finished_bundle); 5340 } 5341 5342 fn failWin32ResourceCompile( 5343 comp: *Compilation, 5344 win32_resource: *Win32Resource, 5345 source: []const u8, 5346 diagnostics: *resinator.errors.Diagnostics, 5347 mappings: resinator.source_mapping.SourceMappings, 5348 ) SemaError { 5349 @setCold(true); 5350 5351 var bundle: ErrorBundle.Wip = undefined; 5352 try bundle.init(comp.gpa); 5353 errdefer bundle.deinit(); 5354 5355 var msg_buf: std.ArrayListUnmanaged(u8) = .{}; 5356 defer msg_buf.deinit(comp.gpa); 5357 var cur_err: ?ErrorBundle.ErrorMessage = null; 5358 var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .{}; 5359 defer cur_notes.deinit(comp.gpa); 5360 for (diagnostics.errors.items) |err_details| { 5361 switch (err_details.type) { 5362 .hint => continue, 5363 // Clear the current error so that notes don't bleed into unassociated errors 5364 .warning => { 5365 cur_err = null; 5366 continue; 5367 }, 5368 .note => if (cur_err == null) continue, 5369 .err => {}, 5370 } 5371 const corresponding_span = mappings.get(err_details.token.line_number); 5372 const corresponding_file = mappings.files.get(corresponding_span.filename_offset); 5373 5374 const source_line_start = err_details.token.getLineStart(source); 5375 const column = err_details.token.calculateColumn(source, 1, source_line_start); 5376 const err_line = corresponding_span.start_line; 5377 5378 msg_buf.clearRetainingCapacity(); 5379 try err_details.render(msg_buf.writer(comp.gpa), source, diagnostics.strings.items); 5380 5381 const src_loc = src_loc: { 5382 var src_loc: ErrorBundle.SourceLocation = .{ 5383 .src_path = try bundle.addString(corresponding_file), 5384 .line = @intCast(err_line - 1), // 1-based -> 0-based 5385 .column = @intCast(column), 5386 .span_start = 0, 5387 .span_main = 0, 5388 .span_end = 0, 5389 }; 5390 if (err_details.print_source_line) { 5391 const source_line = err_details.token.getLine(source, source_line_start); 5392 const visual_info = err_details.visualTokenInfo(source_line_start, source_line_start + source_line.len); 5393 src_loc.span_start = @intCast(visual_info.point_offset - visual_info.before_len); 5394 src_loc.span_main = @intCast(visual_info.point_offset); 5395 src_loc.span_end = @intCast(visual_info.point_offset + 1 + visual_info.after_len); 5396 src_loc.source_line = try bundle.addString(source_line); 5397 } 5398 break :src_loc try bundle.addSourceLocation(src_loc); 5399 }; 5400 5401 switch (err_details.type) { 5402 .err => { 5403 if (cur_err) |err| { 5404 try win32ResourceFlushErrorMessage(&bundle, err, cur_notes.items); 5405 } 5406 cur_err = .{ 5407 .msg = try bundle.addString(msg_buf.items), 5408 .src_loc = src_loc, 5409 }; 5410 cur_notes.clearRetainingCapacity(); 5411 }, 5412 .note => { 5413 cur_err.?.notes_len += 1; 5414 try cur_notes.append(comp.gpa, .{ 5415 .msg = try bundle.addString(msg_buf.items), 5416 .src_loc = src_loc, 5417 }); 5418 }, 5419 .warning, .hint => unreachable, 5420 } 5421 } 5422 if (cur_err) |err| { 5423 try win32ResourceFlushErrorMessage(&bundle, err, cur_notes.items); 5424 } 5425 5426 const finished_bundle = try bundle.toOwnedBundle(""); 5427 return comp.failWin32ResourceWithOwnedBundle(win32_resource, finished_bundle); 5428 } 5429 5430 fn win32ResourceFlushErrorMessage(wip: *ErrorBundle.Wip, msg: ErrorBundle.ErrorMessage, notes: []const ErrorBundle.ErrorMessage) !void { 5431 try wip.addRootErrorMessage(msg); 5432 const notes_start = try wip.reserveNotes(@intCast(notes.len)); 5433 for (notes_start.., notes) |i, note| { 5434 wip.extra.items[i] = @intFromEnum(wip.addErrorMessageAssumeCapacity(note)); 5435 } 5436 } 5437 5438 pub const FileExt = enum { 5439 c, 5440 cpp, 5441 cu, 5442 h, 5443 m, 5444 mm, 5445 ll, 5446 bc, 5447 assembly, 5448 assembly_with_cpp, 5449 shared_library, 5450 object, 5451 static_library, 5452 zig, 5453 def, 5454 rc, 5455 res, 5456 unknown, 5457 5458 pub fn clangSupportsDepFile(ext: FileExt) bool { 5459 return switch (ext) { 5460 .c, .cpp, .h, .m, .mm, .cu => true, 5461 5462 .ll, 5463 .bc, 5464 .assembly, 5465 .assembly_with_cpp, 5466 .shared_library, 5467 .object, 5468 .static_library, 5469 .zig, 5470 .def, 5471 .rc, 5472 .res, 5473 .unknown, 5474 => false, 5475 }; 5476 } 5477 5478 pub fn canonicalName(ext: FileExt, target: Target) [:0]const u8 { 5479 return switch (ext) { 5480 .c => ".c", 5481 .cpp => ".cpp", 5482 .cu => ".cu", 5483 .h => ".h", 5484 .m => ".m", 5485 .mm => ".mm", 5486 .ll => ".ll", 5487 .bc => ".bc", 5488 .assembly => ".s", 5489 .assembly_with_cpp => ".S", 5490 .shared_library => target.dynamicLibSuffix(), 5491 .object => target.ofmt.fileExt(target.cpu.arch), 5492 .static_library => target.staticLibSuffix(), 5493 .zig => ".zig", 5494 .def => ".def", 5495 .rc => ".rc", 5496 .res => ".res", 5497 .unknown => "", 5498 }; 5499 } 5500 }; 5501 5502 pub fn hasObjectExt(filename: []const u8) bool { 5503 return mem.endsWith(u8, filename, ".o") or mem.endsWith(u8, filename, ".obj"); 5504 } 5505 5506 pub fn hasStaticLibraryExt(filename: []const u8) bool { 5507 return mem.endsWith(u8, filename, ".a") or mem.endsWith(u8, filename, ".lib"); 5508 } 5509 5510 pub fn hasCExt(filename: []const u8) bool { 5511 return mem.endsWith(u8, filename, ".c"); 5512 } 5513 5514 pub fn hasCppExt(filename: []const u8) bool { 5515 return mem.endsWith(u8, filename, ".C") or 5516 mem.endsWith(u8, filename, ".cc") or 5517 mem.endsWith(u8, filename, ".cpp") or 5518 mem.endsWith(u8, filename, ".cxx") or 5519 mem.endsWith(u8, filename, ".stub"); 5520 } 5521 5522 pub fn hasObjCExt(filename: []const u8) bool { 5523 return mem.endsWith(u8, filename, ".m"); 5524 } 5525 5526 pub fn hasObjCppExt(filename: []const u8) bool { 5527 return mem.endsWith(u8, filename, ".mm"); 5528 } 5529 5530 pub fn hasSharedLibraryExt(filename: []const u8) bool { 5531 if (mem.endsWith(u8, filename, ".so") or 5532 mem.endsWith(u8, filename, ".dll") or 5533 mem.endsWith(u8, filename, ".dylib") or 5534 mem.endsWith(u8, filename, ".tbd")) 5535 { 5536 return true; 5537 } 5538 // Look for .so.X, .so.X.Y, .so.X.Y.Z 5539 var it = mem.splitScalar(u8, filename, '.'); 5540 _ = it.first(); 5541 var so_txt = it.next() orelse return false; 5542 while (!mem.eql(u8, so_txt, "so")) { 5543 so_txt = it.next() orelse return false; 5544 } 5545 const n1 = it.next() orelse return false; 5546 const n2 = it.next(); 5547 const n3 = it.next(); 5548 5549 _ = std.fmt.parseInt(u32, n1, 10) catch return false; 5550 if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false; 5551 if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false; 5552 if (it.next() != null) return false; 5553 5554 return true; 5555 } 5556 5557 pub fn classifyFileExt(filename: []const u8) FileExt { 5558 if (hasCExt(filename)) { 5559 return .c; 5560 } else if (hasCppExt(filename)) { 5561 return .cpp; 5562 } else if (hasObjCExt(filename)) { 5563 return .m; 5564 } else if (hasObjCppExt(filename)) { 5565 return .mm; 5566 } else if (mem.endsWith(u8, filename, ".ll")) { 5567 return .ll; 5568 } else if (mem.endsWith(u8, filename, ".bc")) { 5569 return .bc; 5570 } else if (mem.endsWith(u8, filename, ".s")) { 5571 return .assembly; 5572 } else if (mem.endsWith(u8, filename, ".S")) { 5573 return .assembly_with_cpp; 5574 } else if (mem.endsWith(u8, filename, ".h")) { 5575 return .h; 5576 } else if (mem.endsWith(u8, filename, ".zig")) { 5577 return .zig; 5578 } else if (hasSharedLibraryExt(filename)) { 5579 return .shared_library; 5580 } else if (hasStaticLibraryExt(filename)) { 5581 return .static_library; 5582 } else if (hasObjectExt(filename)) { 5583 return .object; 5584 } else if (mem.endsWith(u8, filename, ".cu")) { 5585 return .cu; 5586 } else if (mem.endsWith(u8, filename, ".def")) { 5587 return .def; 5588 } else if (std.ascii.endsWithIgnoreCase(filename, ".rc")) { 5589 return .rc; 5590 } else if (std.ascii.endsWithIgnoreCase(filename, ".res")) { 5591 return .res; 5592 } else { 5593 return .unknown; 5594 } 5595 } 5596 5597 test "classifyFileExt" { 5598 try std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc")); 5599 try std.testing.expectEqual(FileExt.m, classifyFileExt("foo.m")); 5600 try std.testing.expectEqual(FileExt.mm, classifyFileExt("foo.mm")); 5601 try std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim")); 5602 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so")); 5603 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1")); 5604 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2")); 5605 try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2.3")); 5606 try std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~")); 5607 try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig")); 5608 } 5609 5610 const LibCDirs = struct { 5611 libc_include_dir_list: []const []const u8, 5612 libc_installation: ?*const LibCInstallation, 5613 libc_framework_dir_list: []const []const u8, 5614 sysroot: ?[]const u8, 5615 }; 5616 5617 fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8, target: Target) !LibCDirs { 5618 const arch_name = @tagName(target.cpu.arch); 5619 const os_name = try std.fmt.allocPrint(arena, "{s}.{d}", .{ 5620 @tagName(target.os.tag), 5621 target.os.version_range.semver.min.major, 5622 }); 5623 const s = std.fs.path.sep_str; 5624 const list = try arena.alloc([]const u8, 3); 5625 5626 list[0] = try std.fmt.allocPrint( 5627 arena, 5628 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-none", 5629 .{ zig_lib_dir, arch_name, os_name }, 5630 ); 5631 list[1] = try std.fmt.allocPrint( 5632 arena, 5633 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any", 5634 .{ zig_lib_dir, os_name }, 5635 ); 5636 list[2] = try std.fmt.allocPrint( 5637 arena, 5638 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-macos-any", 5639 .{zig_lib_dir}, 5640 ); 5641 5642 return LibCDirs{ 5643 .libc_include_dir_list = list, 5644 .libc_installation = null, 5645 .libc_framework_dir_list = &.{}, 5646 .sysroot = null, 5647 }; 5648 } 5649 5650 pub fn detectLibCIncludeDirs( 5651 arena: Allocator, 5652 zig_lib_dir: []const u8, 5653 target: Target, 5654 is_native_abi: bool, 5655 link_libc: bool, 5656 libc_installation: ?*const LibCInstallation, 5657 ) !LibCDirs { 5658 if (!link_libc) { 5659 return LibCDirs{ 5660 .libc_include_dir_list = &[0][]u8{}, 5661 .libc_installation = null, 5662 .libc_framework_dir_list = &.{}, 5663 .sysroot = null, 5664 }; 5665 } 5666 5667 if (libc_installation) |lci| { 5668 return detectLibCFromLibCInstallation(arena, target, lci); 5669 } 5670 5671 // If linking system libraries and targeting the native abi, default to 5672 // using the system libc installation. 5673 if (is_native_abi and !target.isMinGW()) { 5674 const libc = try arena.create(LibCInstallation); 5675 libc.* = LibCInstallation.findNative(.{ .allocator = arena, .target = target }) catch |err| switch (err) { 5676 error.CCompilerExitCode, 5677 error.CCompilerCrashed, 5678 error.CCompilerCannotFindHeaders, 5679 error.UnableToSpawnCCompiler, 5680 error.DarwinSdkNotFound, 5681 => |e| { 5682 // We tried to integrate with the native system C compiler, 5683 // however, it is not installed. So we must rely on our bundled 5684 // libc files. 5685 if (target_util.canBuildLibC(target)) { 5686 return detectLibCFromBuilding(arena, zig_lib_dir, target); 5687 } 5688 return e; 5689 }, 5690 else => |e| return e, 5691 }; 5692 return detectLibCFromLibCInstallation(arena, target, libc); 5693 } 5694 5695 // If not linking system libraries, build and provide our own libc by 5696 // default if possible. 5697 if (target_util.canBuildLibC(target)) { 5698 return detectLibCFromBuilding(arena, zig_lib_dir, target); 5699 } 5700 5701 // If zig can't build the libc for the target and we are targeting the 5702 // native abi, fall back to using the system libc installation. 5703 // On windows, instead of the native (mingw) abi, we want to check 5704 // for the MSVC abi as a fallback. 5705 const use_system_abi = if (builtin.target.os.tag == .windows) 5706 target.abi == .msvc 5707 else 5708 is_native_abi; 5709 5710 if (use_system_abi) { 5711 const libc = try arena.create(LibCInstallation); 5712 libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true, .target = target }); 5713 return detectLibCFromLibCInstallation(arena, target, libc); 5714 } 5715 5716 return LibCDirs{ 5717 .libc_include_dir_list = &[0][]u8{}, 5718 .libc_installation = null, 5719 .libc_framework_dir_list = &.{}, 5720 .sysroot = null, 5721 }; 5722 } 5723 5724 fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs { 5725 var list = try std.ArrayList([]const u8).initCapacity(arena, 5); 5726 var framework_list = std.ArrayList([]const u8).init(arena); 5727 5728 list.appendAssumeCapacity(lci.include_dir.?); 5729 5730 const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?); 5731 if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?); 5732 5733 if (target.os.tag == .windows) { 5734 if (std.fs.path.dirname(lci.sys_include_dir.?)) |sys_include_dir_parent| { 5735 // This include path will only exist when the optional "Desktop development with C++" 5736 // is installed. It contains headers, .rc files, and resources. It is especially 5737 // necessary when working with Windows resources. 5738 const atlmfc_dir = try std.fs.path.join(arena, &[_][]const u8{ sys_include_dir_parent, "atlmfc", "include" }); 5739 list.appendAssumeCapacity(atlmfc_dir); 5740 } 5741 if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| { 5742 const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" }); 5743 list.appendAssumeCapacity(um_dir); 5744 5745 const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" }); 5746 list.appendAssumeCapacity(shared_dir); 5747 } 5748 } 5749 if (target.os.tag == .haiku) { 5750 const include_dir_path = lci.include_dir orelse return error.LibCInstallationNotAvailable; 5751 const os_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os" }); 5752 list.appendAssumeCapacity(os_dir); 5753 // Errors.h 5754 const os_support_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os/support" }); 5755 list.appendAssumeCapacity(os_support_dir); 5756 5757 const config_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "config" }); 5758 list.appendAssumeCapacity(config_dir); 5759 } 5760 5761 var sysroot: ?[]const u8 = null; 5762 5763 if (target.isDarwin()) d: { 5764 const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d; 5765 const down2 = std.fs.path.dirname(down1) orelse break :d; 5766 try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" })); 5767 sysroot = down2; 5768 } 5769 5770 return LibCDirs{ 5771 .libc_include_dir_list = list.items, 5772 .libc_installation = lci, 5773 .libc_framework_dir_list = framework_list.items, 5774 .sysroot = sysroot, 5775 }; 5776 } 5777 5778 fn detectLibCFromBuilding( 5779 arena: Allocator, 5780 zig_lib_dir: []const u8, 5781 target: std.Target, 5782 ) !LibCDirs { 5783 if (target.isDarwin()) 5784 return getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir, target); 5785 5786 const generic_name = target_util.libCGenericName(target); 5787 // Some architectures are handled by the same set of headers. 5788 const arch_name = if (target.abi.isMusl()) 5789 musl.archNameHeaders(target.cpu.arch) 5790 else if (target.cpu.arch.isThumb()) 5791 // ARM headers are valid for Thumb too. 5792 switch (target.cpu.arch) { 5793 .thumb => "arm", 5794 .thumbeb => "armeb", 5795 else => unreachable, 5796 } 5797 else 5798 @tagName(target.cpu.arch); 5799 const os_name = @tagName(target.os.tag); 5800 // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name. 5801 const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi); 5802 const s = std.fs.path.sep_str; 5803 const arch_include_dir = try std.fmt.allocPrint( 5804 arena, 5805 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", 5806 .{ zig_lib_dir, arch_name, os_name, abi_name }, 5807 ); 5808 const generic_include_dir = try std.fmt.allocPrint( 5809 arena, 5810 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}", 5811 .{ zig_lib_dir, generic_name }, 5812 ); 5813 const generic_arch_name = target_util.osArchName(target); 5814 const arch_os_include_dir = try std.fmt.allocPrint( 5815 arena, 5816 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any", 5817 .{ zig_lib_dir, generic_arch_name, os_name }, 5818 ); 5819 const generic_os_include_dir = try std.fmt.allocPrint( 5820 arena, 5821 "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any", 5822 .{ zig_lib_dir, os_name }, 5823 ); 5824 5825 const list = try arena.alloc([]const u8, 4); 5826 list[0] = arch_include_dir; 5827 list[1] = generic_include_dir; 5828 list[2] = arch_os_include_dir; 5829 list[3] = generic_os_include_dir; 5830 5831 return LibCDirs{ 5832 .libc_include_dir_list = list, 5833 .libc_installation = null, 5834 .libc_framework_dir_list = &.{}, 5835 .sysroot = null, 5836 }; 5837 } 5838 5839 pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 { 5840 if (comp.wantBuildGLibCFromSource() or 5841 comp.wantBuildMuslFromSource() or 5842 comp.wantBuildMinGWFromSource() or 5843 comp.wantBuildWasiLibcFromSource()) 5844 { 5845 return comp.crt_files.get(basename).?.full_object_path; 5846 } 5847 const lci = comp.bin_file.options.libc_installation orelse return error.LibCInstallationNotAvailable; 5848 const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; 5849 const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename }); 5850 return full_path; 5851 } 5852 5853 fn wantBuildLibCFromSource(comp: Compilation) bool { 5854 const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) { 5855 .Obj => false, 5856 .Lib => comp.bin_file.options.link_mode == .Dynamic, 5857 .Exe => true, 5858 }; 5859 return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and 5860 comp.bin_file.options.libc_installation == null and 5861 comp.bin_file.options.target.ofmt != .c; 5862 } 5863 5864 fn wantBuildGLibCFromSource(comp: Compilation) bool { 5865 return comp.wantBuildLibCFromSource() and comp.getTarget().isGnuLibC(); 5866 } 5867 5868 fn wantBuildMuslFromSource(comp: Compilation) bool { 5869 return comp.wantBuildLibCFromSource() and comp.getTarget().isMusl() and 5870 !comp.getTarget().isWasm(); 5871 } 5872 5873 fn wantBuildWasiLibcFromSource(comp: Compilation) bool { 5874 return comp.wantBuildLibCFromSource() and comp.getTarget().isWasm() and 5875 comp.getTarget().os.tag == .wasi; 5876 } 5877 5878 fn wantBuildMinGWFromSource(comp: Compilation) bool { 5879 return comp.wantBuildLibCFromSource() and comp.getTarget().isMinGW(); 5880 } 5881 5882 fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { 5883 const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) { 5884 .Obj => false, 5885 .Lib => comp.bin_file.options.link_mode == .Dynamic, 5886 .Exe => true, 5887 }; 5888 return is_exe_or_dyn_lib and comp.bin_file.options.link_libunwind and 5889 comp.bin_file.options.target.ofmt != .c; 5890 } 5891 5892 fn setAllocFailure(comp: *Compilation) void { 5893 log.debug("memory allocation failure", .{}); 5894 comp.alloc_failure_occurred = true; 5895 } 5896 5897 /// Assumes that Compilation mutex is locked. 5898 /// See also `lockAndSetMiscFailure`. 5899 pub fn setMiscFailure( 5900 comp: *Compilation, 5901 tag: MiscTask, 5902 comptime format: []const u8, 5903 args: anytype, 5904 ) void { 5905 comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1) catch return comp.setAllocFailure(); 5906 const msg = std.fmt.allocPrint(comp.gpa, format, args) catch return comp.setAllocFailure(); 5907 const gop = comp.misc_failures.getOrPutAssumeCapacity(tag); 5908 if (gop.found_existing) { 5909 gop.value_ptr.deinit(comp.gpa); 5910 } 5911 gop.value_ptr.* = .{ .msg = msg }; 5912 } 5913 5914 /// See also `setMiscFailure`. 5915 pub fn lockAndSetMiscFailure( 5916 comp: *Compilation, 5917 tag: MiscTask, 5918 comptime format: []const u8, 5919 args: anytype, 5920 ) void { 5921 comp.mutex.lock(); 5922 defer comp.mutex.unlock(); 5923 5924 return setMiscFailure(comp, tag, format, args); 5925 } 5926 5927 fn parseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []const u8) Allocator.Error!void { 5928 var context_lines = std.ArrayList([]const u8).init(comp.gpa); 5929 defer context_lines.deinit(); 5930 5931 var current_err: ?*LldError = null; 5932 var lines = mem.splitSequence(u8, stderr, if (builtin.os.tag == .windows) "\r\n" else "\n"); 5933 while (lines.next()) |line| { 5934 if (mem.startsWith(u8, line, prefix ++ ":")) { 5935 if (current_err) |err| { 5936 err.context_lines = try context_lines.toOwnedSlice(); 5937 } 5938 5939 var split = std.mem.splitSequence(u8, line, "error: "); 5940 _ = split.first(); 5941 5942 const duped_msg = try std.fmt.allocPrint(comp.gpa, "{s}: {s}", .{ prefix, split.rest() }); 5943 errdefer comp.gpa.free(duped_msg); 5944 5945 current_err = try comp.lld_errors.addOne(comp.gpa); 5946 current_err.?.* = .{ .msg = duped_msg }; 5947 } else if (current_err != null) { 5948 const context_prefix = ">>> "; 5949 var trimmed = mem.trimRight(u8, line, &std.ascii.whitespace); 5950 if (mem.startsWith(u8, trimmed, context_prefix)) { 5951 trimmed = trimmed[context_prefix.len..]; 5952 } 5953 5954 if (trimmed.len > 0) { 5955 const duped_line = try comp.gpa.dupe(u8, trimmed); 5956 try context_lines.append(duped_line); 5957 } 5958 } 5959 } 5960 5961 if (current_err) |err| { 5962 err.context_lines = try context_lines.toOwnedSlice(); 5963 } 5964 } 5965 5966 pub fn lockAndParseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []const u8) void { 5967 comp.mutex.lock(); 5968 defer comp.mutex.unlock(); 5969 5970 comp.parseLldStderr(prefix, stderr) catch comp.setAllocFailure(); 5971 } 5972 5973 pub fn dump_argv(argv: []const []const u8) void { 5974 std.debug.getStderrMutex().lock(); 5975 defer std.debug.getStderrMutex().unlock(); 5976 const stderr = std.io.getStdErr().writer(); 5977 for (argv[0 .. argv.len - 1]) |arg| { 5978 nosuspend stderr.print("{s} ", .{arg}) catch return; 5979 } 5980 nosuspend stderr.print("{s}\n", .{argv[argv.len - 1]}) catch {}; 5981 } 5982 5983 pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend { 5984 if (comp.bin_file.options.use_llvm) return .stage2_llvm; 5985 const target = comp.bin_file.options.target; 5986 if (target.ofmt == .c) return .stage2_c; 5987 return switch (target.cpu.arch) { 5988 .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm, 5989 .arm, .armeb, .thumb, .thumbeb => .stage2_arm, 5990 .x86_64 => .stage2_x86_64, 5991 .x86 => .stage2_x86, 5992 .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64, 5993 .riscv64 => .stage2_riscv64, 5994 .sparc64 => .stage2_sparc64, 5995 .spirv64 => .stage2_spirv64, 5996 else => .other, 5997 }; 5998 } 5999 6000 pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Allocator.Error![:0]u8 { 6001 const tracy_trace = trace(@src()); 6002 defer tracy_trace.end(); 6003 6004 var buffer = std.ArrayList(u8).init(allocator); 6005 defer buffer.deinit(); 6006 6007 const target = comp.getTarget(); 6008 const generic_arch_name = target.cpu.arch.genericName(); 6009 const zig_backend = comp.getZigBackend(); 6010 6011 @setEvalBranchQuota(4000); 6012 try buffer.writer().print( 6013 \\const std = @import("std"); 6014 \\/// Zig version. When writing code that supports multiple versions of Zig, prefer 6015 \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. 6016 \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable; 6017 \\pub const zig_version_string = "{s}"; 6018 \\pub const zig_backend = std.builtin.CompilerBackend.{}; 6019 \\ 6020 \\pub const output_mode = std.builtin.OutputMode.{}; 6021 \\pub const link_mode = std.builtin.LinkMode.{}; 6022 \\pub const is_test = {}; 6023 \\pub const single_threaded = {}; 6024 \\pub const abi = std.Target.Abi.{}; 6025 \\pub const cpu: std.Target.Cpu = .{{ 6026 \\ .arch = .{}, 6027 \\ .model = &std.Target.{}.cpu.{}, 6028 \\ .features = std.Target.{}.featureSet(&[_]std.Target.{}.Feature{{ 6029 \\ 6030 , .{ 6031 build_options.version, 6032 std.zig.fmtId(@tagName(zig_backend)), 6033 std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)), 6034 std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)), 6035 comp.bin_file.options.is_test, 6036 comp.bin_file.options.single_threaded, 6037 std.zig.fmtId(@tagName(target.abi)), 6038 std.zig.fmtId(@tagName(target.cpu.arch)), 6039 std.zig.fmtId(generic_arch_name), 6040 std.zig.fmtId(target.cpu.model.name), 6041 std.zig.fmtId(generic_arch_name), 6042 std.zig.fmtId(generic_arch_name), 6043 }); 6044 6045 for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| { 6046 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); 6047 const is_enabled = target.cpu.features.isEnabled(index); 6048 if (is_enabled) { 6049 try buffer.writer().print(" .{},\n", .{std.zig.fmtId(feature.name)}); 6050 } 6051 } 6052 6053 try buffer.writer().print( 6054 \\ }}), 6055 \\}}; 6056 \\pub const os = std.Target.Os{{ 6057 \\ .tag = .{}, 6058 \\ .version_range = .{{ 6059 , 6060 .{std.zig.fmtId(@tagName(target.os.tag))}, 6061 ); 6062 6063 switch (target.os.getVersionRange()) { 6064 .none => try buffer.appendSlice(" .none = {} },\n"), 6065 .semver => |semver| try buffer.writer().print( 6066 \\ .semver = .{{ 6067 \\ .min = .{{ 6068 \\ .major = {}, 6069 \\ .minor = {}, 6070 \\ .patch = {}, 6071 \\ }}, 6072 \\ .max = .{{ 6073 \\ .major = {}, 6074 \\ .minor = {}, 6075 \\ .patch = {}, 6076 \\ }}, 6077 \\ }}}}, 6078 \\ 6079 , .{ 6080 semver.min.major, 6081 semver.min.minor, 6082 semver.min.patch, 6083 6084 semver.max.major, 6085 semver.max.minor, 6086 semver.max.patch, 6087 }), 6088 .linux => |linux| try buffer.writer().print( 6089 \\ .linux = .{{ 6090 \\ .range = .{{ 6091 \\ .min = .{{ 6092 \\ .major = {}, 6093 \\ .minor = {}, 6094 \\ .patch = {}, 6095 \\ }}, 6096 \\ .max = .{{ 6097 \\ .major = {}, 6098 \\ .minor = {}, 6099 \\ .patch = {}, 6100 \\ }}, 6101 \\ }}, 6102 \\ .glibc = .{{ 6103 \\ .major = {}, 6104 \\ .minor = {}, 6105 \\ .patch = {}, 6106 \\ }}, 6107 \\ }}}}, 6108 \\ 6109 , .{ 6110 linux.range.min.major, 6111 linux.range.min.minor, 6112 linux.range.min.patch, 6113 6114 linux.range.max.major, 6115 linux.range.max.minor, 6116 linux.range.max.patch, 6117 6118 linux.glibc.major, 6119 linux.glibc.minor, 6120 linux.glibc.patch, 6121 }), 6122 .windows => |windows| try buffer.writer().print( 6123 \\ .windows = .{{ 6124 \\ .min = {s}, 6125 \\ .max = {s}, 6126 \\ }}}}, 6127 \\ 6128 , 6129 .{ windows.min, windows.max }, 6130 ), 6131 } 6132 try buffer.appendSlice("};\n"); 6133 6134 // This is so that compiler_rt and libc.zig libraries know whether they 6135 // will eventually be linked with libc. They make different decisions 6136 // about what to export depending on whether another libc will be linked 6137 // in. For example, compiler_rt will not export the __chkstk symbol if it 6138 // knows libc will provide it, and likewise c.zig will not export memcpy. 6139 const link_libc = comp.bin_file.options.link_libc or 6140 (comp.bin_file.options.skip_linker_dependencies and comp.bin_file.options.parent_compilation_link_libc); 6141 6142 try buffer.writer().print( 6143 \\pub const target = std.Target{{ 6144 \\ .cpu = cpu, 6145 \\ .os = os, 6146 \\ .abi = abi, 6147 \\ .ofmt = object_format, 6148 \\}}; 6149 \\pub const object_format = std.Target.ObjectFormat.{}; 6150 \\pub const mode = std.builtin.OptimizeMode.{}; 6151 \\pub const link_libc = {}; 6152 \\pub const link_libcpp = {}; 6153 \\pub const have_error_return_tracing = {}; 6154 \\pub const valgrind_support = {}; 6155 \\pub const sanitize_thread = {}; 6156 \\pub const position_independent_code = {}; 6157 \\pub const position_independent_executable = {}; 6158 \\pub const strip_debug_info = {}; 6159 \\pub const code_model = std.builtin.CodeModel.{}; 6160 \\pub const omit_frame_pointer = {}; 6161 \\ 6162 , .{ 6163 std.zig.fmtId(@tagName(target.ofmt)), 6164 std.zig.fmtId(@tagName(comp.bin_file.options.optimize_mode)), 6165 link_libc, 6166 comp.bin_file.options.link_libcpp, 6167 comp.bin_file.options.error_return_tracing, 6168 comp.bin_file.options.valgrind, 6169 comp.bin_file.options.tsan, 6170 comp.bin_file.options.pic, 6171 comp.bin_file.options.pie, 6172 comp.bin_file.options.strip, 6173 std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)), 6174 comp.bin_file.options.omit_frame_pointer, 6175 }); 6176 6177 if (target.os.tag == .wasi) { 6178 const wasi_exec_model_fmt = std.zig.fmtId(@tagName(comp.bin_file.options.wasi_exec_model)); 6179 try buffer.writer().print( 6180 \\pub const wasi_exec_model = std.builtin.WasiExecModel.{}; 6181 \\ 6182 , .{wasi_exec_model_fmt}); 6183 } 6184 6185 if (comp.bin_file.options.is_test) { 6186 try buffer.appendSlice( 6187 \\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later 6188 \\ 6189 ); 6190 if (comp.test_evented_io) { 6191 try buffer.appendSlice( 6192 \\pub const test_io_mode = .evented; 6193 \\ 6194 ); 6195 } else { 6196 try buffer.appendSlice( 6197 \\pub const test_io_mode = .blocking; 6198 \\ 6199 ); 6200 } 6201 } 6202 6203 return buffer.toOwnedSliceSentinel(0); 6204 } 6205 6206 pub fn updateSubCompilation( 6207 parent_comp: *Compilation, 6208 sub_comp: *Compilation, 6209 misc_task: MiscTask, 6210 prog_node: *std.Progress.Node, 6211 ) !void { 6212 { 6213 var sub_node = prog_node.start(@tagName(misc_task), 0); 6214 sub_node.activate(); 6215 defer sub_node.end(); 6216 6217 try sub_comp.update(prog_node); 6218 } 6219 6220 // Look for compilation errors in this sub compilation 6221 const gpa = parent_comp.gpa; 6222 var keep_errors = false; 6223 var errors = try sub_comp.getAllErrorsAlloc(); 6224 defer if (!keep_errors) errors.deinit(gpa); 6225 6226 if (errors.errorMessageCount() > 0) { 6227 try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1); 6228 parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{ 6229 .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {s} failed", .{ 6230 @tagName(misc_task), 6231 }), 6232 .children = errors, 6233 }); 6234 keep_errors = true; 6235 return error.SubCompilationFailed; 6236 } 6237 } 6238 6239 fn buildOutputFromZig( 6240 comp: *Compilation, 6241 src_basename: []const u8, 6242 output_mode: std.builtin.OutputMode, 6243 out: *?CRTFile, 6244 misc_task_tag: MiscTask, 6245 prog_node: *std.Progress.Node, 6246 ) !void { 6247 const tracy_trace = trace(@src()); 6248 defer tracy_trace.end(); 6249 6250 std.debug.assert(output_mode != .Exe); 6251 6252 var main_pkg: Package = .{ 6253 .root_src_directory = comp.zig_lib_directory, 6254 .root_src_path = src_basename, 6255 }; 6256 defer main_pkg.deinitTable(comp.gpa); 6257 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; 6258 const target = comp.getTarget(); 6259 const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{ 6260 .root_name = root_name, 6261 .target = target, 6262 .output_mode = output_mode, 6263 }); 6264 defer comp.gpa.free(bin_basename); 6265 6266 const emit_bin = Compilation.EmitLoc{ 6267 .directory = null, // Put it in the cache directory. 6268 .basename = bin_basename, 6269 }; 6270 const sub_compilation = try Compilation.create(comp.gpa, .{ 6271 .global_cache_directory = comp.global_cache_directory, 6272 .local_cache_directory = comp.global_cache_directory, 6273 .zig_lib_directory = comp.zig_lib_directory, 6274 .cache_mode = .whole, 6275 .target = target, 6276 .root_name = root_name, 6277 .main_pkg = &main_pkg, 6278 .output_mode = output_mode, 6279 .thread_pool = comp.thread_pool, 6280 .libc_installation = comp.bin_file.options.libc_installation, 6281 .emit_bin = emit_bin, 6282 .optimize_mode = comp.compilerRtOptMode(), 6283 .link_mode = .Static, 6284 .function_sections = true, 6285 .no_builtin = true, 6286 .want_sanitize_c = false, 6287 .want_stack_check = false, 6288 .want_stack_protector = 0, 6289 .want_red_zone = comp.bin_file.options.red_zone, 6290 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, 6291 .want_valgrind = false, 6292 .want_tsan = false, 6293 .want_unwind_tables = comp.bin_file.options.eh_frame_hdr, 6294 .want_pic = comp.bin_file.options.pic, 6295 .want_pie = comp.bin_file.options.pie, 6296 .emit_h = null, 6297 .strip = comp.compilerRtStrip(), 6298 .is_native_os = comp.bin_file.options.is_native_os, 6299 .is_native_abi = comp.bin_file.options.is_native_abi, 6300 .self_exe_path = comp.self_exe_path, 6301 .verbose_cc = comp.verbose_cc, 6302 .verbose_link = comp.bin_file.options.verbose_link, 6303 .verbose_air = comp.verbose_air, 6304 .verbose_intern_pool = comp.verbose_intern_pool, 6305 .verbose_generic_instances = comp.verbose_intern_pool, 6306 .verbose_llvm_ir = comp.verbose_llvm_ir, 6307 .verbose_llvm_bc = comp.verbose_llvm_bc, 6308 .verbose_cimport = comp.verbose_cimport, 6309 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, 6310 .clang_passthrough_mode = comp.clang_passthrough_mode, 6311 .skip_linker_dependencies = true, 6312 .parent_compilation_link_libc = comp.bin_file.options.link_libc, 6313 }); 6314 defer sub_compilation.destroy(); 6315 6316 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); 6317 6318 assert(out.* == null); 6319 out.* = Compilation.CRTFile{ 6320 .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ 6321 sub_compilation.bin_file.options.emit.?.sub_path, 6322 }), 6323 .lock = sub_compilation.bin_file.toOwnedLock(), 6324 }; 6325 } 6326 6327 pub fn build_crt_file( 6328 comp: *Compilation, 6329 root_name: []const u8, 6330 output_mode: std.builtin.OutputMode, 6331 misc_task_tag: MiscTask, 6332 prog_node: *std.Progress.Node, 6333 c_source_files: []const Compilation.CSourceFile, 6334 ) !void { 6335 const tracy_trace = trace(@src()); 6336 defer tracy_trace.end(); 6337 6338 const target = comp.getTarget(); 6339 const basename = try std.zig.binNameAlloc(comp.gpa, .{ 6340 .root_name = root_name, 6341 .target = target, 6342 .output_mode = output_mode, 6343 }); 6344 errdefer comp.gpa.free(basename); 6345 6346 const sub_compilation = try Compilation.create(comp.gpa, .{ 6347 .local_cache_directory = comp.global_cache_directory, 6348 .global_cache_directory = comp.global_cache_directory, 6349 .zig_lib_directory = comp.zig_lib_directory, 6350 .cache_mode = .whole, 6351 .target = target, 6352 .root_name = root_name, 6353 .main_pkg = null, 6354 .output_mode = output_mode, 6355 .thread_pool = comp.thread_pool, 6356 .libc_installation = comp.bin_file.options.libc_installation, 6357 .emit_bin = .{ 6358 .directory = null, // Put it in the cache directory. 6359 .basename = basename, 6360 }, 6361 .optimize_mode = comp.compilerRtOptMode(), 6362 .want_sanitize_c = false, 6363 .want_stack_check = false, 6364 .want_stack_protector = 0, 6365 .want_red_zone = comp.bin_file.options.red_zone, 6366 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, 6367 .want_valgrind = false, 6368 .want_tsan = false, 6369 .want_pic = comp.bin_file.options.pic, 6370 .want_pie = comp.bin_file.options.pie, 6371 .want_lto = switch (output_mode) { 6372 .Lib => comp.bin_file.options.lto, 6373 .Obj, .Exe => false, 6374 }, 6375 .emit_h = null, 6376 .strip = comp.compilerRtStrip(), 6377 .is_native_os = comp.bin_file.options.is_native_os, 6378 .is_native_abi = comp.bin_file.options.is_native_abi, 6379 .self_exe_path = comp.self_exe_path, 6380 .c_source_files = c_source_files, 6381 .verbose_cc = comp.verbose_cc, 6382 .verbose_link = comp.bin_file.options.verbose_link, 6383 .verbose_air = comp.verbose_air, 6384 .verbose_intern_pool = comp.verbose_intern_pool, 6385 .verbose_generic_instances = comp.verbose_generic_instances, 6386 .verbose_llvm_ir = comp.verbose_llvm_ir, 6387 .verbose_llvm_bc = comp.verbose_llvm_bc, 6388 .verbose_cimport = comp.verbose_cimport, 6389 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, 6390 .clang_passthrough_mode = comp.clang_passthrough_mode, 6391 .skip_linker_dependencies = true, 6392 .parent_compilation_link_libc = comp.bin_file.options.link_libc, 6393 }); 6394 defer sub_compilation.destroy(); 6395 6396 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); 6397 6398 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); 6399 6400 comp.crt_files.putAssumeCapacityNoClobber(basename, .{ 6401 .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ 6402 sub_compilation.bin_file.options.emit.?.sub_path, 6403 }), 6404 .lock = sub_compilation.bin_file.toOwnedLock(), 6405 }); 6406 } 6407 6408 pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { 6409 // Avoid deadlocking on building import libs such as kernel32.lib 6410 // This can happen when the user uses `build-exe foo.obj -lkernel32` and 6411 // then when we create a sub-Compilation for zig libc, it also tries to 6412 // build kernel32.lib. 6413 if (comp.bin_file.options.skip_linker_dependencies) return; 6414 6415 // This happens when an `extern "foo"` function is referenced. 6416 // If we haven't seen this library yet and we're targeting Windows, we need 6417 // to queue up a work item to produce the DLL import library for this. 6418 const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name); 6419 if (!gop.found_existing and comp.getTarget().os.tag == .windows) { 6420 gop.value_ptr.* = .{ 6421 .needed = true, 6422 .weak = false, 6423 .path = null, 6424 }; 6425 try comp.work_queue.writeItem(.{ 6426 .windows_import_lib = comp.bin_file.options.system_libs.count() - 1, 6427 }); 6428 } 6429 } 6430 6431 /// This decides the optimization mode for all zig-provided libraries, including 6432 /// compiler-rt, libcxx, libc, libunwind, etc. 6433 pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode { 6434 if (comp.debug_compiler_runtime_libs) { 6435 return comp.bin_file.options.optimize_mode; 6436 } 6437 switch (comp.bin_file.options.optimize_mode) { 6438 .Debug, .ReleaseSafe => return target_util.defaultCompilerRtOptimizeMode(comp.getTarget()), 6439 .ReleaseFast => return .ReleaseFast, 6440 .ReleaseSmall => return .ReleaseSmall, 6441 } 6442 } 6443 6444 /// This decides whether to strip debug info for all zig-provided libraries, including 6445 /// compiler-rt, libcxx, libc, libunwind, etc. 6446 pub fn compilerRtStrip(comp: Compilation) bool { 6447 return comp.bin_file.options.strip; 6448 }