blob 3f2dbd0c (209570B) - Raw
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 const assert = std.debug.assert; 4 const io = std.io; 5 const fs = std.fs; 6 const mem = std.mem; 7 const process = std.process; 8 const Allocator = mem.Allocator; 9 const ArrayList = std.ArrayList; 10 const Ast = std.zig.Ast; 11 const warn = std.log.warn; 12 13 const tracy = @import("tracy.zig"); 14 const Compilation = @import("Compilation.zig"); 15 const link = @import("link.zig"); 16 const Package = @import("Package.zig"); 17 const build_options = @import("build_options"); 18 const introspect = @import("introspect.zig"); 19 const LibCInstallation = @import("libc_installation.zig").LibCInstallation; 20 const wasi_libc = @import("wasi_libc.zig"); 21 const translate_c = @import("translate_c.zig"); 22 const Cache = @import("Cache.zig"); 23 const target_util = @import("target.zig"); 24 const ThreadPool = @import("ThreadPool.zig"); 25 const crash_report = @import("crash_report.zig"); 26 27 // Crash report needs to override the panic handler and other root decls 28 pub usingnamespace crash_report.root_decls; 29 30 pub fn fatal(comptime format: []const u8, args: anytype) noreturn { 31 std.log.err(format, args); 32 process.exit(1); 33 } 34 35 /// There are many assumptions in the entire codebase that Zig source files can 36 /// be byte-indexed with a u32 integer. 37 pub const max_src_size = std.math.maxInt(u32); 38 39 pub const debug_extensions_enabled = builtin.mode == .Debug; 40 41 pub const Color = enum { 42 auto, 43 off, 44 on, 45 }; 46 47 const normal_usage = 48 \\Usage: zig [command] [options] 49 \\ 50 \\Commands: 51 \\ 52 \\ build Build project from build.zig 53 \\ init-exe Initialize a `zig build` application in the cwd 54 \\ init-lib Initialize a `zig build` library in the cwd 55 \\ 56 \\ ast-check Look for simple compile errors in any set of files 57 \\ build-exe Create executable from source or object files 58 \\ build-lib Create library from source or object files 59 \\ build-obj Create object from source or object files 60 \\ fmt Reformat Zig source into canonical form 61 \\ run Create executable and run immediately 62 \\ test Create and run a test build 63 \\ translate-c Convert C code to Zig code 64 \\ 65 \\ ar Use Zig as a drop-in archiver 66 \\ cc Use Zig as a drop-in C compiler 67 \\ c++ Use Zig as a drop-in C++ compiler 68 \\ dlltool Use Zig as a drop-in dlltool.exe 69 \\ lib Use Zig as a drop-in lib.exe 70 \\ ranlib Use Zig as a drop-in ranlib 71 \\ 72 \\ env Print lib path, std path, cache directory, and version 73 \\ help Print this help and exit 74 \\ libc Display native libc paths file or validate one 75 \\ targets List available compilation targets 76 \\ version Print version number and exit 77 \\ zen Print Zen of Zig and exit 78 \\ 79 \\General Options: 80 \\ 81 \\ -h, --help Print command-specific usage 82 \\ 83 ; 84 85 const debug_usage = normal_usage ++ 86 \\ 87 \\Debug Commands: 88 \\ 89 \\ changelist Compute mappings from old ZIR to new ZIR 90 \\ 91 ; 92 93 const usage = if (debug_extensions_enabled) debug_usage else normal_usage; 94 95 pub const log_level: std.log.Level = switch (builtin.mode) { 96 .Debug => .debug, 97 .ReleaseSafe, .ReleaseFast => .info, 98 .ReleaseSmall => .err, 99 }; 100 101 var log_scopes: std.ArrayListUnmanaged([]const u8) = .{}; 102 103 pub fn log( 104 comptime level: std.log.Level, 105 comptime scope: @TypeOf(.EnumLiteral), 106 comptime format: []const u8, 107 args: anytype, 108 ) void { 109 // Hide debug messages unless: 110 // * logging enabled with `-Dlog`. 111 // * the --debug-log arg for the scope has been provided 112 if (@enumToInt(level) > @enumToInt(std.log.level) or 113 @enumToInt(level) > @enumToInt(std.log.Level.info)) 114 { 115 if (!build_options.enable_logging) return; 116 117 const scope_name = @tagName(scope); 118 for (log_scopes.items) |log_scope| { 119 if (mem.eql(u8, log_scope, scope_name)) 120 break; 121 } else return; 122 } 123 124 const prefix1 = comptime level.asText(); 125 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; 126 127 // Print the message to stderr, silently ignoring any errors 128 std.debug.print(prefix1 ++ prefix2 ++ format ++ "\n", args); 129 } 130 131 var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{ 132 .stack_trace_frames = build_options.mem_leak_frames, 133 }){}; 134 135 pub fn main() anyerror!void { 136 crash_report.initialize(); 137 138 var gpa_need_deinit = false; 139 const gpa = gpa: { 140 if (!builtin.link_libc) { 141 gpa_need_deinit = true; 142 break :gpa general_purpose_allocator.allocator(); 143 } 144 // We would prefer to use raw libc allocator here, but cannot 145 // use it if it won't support the alignment we need. 146 if (@alignOf(std.c.max_align_t) < @alignOf(i128)) { 147 break :gpa std.heap.c_allocator; 148 } 149 break :gpa std.heap.raw_c_allocator; 150 }; 151 defer if (gpa_need_deinit) { 152 _ = general_purpose_allocator.deinit(); 153 }; 154 var arena_instance = std.heap.ArenaAllocator.init(gpa); 155 defer arena_instance.deinit(); 156 const arena = arena_instance.allocator(); 157 158 const args = try process.argsAlloc(arena); 159 160 if (tracy.enable_allocation) { 161 var gpa_tracy = tracy.tracyAllocator(gpa); 162 return mainArgs(gpa_tracy.allocator(), arena, args); 163 } 164 165 return mainArgs(gpa, arena, args); 166 } 167 168 pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { 169 if (args.len <= 1) { 170 std.log.info("{s}", .{usage}); 171 fatal("expected command argument", .{}); 172 } 173 174 if (std.process.can_execv and std.os.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) { 175 // In this case we have accidentally invoked ourselves as "the system C compiler" 176 // to figure out where libc is installed. This is essentially infinite recursion 177 // via child process execution due to the CC environment variable pointing to Zig. 178 // Here we ignore the CC environment variable and exec `cc` as a child process. 179 // However it's possible Zig is installed as *that* C compiler as well, which is 180 // why we have this additional environment variable here to check. 181 var env_map = try std.process.getEnvMap(arena); 182 183 const inf_loop_env_key = "ZIG_IS_TRYING_TO_NOT_CALL_ITSELF"; 184 if (env_map.get(inf_loop_env_key) != null) { 185 fatal("The compilation links against libc, but Zig is unable to provide a libc " ++ 186 "for this operating system, and no --libc " ++ 187 "parameter was provided, so Zig attempted to invoke the system C compiler " ++ 188 "in order to determine where libc is installed. However the system C " ++ 189 "compiler is `zig cc`, so no libc installation was found.", .{}); 190 } 191 try env_map.put(inf_loop_env_key, "1"); 192 193 // Some programs such as CMake will strip the `cc` and subsequent args from the 194 // CC environment variable. We detect and support this scenario here because of 195 // the ZIG_IS_DETECTING_LIBC_PATHS environment variable. 196 if (mem.eql(u8, args[1], "cc")) { 197 return std.process.execve(arena, args[1..], &env_map); 198 } else { 199 const modified_args = try arena.dupe([]const u8, args); 200 modified_args[0] = "cc"; 201 return std.process.execve(arena, modified_args, &env_map); 202 } 203 } 204 205 defer log_scopes.deinit(gpa); 206 207 const cmd = args[1]; 208 const cmd_args = args[2..]; 209 if (mem.eql(u8, cmd, "build-exe")) { 210 return buildOutputType(gpa, arena, args, .{ .build = .Exe }); 211 } else if (mem.eql(u8, cmd, "build-lib")) { 212 return buildOutputType(gpa, arena, args, .{ .build = .Lib }); 213 } else if (mem.eql(u8, cmd, "build-obj")) { 214 return buildOutputType(gpa, arena, args, .{ .build = .Obj }); 215 } else if (mem.eql(u8, cmd, "test")) { 216 return buildOutputType(gpa, arena, args, .zig_test); 217 } else if (mem.eql(u8, cmd, "run")) { 218 return buildOutputType(gpa, arena, args, .run); 219 } else if (mem.eql(u8, cmd, "dlltool") or 220 mem.eql(u8, cmd, "ranlib") or 221 mem.eql(u8, cmd, "lib") or 222 mem.eql(u8, cmd, "ar")) 223 { 224 return punt_to_llvm_ar(arena, args); 225 } else if (mem.eql(u8, cmd, "cc")) { 226 return buildOutputType(gpa, arena, args, .cc); 227 } else if (mem.eql(u8, cmd, "c++")) { 228 return buildOutputType(gpa, arena, args, .cpp); 229 } else if (mem.eql(u8, cmd, "translate-c")) { 230 return buildOutputType(gpa, arena, args, .translate_c); 231 } else if (mem.eql(u8, cmd, "clang") or 232 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) 233 { 234 return punt_to_clang(arena, args); 235 } else if (mem.eql(u8, cmd, "ld.lld") or 236 mem.eql(u8, cmd, "lld-link") or 237 mem.eql(u8, cmd, "wasm-ld")) 238 { 239 return punt_to_lld(arena, args); 240 } else if (mem.eql(u8, cmd, "build")) { 241 return cmdBuild(gpa, arena, cmd_args); 242 } else if (mem.eql(u8, cmd, "fmt")) { 243 return cmdFmt(gpa, arena, cmd_args); 244 } else if (mem.eql(u8, cmd, "libc")) { 245 return cmdLibC(gpa, cmd_args); 246 } else if (mem.eql(u8, cmd, "init-exe")) { 247 return cmdInit(gpa, arena, cmd_args, .Exe); 248 } else if (mem.eql(u8, cmd, "init-lib")) { 249 return cmdInit(gpa, arena, cmd_args, .Lib); 250 } else if (mem.eql(u8, cmd, "targets")) { 251 const info = try detectNativeTargetInfo(arena, .{}); 252 const stdout = io.getStdOut().writer(); 253 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target); 254 } else if (mem.eql(u8, cmd, "version")) { 255 return std.io.getStdOut().writeAll(build_options.version ++ "\n"); 256 } else if (mem.eql(u8, cmd, "env")) { 257 return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer()); 258 } else if (mem.eql(u8, cmd, "zen")) { 259 return io.getStdOut().writeAll(info_zen); 260 } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) { 261 return io.getStdOut().writeAll(usage); 262 } else if (mem.eql(u8, cmd, "ast-check")) { 263 return cmdAstCheck(gpa, arena, cmd_args); 264 } else if (debug_extensions_enabled and mem.eql(u8, cmd, "changelist")) { 265 return cmdChangelist(gpa, arena, cmd_args); 266 } else { 267 std.log.info("{s}", .{usage}); 268 fatal("unknown command: {s}", .{args[1]}); 269 } 270 } 271 272 const usage_build_generic = 273 \\Usage: zig build-exe [options] [files] 274 \\ zig build-lib [options] [files] 275 \\ zig build-obj [options] [files] 276 \\ zig test [options] [files] 277 \\ zig run [options] [files] [-- [args]] 278 \\ zig translate-c [options] [file] 279 \\ 280 \\Supported file types: 281 \\ .zig Zig source code 282 \\ .o ELF object file 283 \\ .o Mach-O (macOS) object file 284 \\ .o WebAssembly object file 285 \\ .obj COFF (Windows) object file 286 \\ .lib COFF (Windows) static library 287 \\ .a ELF static library 288 \\ .a Mach-O (macOS) static library 289 \\ .a WebAssembly static library 290 \\ .so ELF shared object (dynamic link) 291 \\ .dll Windows Dynamic Link Library 292 \\ .dylib Mach-O (macOS) dynamic library 293 \\ .tbd (macOS) text-based dylib definition 294 \\ .s Target-specific assembly source code 295 \\ .S Assembly with C preprocessor (requires LLVM extensions) 296 \\ .c C source code (requires LLVM extensions) 297 \\ .cxx .cc .C .cpp C++ source code (requires LLVM extensions) 298 \\ .m Objective-C source code (requires LLVM extensions) 299 \\ .mm Objective-C++ source code (requires LLVM extensions) 300 \\ .bc LLVM IR Module (requires LLVM extensions) 301 \\ 302 \\General Options: 303 \\ -h, --help Print this help and exit 304 \\ --watch Enable compiler REPL 305 \\ --color [auto|off|on] Enable or disable colored error messages 306 \\ -femit-bin[=path] (default) Output machine code 307 \\ -fno-emit-bin Do not output machine code 308 \\ -femit-asm[=path] Output .s (assembly code) 309 \\ -fno-emit-asm (default) Do not output .s (assembly code) 310 \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions) 311 \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR 312 \\ -femit-llvm-bc[=path] Produce a LLVM module as a .bc file (requires LLVM extensions) 313 \\ -fno-emit-llvm-bc (default) Do not produce a LLVM module as a .bc file 314 \\ -femit-h[=path] Generate a C header file (.h) 315 \\ -fno-emit-h (default) Do not generate a C header file (.h) 316 \\ -femit-docs[=path] Create a docs/ dir with html documentation 317 \\ -fno-emit-docs (default) Do not produce docs/ dir with html documentation 318 \\ -femit-analysis[=path] Write analysis JSON file with type information 319 \\ -fno-emit-analysis (default) Do not write analysis JSON file with type information 320 \\ -femit-implib[=path] (default) Produce an import .lib when building a Windows DLL 321 \\ -fno-emit-implib Do not produce an import .lib when building a Windows DLL 322 \\ --show-builtin Output the source of @import("builtin") then exit 323 \\ --cache-dir [path] Override the local cache directory 324 \\ --global-cache-dir [path] Override the global cache directory 325 \\ --zig-lib-dir [path] Override path to Zig installation lib directory 326 \\ --enable-cache Output to cache directory; print path to stdout 327 \\ 328 \\Compile Options: 329 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command 330 \\ -mcpu [cpu] Specify target CPU and feature set 331 \\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses 332 \\ small|kernel| 333 \\ medium|large] 334 \\ -mred-zone Force-enable the "red-zone" 335 \\ -mno-red-zone Force-disable the "red-zone" 336 \\ -fomit-frame-pointer Omit the stack frame pointer 337 \\ -fno-omit-frame-pointer Store the stack frame pointer 338 \\ -mexec-model=[value] (WASI) Execution model 339 \\ --name [name] Override root name (not a file path) 340 \\ -O [mode] Choose what to optimize for 341 \\ Debug (default) Optimizations off, safety on 342 \\ ReleaseFast Optimizations on, safety off 343 \\ ReleaseSafe Optimizations on, safety on 344 \\ ReleaseSmall Optimize for small binary, safety off 345 \\ --pkg-begin [name] [path] Make pkg available to import and push current pkg 346 \\ --pkg-end Pop current pkg 347 \\ --main-pkg-path Set the directory of the root package 348 \\ -fPIC Force-enable Position Independent Code 349 \\ -fno-PIC Force-disable Position Independent Code 350 \\ -fPIE Force-enable Position Independent Executable 351 \\ -fno-PIE Force-disable Position Independent Executable 352 \\ -flto Force-enable Link Time Optimization (requires LLVM extensions) 353 \\ -fno-lto Force-disable Link Time Optimization 354 \\ -fstack-check Enable stack probing in unsafe builds 355 \\ -fno-stack-check Disable stack probing in safe builds 356 \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds 357 \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds 358 \\ -fvalgrind Include valgrind client requests in release builds 359 \\ -fno-valgrind Omit valgrind client requests in debug builds 360 \\ -fsanitize-thread Enable Thread Sanitizer 361 \\ -fno-sanitize-thread Disable Thread Sanitizer 362 \\ -fdll-export-fns Mark exported functions as DLL exports (Windows) 363 \\ -fno-dll-export-fns Force-disable marking exported functions as DLL exports 364 \\ -funwind-tables Always produce unwind table entries for all functions 365 \\ -fno-unwind-tables Never produce unwind table entries 366 \\ -fLLVM Force using LLVM as the codegen backend 367 \\ -fno-LLVM Prevent using LLVM as the codegen backend 368 \\ -fClang Force using Clang as the C/C++ compilation backend 369 \\ -fno-Clang Prevent using Clang as the C/C++ compilation backend 370 \\ -fstage1 Force using bootstrap compiler as the codegen backend 371 \\ -fno-stage1 Prevent using bootstrap compiler as the codegen backend 372 \\ -fsingle-threaded Code assumes there is only one thread 373 \\ -fno-single-threaded Code may not assume there is only one thread 374 \\ --strip Omit debug symbols 375 \\ -ofmt=[mode] Override target object format 376 \\ elf Executable and Linking Format 377 \\ c C source code 378 \\ wasm WebAssembly 379 \\ coff Common Object File Format (Windows) 380 \\ macho macOS relocatables 381 \\ spirv Standard, Portable Intermediate Representation V (SPIR-V) 382 \\ plan9 Plan 9 from Bell Labs object format 383 \\ hex (planned feature) Intel IHEX 384 \\ raw (planned feature) Dump machine code directly 385 \\ -dirafter [dir] Add directory to AFTER include search path 386 \\ -isystem [dir] Add directory to SYSTEM include search path 387 \\ -I[dir] Add directory to include search path 388 \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted) 389 \\ --libc [file] Provide a file which specifies libc paths 390 \\ -cflags [flags] -- Set extra flags for the next positional C source files 391 \\ -ffunction-sections Places each function in a separate section 392 \\ 393 \\Link Options: 394 \\ -l[lib], --library [lib] Link against system library (only if actually used) 395 \\ -needed-l[lib], Link against system library (even if unused) 396 \\ --needed-library [lib] 397 \\ -L[d], --library-directory [d] Add a directory to the library search path 398 \\ -T[script], --script [script] Use a custom linker script 399 \\ --version-script [path] Provide a version .map file 400 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) 401 \\ --sysroot [path] Set the system root directory (usually /) 402 \\ --version [ver] Dynamic library semver 403 \\ -fsoname[=name] Override the default SONAME value 404 \\ -fno-soname Disable emitting a SONAME 405 \\ -fLLD Force using LLD as the linker 406 \\ -fno-LLD Prevent using LLD as the linker 407 \\ -fcompiler-rt Always include compiler-rt symbols in output 408 \\ -fno-compiler-rt Prevent including compiler-rt symbols in output 409 \\ -rdynamic Add all symbols to the dynamic symbol table 410 \\ -rpath [path] Add directory to the runtime library search path 411 \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library 412 \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library 413 \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries 414 \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries 415 \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker 416 \\ --emit-relocs Enable output of relocation sections for post build tools 417 \\ -z [arg] Set linker extension flags 418 \\ nodelete Indicate that the object cannot be deleted from a process 419 \\ notext Permit read-only relocations in read-only segments 420 \\ defs Force a fatal error if any undefined symbols remain 421 \\ origin Indicate that the object must have its origin processed 422 \\ noexecstack Indicate that the object requires an executable stack 423 \\ now Force all relocations to be processed on load 424 \\ relro Force all relocations to be resolved and be read-only on load 425 \\ -dynamic Force output to be dynamically linked 426 \\ -static Force output to be statically linked 427 \\ -Bsymbolic Bind global references locally 428 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker 429 \\ --stack [size] Override default stack size 430 \\ --image-base [addr] Set base address for executable image 431 \\ -framework [name] (Darwin) link against framework 432 \\ -F[dir] (Darwin) add search path for frameworks 433 \\ -install_name=[value] (Darwin) add dylib's install name 434 \\ --import-memory (WebAssembly) import memory from the environment 435 \\ --import-table (WebAssembly) import function table from the host environment 436 \\ --export-table (WebAssembly) export function table to the host environment 437 \\ --initial-memory=[bytes] (WebAssembly) initial size of the linear memory 438 \\ --max-memory=[bytes] (WebAssembly) maximum size of the linear memory 439 \\ --global-base=[addr] (WebAssembly) where to start to place global data 440 \\ --export=[value] (WebAssembly) Force a symbol to be exported 441 \\ 442 \\Test Options: 443 \\ --test-filter [text] Skip tests that do not match filter 444 \\ --test-name-prefix [text] Add prefix to all tests 445 \\ --test-cmd [arg] Specify test execution command one arg at a time 446 \\ --test-cmd-bin Appends test binary path to test cmd args 447 \\ --test-evented-io Runs the test in evented I/O mode 448 \\ --test-no-exec Compiles test binary without running it 449 \\ 450 \\Debug Options (Zig Compiler Development): 451 \\ -ftime-report Print timing diagnostics 452 \\ -fstack-report Print stack size diagnostics 453 \\ --verbose-link Display linker invocations 454 \\ --verbose-cc Display C compiler invocations 455 \\ --verbose-air Enable compiler debug output for Zig AIR 456 \\ --verbose-mir Enable compiler debug output for Zig MIR 457 \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR 458 \\ --verbose-cimport Enable compiler debug output for C imports 459 \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features 460 \\ --debug-log [scope] Enable printing debug/info log messages for scope 461 \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error 462 \\ --debug-link-snapshot Enable dumping of the linker's state in JSON format 463 \\ 464 ; 465 466 const repl_help = 467 \\Commands: 468 \\ update Detect changes to source files and update output files. 469 \\ run Execute the output file, if it is an executable or test. 470 \\ update-and-run Perform an `update` followed by `run`. 471 \\ help Print this text 472 \\ exit Quit this repl 473 \\ 474 ; 475 476 const SOName = union(enum) { 477 no, 478 yes_default_value, 479 yes: []const u8, 480 }; 481 482 const EmitBin = union(enum) { 483 no, 484 yes_default_path, 485 yes: []const u8, 486 yes_a_out, 487 }; 488 489 const Emit = union(enum) { 490 no, 491 yes_default_path, 492 yes: []const u8, 493 494 const Resolved = struct { 495 data: ?Compilation.EmitLoc, 496 dir: ?fs.Dir, 497 498 fn deinit(self: *Resolved) void { 499 if (self.dir) |*dir| { 500 dir.close(); 501 } 502 } 503 }; 504 505 fn resolve(emit: Emit, default_basename: []const u8) !Resolved { 506 var resolved: Resolved = .{ .data = null, .dir = null }; 507 errdefer resolved.deinit(); 508 509 switch (emit) { 510 .no => {}, 511 .yes_default_path => { 512 resolved.data = Compilation.EmitLoc{ 513 .directory = .{ .path = null, .handle = fs.cwd() }, 514 .basename = default_basename, 515 }; 516 }, 517 .yes => |full_path| { 518 const basename = fs.path.basename(full_path); 519 if (fs.path.dirname(full_path)) |dirname| { 520 const handle = try fs.cwd().openDir(dirname, .{}); 521 resolved = .{ 522 .dir = handle, 523 .data = Compilation.EmitLoc{ 524 .basename = basename, 525 .directory = .{ 526 .path = dirname, 527 .handle = handle, 528 }, 529 }, 530 }; 531 } else { 532 resolved.data = Compilation.EmitLoc{ 533 .basename = basename, 534 .directory = .{ .path = null, .handle = fs.cwd() }, 535 }; 536 } 537 }, 538 } 539 return resolved; 540 } 541 }; 542 543 fn optionalStringEnvVar(arena: Allocator, name: []const u8) !?[]const u8 { 544 if (std.process.getEnvVarOwned(arena, name)) |value| { 545 return value; 546 } else |err| switch (err) { 547 error.EnvironmentVariableNotFound => return null, 548 else => |e| return e, 549 } 550 } 551 552 const ArgMode = union(enum) { 553 build: std.builtin.OutputMode, 554 cc, 555 cpp, 556 translate_c, 557 zig_test, 558 run, 559 }; 560 561 fn buildOutputType( 562 gpa: Allocator, 563 arena: Allocator, 564 all_args: []const []const u8, 565 arg_mode: ArgMode, 566 ) !void { 567 var color: Color = .auto; 568 var optimize_mode: std.builtin.Mode = .Debug; 569 var provided_name: ?[]const u8 = null; 570 var link_mode: ?std.builtin.LinkMode = null; 571 var dll_export_fns: ?bool = null; 572 var single_threaded: ?bool = null; 573 var root_src_file: ?[]const u8 = null; 574 var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 }; 575 var have_version = false; 576 var compatibility_version: ?std.builtin.Version = null; 577 var strip = false; 578 var function_sections = false; 579 var watch = false; 580 var debug_compile_errors = false; 581 var verbose_link = std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); 582 var verbose_cc = std.process.hasEnvVarConstant("ZIG_VERBOSE_CC"); 583 var verbose_air = false; 584 var verbose_mir = false; 585 var verbose_llvm_ir = false; 586 var verbose_cimport = false; 587 var verbose_llvm_cpu_features = false; 588 var time_report = false; 589 var stack_report = false; 590 var show_builtin = false; 591 var emit_bin: EmitBin = .yes_default_path; 592 var emit_asm: Emit = .no; 593 var emit_llvm_ir: Emit = .no; 594 var emit_llvm_bc: Emit = .no; 595 var emit_docs: Emit = .no; 596 var emit_analysis: Emit = .no; 597 var emit_implib: Emit = .yes_default_path; 598 var emit_implib_arg_provided = false; 599 var target_arch_os_abi: []const u8 = "native"; 600 var target_mcpu: ?[]const u8 = null; 601 var target_dynamic_linker: ?[]const u8 = null; 602 var target_ofmt: ?[]const u8 = null; 603 var output_mode: std.builtin.OutputMode = undefined; 604 var emit_h: Emit = .no; 605 var soname: SOName = undefined; 606 var ensure_libc_on_non_freestanding = false; 607 var ensure_libcpp_on_non_freestanding = false; 608 var link_libc = false; 609 var link_libcpp = false; 610 var link_libunwind = false; 611 var want_native_include_dirs = false; 612 var enable_cache: ?bool = null; 613 var want_pic: ?bool = null; 614 var want_pie: ?bool = null; 615 var want_lto: ?bool = null; 616 var want_unwind_tables: ?bool = null; 617 var want_sanitize_c: ?bool = null; 618 var want_stack_check: ?bool = null; 619 var want_red_zone: ?bool = null; 620 var omit_frame_pointer: ?bool = null; 621 var want_valgrind: ?bool = null; 622 var want_tsan: ?bool = null; 623 var want_compiler_rt: ?bool = null; 624 var rdynamic: bool = false; 625 var linker_script: ?[]const u8 = null; 626 var version_script: ?[]const u8 = null; 627 var disable_c_depfile = false; 628 var linker_gc_sections: ?bool = null; 629 var linker_allow_shlib_undefined: ?bool = null; 630 var linker_bind_global_refs_locally: ?bool = null; 631 var linker_import_memory: ?bool = null; 632 var linker_import_table: bool = false; 633 var linker_export_table: bool = false; 634 var linker_initial_memory: ?u64 = null; 635 var linker_max_memory: ?u64 = null; 636 var linker_global_base: ?u64 = null; 637 var linker_z_nodelete = false; 638 var linker_z_notext = false; 639 var linker_z_defs = false; 640 var linker_z_origin = false; 641 var linker_z_noexecstack = false; 642 var linker_z_now = false; 643 var linker_z_relro = false; 644 var linker_tsaware = false; 645 var linker_nxcompat = false; 646 var linker_dynamicbase = false; 647 var linker_optimization: ?u8 = null; 648 var test_evented_io = false; 649 var test_no_exec = false; 650 var stack_size_override: ?u64 = null; 651 var image_base_override: ?u64 = null; 652 var use_llvm: ?bool = null; 653 var use_lld: ?bool = null; 654 var use_clang: ?bool = null; 655 var use_stage1: ?bool = null; 656 var link_eh_frame_hdr = false; 657 var link_emit_relocs = false; 658 var each_lib_rpath: ?bool = null; 659 var sysroot: ?[]const u8 = null; 660 var libc_paths_file: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIBC"); 661 var machine_code_model: std.builtin.CodeModel = .default; 662 var runtime_args_start: ?usize = null; 663 var test_filter: ?[]const u8 = null; 664 var test_name_prefix: ?[]const u8 = null; 665 var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR"); 666 var override_global_cache_dir: ?[]const u8 = null; 667 var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR"); 668 var main_pkg_path: ?[]const u8 = null; 669 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; 670 var subsystem: ?std.Target.SubSystem = null; 671 var major_subsystem_version: ?u32 = null; 672 var minor_subsystem_version: ?u32 = null; 673 var wasi_exec_model: ?std.builtin.WasiExecModel = null; 674 var enable_link_snapshots: bool = false; 675 var native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null; 676 var install_name: ?[]const u8 = null; 677 678 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. 679 // This array is populated by zig cc frontend and then has to be converted to zig-style 680 // CPU features. 681 var llvm_m_args = std.ArrayList([]const u8).init(gpa); 682 defer llvm_m_args.deinit(); 683 684 var system_libs = std.StringArrayHashMap(Compilation.SystemLib).init(gpa); 685 defer system_libs.deinit(); 686 687 var static_libs = std.ArrayList([]const u8).init(gpa); 688 defer static_libs.deinit(); 689 690 var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(gpa); 691 defer wasi_emulated_libs.deinit(); 692 693 var clang_argv = std.ArrayList([]const u8).init(gpa); 694 defer clang_argv.deinit(); 695 696 var extra_cflags = std.ArrayList([]const u8).init(gpa); 697 defer extra_cflags.deinit(); 698 699 var lib_dirs = std.ArrayList([]const u8).init(gpa); 700 defer lib_dirs.deinit(); 701 702 var rpath_list = std.ArrayList([]const u8).init(gpa); 703 defer rpath_list.deinit(); 704 705 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa); 706 defer c_source_files.deinit(); 707 708 var link_objects = std.ArrayList([]const u8).init(gpa); 709 defer link_objects.deinit(); 710 711 var framework_dirs = std.ArrayList([]const u8).init(gpa); 712 defer framework_dirs.deinit(); 713 714 var frameworks = std.ArrayList([]const u8).init(gpa); 715 defer frameworks.deinit(); 716 717 // null means replace with the test executable binary 718 var test_exec_args = std.ArrayList(?[]const u8).init(gpa); 719 defer test_exec_args.deinit(); 720 721 var linker_export_symbol_names = std.ArrayList([]const u8).init(gpa); 722 defer linker_export_symbol_names.deinit(); 723 724 // This package only exists to clean up the code parsing --pkg-begin and 725 // --pkg-end flags. Use dummy values that are safe for the destroy call. 726 var pkg_tree_root: Package = .{ 727 .root_src_directory = .{ .path = null, .handle = fs.cwd() }, 728 .root_src_path = &[0]u8{}, 729 }; 730 defer freePkgTree(gpa, &pkg_tree_root, false); 731 var cur_pkg: *Package = &pkg_tree_root; 732 733 // before arg parsing, check for the NO_COLOR environment variable 734 // if it exists, default the color setting to .off 735 // explicit --color arguments will still override this setting. 736 color = if (std.process.hasEnvVarConstant("NO_COLOR")) .off else .auto; 737 738 switch (arg_mode) { 739 .build, .translate_c, .zig_test, .run => { 740 var optimize_mode_string: ?[]const u8 = null; 741 switch (arg_mode) { 742 .build => |m| { 743 output_mode = m; 744 }, 745 .translate_c => { 746 emit_bin = .no; 747 output_mode = .Obj; 748 }, 749 .zig_test, .run => { 750 output_mode = .Exe; 751 }, 752 else => unreachable, 753 } 754 755 soname = .yes_default_value; 756 const args = all_args[2..]; 757 var i: usize = 0; 758 args_loop: while (i < args.len) : (i += 1) { 759 const arg = args[i]; 760 if (mem.startsWith(u8, arg, "-")) { 761 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { 762 try io.getStdOut().writeAll(usage_build_generic); 763 return cleanExit(); 764 } else if (mem.eql(u8, arg, "--")) { 765 if (arg_mode == .run) { 766 // The index refers to all_args so skip `zig` `run` 767 // and `--` 768 runtime_args_start = i + 3; 769 break :args_loop; 770 } else { 771 fatal("unexpected end-of-parameter mark: --", .{}); 772 } 773 } else if (mem.eql(u8, arg, "--pkg-begin")) { 774 if (i + 2 >= args.len) fatal("Expected 2 arguments after {s}", .{arg}); 775 i += 1; 776 const pkg_name = args[i]; 777 i += 1; 778 const pkg_path = args[i]; 779 780 const new_cur_pkg = Package.create( 781 gpa, 782 fs.path.dirname(pkg_path), 783 fs.path.basename(pkg_path), 784 ) catch |err| { 785 fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) }); 786 }; 787 try cur_pkg.addAndAdopt(gpa, pkg_name, new_cur_pkg); 788 cur_pkg = new_cur_pkg; 789 } else if (mem.eql(u8, arg, "--pkg-end")) { 790 cur_pkg = cur_pkg.parent orelse 791 fatal("encountered --pkg-end with no matching --pkg-begin", .{}); 792 } else if (mem.eql(u8, arg, "--main-pkg-path")) { 793 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 794 i += 1; 795 main_pkg_path = args[i]; 796 } else if (mem.eql(u8, arg, "-cflags")) { 797 extra_cflags.shrinkRetainingCapacity(0); 798 while (true) { 799 i += 1; 800 if (i >= args.len) fatal("expected -- after -cflags", .{}); 801 if (mem.eql(u8, args[i], "--")) break; 802 try extra_cflags.append(args[i]); 803 } 804 } else if (mem.eql(u8, arg, "--color")) { 805 if (i + 1 >= args.len) { 806 fatal("expected [auto|on|off] after --color", .{}); 807 } 808 i += 1; 809 const next_arg = args[i]; 810 color = std.meta.stringToEnum(Color, next_arg) orelse { 811 fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); 812 }; 813 } else if (mem.eql(u8, arg, "--subsystem")) { 814 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 815 i += 1; 816 if (mem.eql(u8, args[i], "console")) { 817 subsystem = .Console; 818 } else if (mem.eql(u8, args[i], "windows")) { 819 subsystem = .Windows; 820 } else if (mem.eql(u8, args[i], "posix")) { 821 subsystem = .Posix; 822 } else if (mem.eql(u8, args[i], "native")) { 823 subsystem = .Native; 824 } else if (mem.eql(u8, args[i], "efi_application")) { 825 subsystem = .EfiApplication; 826 } else if (mem.eql(u8, args[i], "efi_boot_service_driver")) { 827 subsystem = .EfiBootServiceDriver; 828 } else if (mem.eql(u8, args[i], "efi_rom")) { 829 subsystem = .EfiRom; 830 } else if (mem.eql(u8, args[i], "efi_runtime_driver")) { 831 subsystem = .EfiRuntimeDriver; 832 } else { 833 fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{ 834 args[i], 835 \\ console 836 \\ windows 837 \\ posix 838 \\ native 839 \\ efi_application 840 \\ efi_boot_service_driver 841 \\ efi_rom 842 \\ efi_runtime_driver 843 \\ 844 }); 845 } 846 } else if (mem.eql(u8, arg, "-O")) { 847 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 848 i += 1; 849 optimize_mode_string = args[i]; 850 } else if (mem.eql(u8, arg, "--stack")) { 851 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 852 i += 1; 853 stack_size_override = std.fmt.parseUnsigned(u64, args[i], 0) catch |err| { 854 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 855 }; 856 } else if (mem.eql(u8, arg, "--image-base")) { 857 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 858 i += 1; 859 image_base_override = std.fmt.parseUnsigned(u64, args[i], 0) catch |err| { 860 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 861 }; 862 } else if (mem.eql(u8, arg, "--name")) { 863 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 864 i += 1; 865 provided_name = args[i]; 866 } else if (mem.eql(u8, arg, "-rpath")) { 867 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 868 i += 1; 869 try rpath_list.append(args[i]); 870 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) { 871 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 872 i += 1; 873 try lib_dirs.append(args[i]); 874 } else if (mem.eql(u8, arg, "-F")) { 875 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 876 i += 1; 877 try framework_dirs.append(args[i]); 878 } else if (mem.eql(u8, arg, "-framework")) { 879 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 880 i += 1; 881 try frameworks.append(args[i]); 882 } else if (mem.eql(u8, arg, "-install_name")) { 883 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 884 i += 1; 885 install_name = args[i]; 886 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { 887 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 888 i += 1; 889 linker_script = args[i]; 890 } else if (mem.eql(u8, arg, "--version-script")) { 891 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 892 i += 1; 893 version_script = args[i]; 894 } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) { 895 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 896 // We don't know whether this library is part of libc or libc++ until 897 // we resolve the target, so we simply append to the list for now. 898 i += 1; 899 try system_libs.put(args[i], .{ .needed = false }); 900 } else if (mem.eql(u8, arg, "--needed-library") or mem.eql(u8, arg, "-needed-l")) { 901 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 902 i += 1; 903 try system_libs.put(args[i], .{ .needed = true }); 904 } else if (mem.eql(u8, arg, "-D") or 905 mem.eql(u8, arg, "-isystem") or 906 mem.eql(u8, arg, "-I") or 907 mem.eql(u8, arg, "-dirafter") or 908 mem.eql(u8, arg, "-iwithsysroot") or 909 mem.eql(u8, arg, "-iframework") or 910 mem.eql(u8, arg, "-iframeworkwithsysroot")) 911 { 912 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 913 i += 1; 914 try clang_argv.append(arg); 915 try clang_argv.append(args[i]); 916 } else if (mem.eql(u8, arg, "--version")) { 917 if (i + 1 >= args.len) { 918 fatal("expected parameter after --version", .{}); 919 } 920 i += 1; 921 version = std.builtin.Version.parse(args[i]) catch |err| { 922 fatal("unable to parse --version '{s}': {s}", .{ args[i], @errorName(err) }); 923 }; 924 have_version = true; 925 } else if (mem.eql(u8, arg, "-target")) { 926 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 927 i += 1; 928 target_arch_os_abi = args[i]; 929 } else if (mem.eql(u8, arg, "-mcpu")) { 930 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 931 i += 1; 932 target_mcpu = args[i]; 933 } else if (mem.eql(u8, arg, "-mcmodel")) { 934 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 935 i += 1; 936 machine_code_model = parseCodeModel(args[i]); 937 } else if (mem.startsWith(u8, arg, "-ofmt=")) { 938 target_ofmt = arg["-ofmt=".len..]; 939 } else if (mem.startsWith(u8, arg, "-mcpu=")) { 940 target_mcpu = arg["-mcpu=".len..]; 941 } else if (mem.startsWith(u8, arg, "-mcmodel=")) { 942 machine_code_model = parseCodeModel(arg["-mcmodel=".len..]); 943 } else if (mem.startsWith(u8, arg, "-O")) { 944 optimize_mode_string = arg["-O".len..]; 945 } else if (mem.eql(u8, arg, "--dynamic-linker")) { 946 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 947 i += 1; 948 target_dynamic_linker = args[i]; 949 } else if (mem.eql(u8, arg, "--sysroot")) { 950 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 951 i += 1; 952 sysroot = args[i]; 953 try clang_argv.append("-isysroot"); 954 try clang_argv.append(args[i]); 955 } else if (mem.eql(u8, arg, "--libc")) { 956 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 957 i += 1; 958 libc_paths_file = args[i]; 959 } else if (mem.eql(u8, arg, "--test-filter")) { 960 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 961 i += 1; 962 test_filter = args[i]; 963 } else if (mem.eql(u8, arg, "--test-name-prefix")) { 964 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 965 i += 1; 966 test_name_prefix = args[i]; 967 } else if (mem.eql(u8, arg, "--test-cmd")) { 968 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 969 i += 1; 970 try test_exec_args.append(args[i]); 971 } else if (mem.eql(u8, arg, "--cache-dir")) { 972 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 973 i += 1; 974 override_local_cache_dir = args[i]; 975 } else if (mem.eql(u8, arg, "--global-cache-dir")) { 976 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 977 i += 1; 978 override_global_cache_dir = args[i]; 979 } else if (mem.eql(u8, arg, "--zig-lib-dir")) { 980 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 981 i += 1; 982 override_lib_dir = args[i]; 983 } else if (mem.eql(u8, arg, "--debug-log")) { 984 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 985 i += 1; 986 if (!build_options.enable_logging) { 987 std.log.warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{}); 988 } else { 989 try log_scopes.append(gpa, args[i]); 990 } 991 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) { 992 if (!build_options.enable_link_snapshots) { 993 std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{}); 994 } else { 995 enable_link_snapshots = true; 996 } 997 } else if (mem.eql(u8, arg, "-fcompiler-rt")) { 998 want_compiler_rt = true; 999 } else if (mem.eql(u8, arg, "-fno-compiler-rt")) { 1000 want_compiler_rt = false; 1001 } else if (mem.eql(u8, arg, "-feach-lib-rpath")) { 1002 each_lib_rpath = true; 1003 } else if (mem.eql(u8, arg, "-fno-each-lib-rpath")) { 1004 each_lib_rpath = false; 1005 } else if (mem.eql(u8, arg, "--enable-cache")) { 1006 enable_cache = true; 1007 } else if (mem.eql(u8, arg, "--test-cmd-bin")) { 1008 try test_exec_args.append(null); 1009 } else if (mem.eql(u8, arg, "--test-evented-io")) { 1010 test_evented_io = true; 1011 } else if (mem.eql(u8, arg, "--test-no-exec")) { 1012 test_no_exec = true; 1013 } else if (mem.eql(u8, arg, "--watch")) { 1014 watch = true; 1015 } else if (mem.eql(u8, arg, "-ftime-report")) { 1016 time_report = true; 1017 } else if (mem.eql(u8, arg, "-fstack-report")) { 1018 stack_report = true; 1019 } else if (mem.eql(u8, arg, "-fPIC")) { 1020 want_pic = true; 1021 } else if (mem.eql(u8, arg, "-fno-PIC")) { 1022 want_pic = false; 1023 } else if (mem.eql(u8, arg, "-fPIE")) { 1024 want_pie = true; 1025 } else if (mem.eql(u8, arg, "-fno-PIE")) { 1026 want_pie = false; 1027 } else if (mem.eql(u8, arg, "-flto")) { 1028 want_lto = true; 1029 } else if (mem.eql(u8, arg, "-fno-lto")) { 1030 want_lto = false; 1031 } else if (mem.eql(u8, arg, "-funwind-tables")) { 1032 want_unwind_tables = true; 1033 } else if (mem.eql(u8, arg, "-fno-unwind-tables")) { 1034 want_unwind_tables = false; 1035 } else if (mem.eql(u8, arg, "-fstack-check")) { 1036 want_stack_check = true; 1037 } else if (mem.eql(u8, arg, "-fno-stack-check")) { 1038 want_stack_check = false; 1039 } else if (mem.eql(u8, arg, "-mred-zone")) { 1040 want_red_zone = true; 1041 } else if (mem.eql(u8, arg, "-mno-red-zone")) { 1042 want_red_zone = false; 1043 } else if (mem.eql(u8, arg, "-fomit-frame-pointer")) { 1044 omit_frame_pointer = true; 1045 } else if (mem.eql(u8, arg, "-fno-omit-frame-pointer")) { 1046 omit_frame_pointer = false; 1047 } else if (mem.eql(u8, arg, "-fsanitize-c")) { 1048 want_sanitize_c = true; 1049 } else if (mem.eql(u8, arg, "-fno-sanitize-c")) { 1050 want_sanitize_c = false; 1051 } else if (mem.eql(u8, arg, "-fvalgrind")) { 1052 want_valgrind = true; 1053 } else if (mem.eql(u8, arg, "-fno-valgrind")) { 1054 want_valgrind = false; 1055 } else if (mem.eql(u8, arg, "-fsanitize-thread")) { 1056 want_tsan = true; 1057 } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) { 1058 want_tsan = false; 1059 } else if (mem.eql(u8, arg, "-fLLVM")) { 1060 use_llvm = true; 1061 } else if (mem.eql(u8, arg, "-fno-LLVM")) { 1062 use_llvm = false; 1063 } else if (mem.eql(u8, arg, "-fLLD")) { 1064 use_lld = true; 1065 } else if (mem.eql(u8, arg, "-fno-LLD")) { 1066 use_lld = false; 1067 } else if (mem.eql(u8, arg, "-fClang")) { 1068 use_clang = true; 1069 } else if (mem.eql(u8, arg, "-fno-Clang")) { 1070 use_clang = false; 1071 } else if (mem.eql(u8, arg, "-fstage1")) { 1072 use_stage1 = true; 1073 } else if (mem.eql(u8, arg, "-fno-stage1")) { 1074 use_stage1 = false; 1075 } else if (mem.eql(u8, arg, "-rdynamic")) { 1076 rdynamic = true; 1077 } else if (mem.eql(u8, arg, "-fsoname")) { 1078 soname = .yes_default_value; 1079 } else if (mem.startsWith(u8, arg, "-fsoname=")) { 1080 soname = .{ .yes = arg["-fsoname=".len..] }; 1081 } else if (mem.eql(u8, arg, "-fno-soname")) { 1082 soname = .no; 1083 } else if (mem.eql(u8, arg, "-femit-bin")) { 1084 emit_bin = .yes_default_path; 1085 } else if (mem.startsWith(u8, arg, "-femit-bin=")) { 1086 emit_bin = .{ .yes = arg["-femit-bin=".len..] }; 1087 } else if (mem.eql(u8, arg, "-fno-emit-bin")) { 1088 emit_bin = .no; 1089 } else if (mem.eql(u8, arg, "-femit-h")) { 1090 emit_h = .yes_default_path; 1091 } else if (mem.startsWith(u8, arg, "-femit-h=")) { 1092 emit_h = .{ .yes = arg["-femit-h=".len..] }; 1093 } else if (mem.eql(u8, arg, "-fno-emit-h")) { 1094 emit_h = .no; 1095 } else if (mem.eql(u8, arg, "-femit-asm")) { 1096 emit_asm = .yes_default_path; 1097 } else if (mem.startsWith(u8, arg, "-femit-asm=")) { 1098 emit_asm = .{ .yes = arg["-femit-asm=".len..] }; 1099 } else if (mem.eql(u8, arg, "-fno-emit-asm")) { 1100 emit_asm = .no; 1101 } else if (mem.eql(u8, arg, "-femit-llvm-ir")) { 1102 emit_llvm_ir = .yes_default_path; 1103 } else if (mem.startsWith(u8, arg, "-femit-llvm-ir=")) { 1104 emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] }; 1105 } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) { 1106 emit_llvm_ir = .no; 1107 } else if (mem.eql(u8, arg, "-femit-llvm-bc")) { 1108 emit_llvm_bc = .yes_default_path; 1109 } else if (mem.startsWith(u8, arg, "-femit-llvm-bc=")) { 1110 emit_llvm_bc = .{ .yes = arg["-femit-llvm-bc=".len..] }; 1111 } else if (mem.eql(u8, arg, "-fno-emit-llvm-bc")) { 1112 emit_llvm_bc = .no; 1113 } else if (mem.eql(u8, arg, "-femit-docs")) { 1114 emit_docs = .yes_default_path; 1115 } else if (mem.startsWith(u8, arg, "-femit-docs=")) { 1116 emit_docs = .{ .yes = arg["-femit-docs=".len..] }; 1117 } else if (mem.eql(u8, arg, "-fno-emit-docs")) { 1118 emit_docs = .no; 1119 } else if (mem.eql(u8, arg, "-femit-analysis")) { 1120 emit_analysis = .yes_default_path; 1121 } else if (mem.startsWith(u8, arg, "-femit-analysis=")) { 1122 emit_analysis = .{ .yes = arg["-femit-analysis=".len..] }; 1123 } else if (mem.eql(u8, arg, "-fno-emit-analysis")) { 1124 emit_analysis = .no; 1125 } else if (mem.eql(u8, arg, "-femit-implib")) { 1126 emit_implib = .yes_default_path; 1127 emit_implib_arg_provided = true; 1128 } else if (mem.startsWith(u8, arg, "-femit-implib=")) { 1129 emit_implib = .{ .yes = arg["-femit-implib=".len..] }; 1130 emit_implib_arg_provided = true; 1131 } else if (mem.eql(u8, arg, "-fno-emit-implib")) { 1132 emit_implib = .no; 1133 emit_implib_arg_provided = true; 1134 } else if (mem.eql(u8, arg, "-dynamic")) { 1135 link_mode = .Dynamic; 1136 } else if (mem.eql(u8, arg, "-static")) { 1137 link_mode = .Static; 1138 } else if (mem.eql(u8, arg, "-fdll-export-fns")) { 1139 dll_export_fns = true; 1140 } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) { 1141 dll_export_fns = false; 1142 } else if (mem.eql(u8, arg, "--show-builtin")) { 1143 show_builtin = true; 1144 emit_bin = .no; 1145 } else if (mem.eql(u8, arg, "--strip")) { 1146 strip = true; 1147 } else if (mem.eql(u8, arg, "-fsingle-threaded")) { 1148 single_threaded = true; 1149 } else if (mem.eql(u8, arg, "-fno-single-threaded")) { 1150 single_threaded = false; 1151 } else if (mem.eql(u8, arg, "-ffunction-sections")) { 1152 function_sections = true; 1153 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) { 1154 link_eh_frame_hdr = true; 1155 } else if (mem.eql(u8, arg, "--emit-relocs")) { 1156 link_emit_relocs = true; 1157 } else if (mem.eql(u8, arg, "-fallow-shlib-undefined")) { 1158 linker_allow_shlib_undefined = true; 1159 } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) { 1160 linker_allow_shlib_undefined = false; 1161 } else if (mem.eql(u8, arg, "-z")) { 1162 i += 1; 1163 if (i >= args.len) { 1164 fatal("expected linker extension flag after '{s}'", .{arg}); 1165 } 1166 const z_arg = args[i]; 1167 if (mem.eql(u8, z_arg, "nodelete")) { 1168 linker_z_nodelete = true; 1169 } else if (mem.eql(u8, z_arg, "notext")) { 1170 linker_z_notext = true; 1171 } else if (mem.eql(u8, z_arg, "defs")) { 1172 linker_z_defs = true; 1173 } else if (mem.eql(u8, z_arg, "origin")) { 1174 linker_z_origin = true; 1175 } else if (mem.eql(u8, z_arg, "noexecstack")) { 1176 linker_z_noexecstack = true; 1177 } else if (mem.eql(u8, z_arg, "now")) { 1178 linker_z_now = true; 1179 } else if (mem.eql(u8, z_arg, "relro")) { 1180 linker_z_relro = true; 1181 } else { 1182 warn("unsupported linker extension flag: -z {s}", .{z_arg}); 1183 } 1184 } else if (mem.eql(u8, arg, "--import-memory")) { 1185 linker_import_memory = true; 1186 } else if (mem.eql(u8, arg, "--import-table")) { 1187 linker_import_table = true; 1188 } else if (mem.eql(u8, arg, "--export-table")) { 1189 linker_export_table = true; 1190 } else if (mem.startsWith(u8, arg, "--initial-memory=")) { 1191 linker_initial_memory = parseIntSuffix(arg, "--initial-memory=".len); 1192 } else if (mem.startsWith(u8, arg, "--max-memory=")) { 1193 linker_max_memory = parseIntSuffix(arg, "--max-memory=".len); 1194 } else if (mem.startsWith(u8, arg, "--global-base=")) { 1195 linker_global_base = parseIntSuffix(arg, "--global-base=".len); 1196 } else if (mem.startsWith(u8, arg, "--export=")) { 1197 try linker_export_symbol_names.append(arg["--export=".len..]); 1198 } else if (mem.eql(u8, arg, "-Bsymbolic")) { 1199 linker_bind_global_refs_locally = true; 1200 } else if (mem.eql(u8, arg, "--debug-compile-errors")) { 1201 debug_compile_errors = true; 1202 } else if (mem.eql(u8, arg, "--verbose-link")) { 1203 verbose_link = true; 1204 } else if (mem.eql(u8, arg, "--verbose-cc")) { 1205 verbose_cc = true; 1206 } else if (mem.eql(u8, arg, "--verbose-air")) { 1207 verbose_air = true; 1208 } else if (mem.eql(u8, arg, "--verbose-mir")) { 1209 verbose_mir = true; 1210 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { 1211 verbose_llvm_ir = true; 1212 } else if (mem.eql(u8, arg, "--verbose-cimport")) { 1213 verbose_cimport = true; 1214 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { 1215 verbose_llvm_cpu_features = true; 1216 } else if (mem.startsWith(u8, arg, "-T")) { 1217 linker_script = arg[2..]; 1218 } else if (mem.startsWith(u8, arg, "-L")) { 1219 try lib_dirs.append(arg[2..]); 1220 } else if (mem.startsWith(u8, arg, "-F")) { 1221 try framework_dirs.append(arg[2..]); 1222 } else if (mem.startsWith(u8, arg, "-l")) { 1223 // We don't know whether this library is part of libc or libc++ until 1224 // we resolve the target, so we simply append to the list for now. 1225 try system_libs.put(arg["-l".len..], .{ .needed = false }); 1226 } else if (mem.startsWith(u8, arg, "-needed-l")) { 1227 try system_libs.put(arg["-needed-l".len..], .{ .needed = true }); 1228 } else if (mem.startsWith(u8, arg, "-D") or 1229 mem.startsWith(u8, arg, "-I")) 1230 { 1231 try clang_argv.append(arg); 1232 } else if (mem.startsWith(u8, arg, "-mexec-model=")) { 1233 wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse { 1234 fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]}); 1235 }; 1236 } else { 1237 fatal("unrecognized parameter: '{s}'", .{arg}); 1238 } 1239 } else switch (Compilation.classifyFileExt(arg)) { 1240 .object, .static_library, .shared_library => { 1241 try link_objects.append(arg); 1242 }, 1243 .assembly, .c, .cpp, .h, .ll, .bc, .m, .mm => { 1244 try c_source_files.append(.{ 1245 .src_path = arg, 1246 .extra_flags = try arena.dupe([]const u8, extra_cflags.items), 1247 }); 1248 }, 1249 .zig => { 1250 if (root_src_file) |other| { 1251 fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other }); 1252 } else { 1253 root_src_file = arg; 1254 } 1255 }, 1256 .unknown => { 1257 fatal("unrecognized file extension of parameter '{s}'", .{arg}); 1258 }, 1259 } 1260 } 1261 if (optimize_mode_string) |s| { 1262 optimize_mode = std.meta.stringToEnum(std.builtin.Mode, s) orelse 1263 fatal("unrecognized optimization mode: '{s}'", .{s}); 1264 } 1265 }, 1266 .cc, .cpp => { 1267 emit_h = .no; 1268 soname = .no; 1269 strip = true; 1270 ensure_libc_on_non_freestanding = true; 1271 ensure_libcpp_on_non_freestanding = arg_mode == .cpp; 1272 want_native_include_dirs = true; 1273 // Clang's driver enables this switch unconditionally. 1274 // Disabling the emission of .eh_frame_hdr can unexpectedly break 1275 // some functionality that depend on it, such as C++ exceptions and 1276 // DWARF-based stack traces. 1277 link_eh_frame_hdr = true; 1278 1279 const COutMode = enum { 1280 link, 1281 object, 1282 assembly, 1283 preprocessor, 1284 }; 1285 var c_out_mode: COutMode = .link; 1286 var out_path: ?[]const u8 = null; 1287 var is_shared_lib = false; 1288 var linker_args = std.ArrayList([]const u8).init(arena); 1289 var it = ClangArgIterator.init(arena, all_args); 1290 var emit_llvm = false; 1291 var needed = false; 1292 var force_static_libs = false; 1293 while (it.has_next) { 1294 it.next() catch |err| { 1295 fatal("unable to parse command line parameters: {s}", .{@errorName(err)}); 1296 }; 1297 switch (it.zig_equivalent) { 1298 .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown 1299 .o => out_path = it.only_arg, // -o 1300 .c => c_out_mode = .object, // -c 1301 .asm_only => c_out_mode = .assembly, // -S 1302 .preprocess_only => c_out_mode = .preprocessor, // -E 1303 .emit_llvm => emit_llvm = true, 1304 .other => { 1305 try clang_argv.appendSlice(it.other_args); 1306 }, 1307 .positional => { 1308 const file_ext = Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0)); 1309 switch (file_ext) { 1310 .assembly, .c, .cpp, .ll, .bc, .h, .m, .mm => try c_source_files.append(.{ .src_path = it.only_arg }), 1311 .unknown, .shared_library, .object, .static_library => { 1312 try link_objects.append(it.only_arg); 1313 }, 1314 .zig => { 1315 if (root_src_file) |other| { 1316 fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other }); 1317 } else { 1318 root_src_file = it.only_arg; 1319 } 1320 }, 1321 } 1322 }, 1323 .l => { 1324 // -l 1325 // We don't know whether this library is part of libc or libc++ until 1326 // we resolve the target, so we simply append to the list for now. 1327 if (force_static_libs) { 1328 try static_libs.append(it.only_arg); 1329 } else { 1330 try system_libs.put(it.only_arg, .{ .needed = needed }); 1331 } 1332 }, 1333 .ignore => {}, 1334 .driver_punt => { 1335 // Never mind what we're doing, just pass the args directly. For example --help. 1336 return punt_to_clang(arena, all_args); 1337 }, 1338 .pic => want_pic = true, 1339 .no_pic => want_pic = false, 1340 .pie => want_pie = true, 1341 .no_pie => want_pie = false, 1342 .lto => want_lto = true, 1343 .no_lto => want_lto = false, 1344 .red_zone => want_red_zone = true, 1345 .no_red_zone => want_red_zone = false, 1346 .omit_frame_pointer => omit_frame_pointer = true, 1347 .no_omit_frame_pointer => omit_frame_pointer = false, 1348 .function_sections => function_sections = true, 1349 .no_function_sections => function_sections = false, 1350 .color_diagnostics => color = .on, 1351 .no_color_diagnostics => color = .off, 1352 .unwind_tables => want_unwind_tables = true, 1353 .no_unwind_tables => want_unwind_tables = false, 1354 .nostdlib => ensure_libc_on_non_freestanding = false, 1355 .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false, 1356 .shared => { 1357 link_mode = .Dynamic; 1358 is_shared_lib = true; 1359 }, 1360 .rdynamic => rdynamic = true, 1361 .wl => { 1362 var split_it = mem.split(u8, it.only_arg, ","); 1363 while (split_it.next()) |linker_arg| { 1364 // Handle nested-joined args like `-Wl,-rpath=foo`. 1365 // Must be prefixed with 1 or 2 dashes. 1366 if (linker_arg.len >= 3 and linker_arg[0] == '-' and linker_arg[2] != '-') { 1367 if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| { 1368 try linker_args.append(linker_arg[0..equals_pos]); 1369 try linker_args.append(linker_arg[equals_pos + 1 ..]); 1370 continue; 1371 } 1372 } 1373 if (mem.eql(u8, linker_arg, "--as-needed")) { 1374 needed = false; 1375 } else if (mem.eql(u8, linker_arg, "--no-as-needed")) { 1376 needed = true; 1377 } else if (mem.eql(u8, linker_arg, "-Bdynamic") or 1378 mem.eql(u8, linker_arg, "-dy") or 1379 mem.eql(u8, linker_arg, "-call_shared")) 1380 { 1381 force_static_libs = false; 1382 } else if (mem.eql(u8, linker_arg, "-Bstatic") or 1383 mem.eql(u8, linker_arg, "-dn") or 1384 mem.eql(u8, linker_arg, "-non_shared") or 1385 mem.eql(u8, linker_arg, "-static")) 1386 { 1387 force_static_libs = true; 1388 } else { 1389 try linker_args.append(linker_arg); 1390 } 1391 } 1392 }, 1393 .optimize => { 1394 // Alright, what release mode do they want? 1395 const level = if (it.only_arg.len >= 1 and it.only_arg[0] == 'O') it.only_arg[1..] else it.only_arg; 1396 if (mem.eql(u8, level, "s") or 1397 mem.eql(u8, level, "z")) 1398 { 1399 optimize_mode = .ReleaseSmall; 1400 } else if (mem.eql(u8, level, "1") or 1401 mem.eql(u8, level, "2") or 1402 mem.eql(u8, level, "3") or 1403 mem.eql(u8, level, "4") or 1404 mem.eql(u8, level, "fast")) 1405 { 1406 optimize_mode = .ReleaseFast; 1407 } else if (mem.eql(u8, level, "g") or 1408 mem.eql(u8, level, "0")) 1409 { 1410 optimize_mode = .Debug; 1411 } else { 1412 try clang_argv.appendSlice(it.other_args); 1413 } 1414 }, 1415 .debug => { 1416 strip = false; 1417 if (mem.eql(u8, it.only_arg, "g")) { 1418 // We handled with strip = false above. 1419 } else if (mem.eql(u8, it.only_arg, "g1") or 1420 mem.eql(u8, it.only_arg, "gline-tables-only")) 1421 { 1422 // We handled with strip = false above. but we also want reduced debug info. 1423 try clang_argv.append("-gline-tables-only"); 1424 } else { 1425 try clang_argv.appendSlice(it.other_args); 1426 } 1427 }, 1428 .sanitize => { 1429 if (mem.eql(u8, it.only_arg, "undefined")) { 1430 want_sanitize_c = true; 1431 } else if (mem.eql(u8, it.only_arg, "thread")) { 1432 want_tsan = true; 1433 } else { 1434 try clang_argv.appendSlice(it.other_args); 1435 } 1436 }, 1437 .linker_script => linker_script = it.only_arg, 1438 .verbose => { 1439 verbose_link = true; 1440 // Have Clang print more infos, some tools such as CMake 1441 // parse this to discover any implicit include and 1442 // library dir to look-up into. 1443 try clang_argv.append("-v"); 1444 }, 1445 .dry_run => { 1446 verbose_link = true; 1447 try clang_argv.append("-###"); 1448 // This flag is supposed to mean "dry run" but currently this 1449 // will actually still execute. The tracking issue for this is 1450 // https://github.com/ziglang/zig/issues/7170 1451 }, 1452 .for_linker => try linker_args.append(it.only_arg), 1453 .linker_input_z => { 1454 try linker_args.append("-z"); 1455 try linker_args.append(it.only_arg); 1456 }, 1457 .lib_dir => try lib_dirs.append(it.only_arg), 1458 .mcpu => target_mcpu = it.only_arg, 1459 .m => try llvm_m_args.append(it.only_arg), 1460 .dep_file => { 1461 disable_c_depfile = true; 1462 try clang_argv.appendSlice(it.other_args); 1463 }, 1464 .dep_file_mm => { // -MM 1465 // "Like -MMD, but also implies -E and writes to stdout by default" 1466 c_out_mode = .preprocessor; 1467 disable_c_depfile = true; 1468 try clang_argv.appendSlice(it.other_args); 1469 }, 1470 .framework_dir => try framework_dirs.append(it.only_arg), 1471 .framework => try frameworks.append(it.only_arg), 1472 .nostdlibinc => want_native_include_dirs = false, 1473 .strip => strip = true, 1474 .exec_model => { 1475 wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, it.only_arg) orelse { 1476 fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{it.only_arg}); 1477 }; 1478 }, 1479 } 1480 } 1481 // Parse linker args. 1482 var i: usize = 0; 1483 while (i < linker_args.items.len) : (i += 1) { 1484 const arg = linker_args.items[i]; 1485 if (mem.eql(u8, arg, "-soname")) { 1486 i += 1; 1487 if (i >= linker_args.items.len) { 1488 fatal("expected linker arg after '{s}'", .{arg}); 1489 } 1490 const name = linker_args.items[i]; 1491 soname = .{ .yes = name }; 1492 // Use it as --name. 1493 // Example: libsoundio.so.2 1494 var prefix: usize = 0; 1495 if (mem.startsWith(u8, name, "lib")) { 1496 prefix = 3; 1497 } 1498 var end: usize = name.len; 1499 if (mem.endsWith(u8, name, ".so")) { 1500 end -= 3; 1501 } else { 1502 var found_digit = false; 1503 while (end > 0 and std.ascii.isDigit(name[end - 1])) { 1504 found_digit = true; 1505 end -= 1; 1506 } 1507 if (found_digit and end > 0 and name[end - 1] == '.') { 1508 end -= 1; 1509 } else { 1510 end = name.len; 1511 } 1512 if (mem.endsWith(u8, name[prefix..end], ".so")) { 1513 end -= 3; 1514 } 1515 } 1516 provided_name = name[prefix..end]; 1517 } else if (mem.eql(u8, arg, "-rpath")) { 1518 i += 1; 1519 if (i >= linker_args.items.len) { 1520 fatal("expected linker arg after '{s}'", .{arg}); 1521 } 1522 try rpath_list.append(linker_args.items[i]); 1523 } else if (mem.eql(u8, arg, "-I") or 1524 mem.eql(u8, arg, "--dynamic-linker") or 1525 mem.eql(u8, arg, "-dynamic-linker")) 1526 { 1527 i += 1; 1528 if (i >= linker_args.items.len) { 1529 fatal("expected linker arg after '{s}'", .{arg}); 1530 } 1531 target_dynamic_linker = linker_args.items[i]; 1532 } else if (mem.eql(u8, arg, "-E") or 1533 mem.eql(u8, arg, "--export-dynamic") or 1534 mem.eql(u8, arg, "-export-dynamic")) 1535 { 1536 rdynamic = true; 1537 } else if (mem.eql(u8, arg, "--version-script")) { 1538 i += 1; 1539 if (i >= linker_args.items.len) { 1540 fatal("expected linker arg after '{s}'", .{arg}); 1541 } 1542 version_script = linker_args.items[i]; 1543 } else if (mem.eql(u8, arg, "-O")) { 1544 i += 1; 1545 if (i >= linker_args.items.len) { 1546 fatal("expected linker arg after '{s}'", .{arg}); 1547 } 1548 linker_optimization = std.fmt.parseUnsigned(u8, linker_args.items[i], 10) catch |err| { 1549 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 1550 }; 1551 } else if (mem.startsWith(u8, arg, "-O")) { 1552 linker_optimization = std.fmt.parseUnsigned(u8, arg["-O".len..], 10) catch |err| { 1553 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 1554 }; 1555 } else if (mem.eql(u8, arg, "--gc-sections")) { 1556 linker_gc_sections = true; 1557 } else if (mem.eql(u8, arg, "--no-gc-sections")) { 1558 linker_gc_sections = false; 1559 } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or 1560 mem.eql(u8, arg, "-allow-shlib-undefined")) 1561 { 1562 linker_allow_shlib_undefined = true; 1563 } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or 1564 mem.eql(u8, arg, "-no-allow-shlib-undefined")) 1565 { 1566 linker_allow_shlib_undefined = false; 1567 } else if (mem.eql(u8, arg, "-Bsymbolic")) { 1568 linker_bind_global_refs_locally = true; 1569 } else if (mem.eql(u8, arg, "--import-memory")) { 1570 linker_import_memory = true; 1571 } else if (mem.eql(u8, arg, "--import-table")) { 1572 linker_import_table = true; 1573 } else if (mem.eql(u8, arg, "--export-table")) { 1574 linker_export_table = true; 1575 } else if (mem.startsWith(u8, arg, "--initial-memory=")) { 1576 linker_initial_memory = parseIntSuffix(arg, "--initial-memory=".len); 1577 } else if (mem.startsWith(u8, arg, "--max-memory=")) { 1578 linker_max_memory = parseIntSuffix(arg, "--max-memory=".len); 1579 } else if (mem.startsWith(u8, arg, "--global-base=")) { 1580 linker_global_base = parseIntSuffix(arg, "--global-base=".len); 1581 } else if (mem.startsWith(u8, arg, "--export=")) { 1582 try linker_export_symbol_names.append(arg["--export=".len..]); 1583 } else if (mem.eql(u8, arg, "-z")) { 1584 i += 1; 1585 if (i >= linker_args.items.len) { 1586 fatal("expected linker extension flag after '{s}'", .{arg}); 1587 } 1588 const z_arg = linker_args.items[i]; 1589 if (mem.eql(u8, z_arg, "nodelete")) { 1590 linker_z_nodelete = true; 1591 } else if (mem.eql(u8, z_arg, "notext")) { 1592 linker_z_notext = true; 1593 } else if (mem.eql(u8, z_arg, "defs")) { 1594 linker_z_defs = true; 1595 } else if (mem.eql(u8, z_arg, "origin")) { 1596 linker_z_origin = true; 1597 } else if (mem.eql(u8, z_arg, "noexecstack")) { 1598 linker_z_noexecstack = true; 1599 } else if (mem.eql(u8, z_arg, "now")) { 1600 linker_z_now = true; 1601 } else if (mem.eql(u8, z_arg, "relro")) { 1602 linker_z_relro = true; 1603 } else { 1604 warn("unsupported linker extension flag: -z {s}", .{z_arg}); 1605 } 1606 } else if (mem.eql(u8, arg, "--major-image-version")) { 1607 i += 1; 1608 if (i >= linker_args.items.len) { 1609 fatal("expected linker arg after '{s}'", .{arg}); 1610 } 1611 version.major = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| { 1612 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 1613 }; 1614 have_version = true; 1615 } else if (mem.eql(u8, arg, "--minor-image-version")) { 1616 i += 1; 1617 if (i >= linker_args.items.len) { 1618 fatal("expected linker arg after '{s}'", .{arg}); 1619 } 1620 version.minor = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| { 1621 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 1622 }; 1623 have_version = true; 1624 } else if (mem.eql(u8, arg, "--stack")) { 1625 i += 1; 1626 if (i >= linker_args.items.len) { 1627 fatal("expected linker arg after '{s}'", .{arg}); 1628 } 1629 stack_size_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| { 1630 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 1631 }; 1632 } else if (mem.eql(u8, arg, "--image-base")) { 1633 i += 1; 1634 if (i >= linker_args.items.len) { 1635 fatal("expected linker arg after '{s}'", .{arg}); 1636 } 1637 image_base_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| { 1638 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 1639 }; 1640 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { 1641 i += 1; 1642 if (i >= linker_args.items.len) { 1643 fatal("expected linker arg after '{s}'", .{arg}); 1644 } 1645 linker_script = linker_args.items[i]; 1646 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) { 1647 link_eh_frame_hdr = true; 1648 } else if (mem.eql(u8, arg, "--no-eh-frame-hdr")) { 1649 link_eh_frame_hdr = false; 1650 } else if (mem.eql(u8, arg, "--tsaware")) { 1651 linker_tsaware = true; 1652 } else if (mem.eql(u8, arg, "--nxcompat")) { 1653 linker_nxcompat = true; 1654 } else if (mem.eql(u8, arg, "--dynamicbase")) { 1655 linker_dynamicbase = true; 1656 } else if (mem.eql(u8, arg, "--high-entropy-va")) { 1657 // This option does not do anything. 1658 } else if (mem.eql(u8, arg, "--export-all-symbols")) { 1659 rdynamic = true; 1660 } else if (mem.eql(u8, arg, "--start-group") or 1661 mem.eql(u8, arg, "--end-group")) 1662 { 1663 // We don't need to care about these because these args are 1664 // for resolving circular dependencies but our linker takes 1665 // care of this without explicit args. 1666 } else if (mem.eql(u8, arg, "--major-os-version") or 1667 mem.eql(u8, arg, "--minor-os-version")) 1668 { 1669 i += 1; 1670 if (i >= linker_args.items.len) { 1671 fatal("expected linker arg after '{s}'", .{arg}); 1672 } 1673 // This option does not do anything. 1674 } else if (mem.eql(u8, arg, "--major-subsystem-version")) { 1675 i += 1; 1676 if (i >= linker_args.items.len) { 1677 fatal("expected linker arg after '{s}'", .{arg}); 1678 } 1679 1680 major_subsystem_version = std.fmt.parseUnsigned( 1681 u32, 1682 linker_args.items[i], 1683 10, 1684 ) catch |err| { 1685 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 1686 }; 1687 } else if (mem.eql(u8, arg, "--minor-subsystem-version")) { 1688 i += 1; 1689 if (i >= linker_args.items.len) { 1690 fatal("expected linker arg after '{s}'", .{arg}); 1691 } 1692 1693 minor_subsystem_version = std.fmt.parseUnsigned( 1694 u32, 1695 linker_args.items[i], 1696 10, 1697 ) catch |err| { 1698 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 1699 }; 1700 } else if (mem.eql(u8, arg, "-framework") or mem.eql(u8, arg, "-weak_framework")) { 1701 i += 1; 1702 if (i >= linker_args.items.len) { 1703 fatal("expected linker arg after '{s}'", .{arg}); 1704 } 1705 try frameworks.append(linker_args.items[i]); 1706 } else if (mem.eql(u8, arg, "-compatibility_version")) { 1707 i += 1; 1708 if (i >= linker_args.items.len) { 1709 fatal("expected linker arg after '{s}'", .{arg}); 1710 } 1711 compatibility_version = std.builtin.Version.parse(linker_args.items[i]) catch |err| { 1712 fatal("unable to parse -compatibility_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) }); 1713 }; 1714 } else if (mem.eql(u8, arg, "-current_version")) { 1715 i += 1; 1716 if (i >= linker_args.items.len) { 1717 fatal("expected linker arg after '{s}'", .{arg}); 1718 } 1719 version = std.builtin.Version.parse(linker_args.items[i]) catch |err| { 1720 fatal("unable to parse -current_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) }); 1721 }; 1722 have_version = true; 1723 } else if (mem.eql(u8, arg, "--out-implib") or 1724 mem.eql(u8, arg, "-implib")) 1725 { 1726 i += 1; 1727 if (i >= linker_args.items.len) { 1728 fatal("expected linker arg after '{s}'", .{arg}); 1729 } 1730 emit_implib = .{ .yes = linker_args.items[i] }; 1731 emit_implib_arg_provided = true; 1732 } else if (mem.eql(u8, arg, "-undefined")) { 1733 i += 1; 1734 if (i >= linker_args.items.len) { 1735 fatal("expected linker arg after '{s}'", .{arg}); 1736 } 1737 if (mem.eql(u8, "dynamic_lookup", linker_args.items[i])) { 1738 linker_allow_shlib_undefined = true; 1739 } else { 1740 fatal("unsupported -undefined option '{s}'", .{linker_args.items[i]}); 1741 } 1742 } else if (mem.eql(u8, arg, "-install_name")) { 1743 i += 1; 1744 if (i >= linker_args.items.len) { 1745 fatal("expected linker arg after '{s}'", .{arg}); 1746 } 1747 install_name = linker_args.items[i]; 1748 } else { 1749 warn("unsupported linker arg: {s}", .{arg}); 1750 } 1751 } 1752 1753 if (want_sanitize_c) |wsc| { 1754 if (wsc and optimize_mode == .ReleaseFast) { 1755 optimize_mode = .ReleaseSafe; 1756 } 1757 } 1758 1759 switch (c_out_mode) { 1760 .link => { 1761 output_mode = if (is_shared_lib) .Lib else .Exe; 1762 emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out; 1763 enable_cache = true; 1764 if (emit_llvm) { 1765 fatal("-emit-llvm cannot be used when linking", .{}); 1766 } 1767 }, 1768 .object => { 1769 output_mode = .Obj; 1770 if (emit_llvm) { 1771 emit_bin = .no; 1772 if (out_path) |p| { 1773 emit_llvm_bc = .{ .yes = p }; 1774 } else { 1775 emit_llvm_bc = .yes_default_path; 1776 } 1777 } else { 1778 if (out_path) |p| { 1779 emit_bin = .{ .yes = p }; 1780 } else { 1781 emit_bin = .yes_default_path; 1782 } 1783 } 1784 }, 1785 .assembly => { 1786 output_mode = .Obj; 1787 emit_bin = .no; 1788 if (emit_llvm) { 1789 if (out_path) |p| { 1790 emit_llvm_ir = .{ .yes = p }; 1791 } else { 1792 emit_llvm_ir = .yes_default_path; 1793 } 1794 } else { 1795 if (out_path) |p| { 1796 emit_asm = .{ .yes = p }; 1797 } else { 1798 emit_asm = .yes_default_path; 1799 } 1800 } 1801 }, 1802 .preprocessor => { 1803 output_mode = .Obj; 1804 // An error message is generated when there is more than 1 C source file. 1805 if (c_source_files.items.len != 1) { 1806 // For example `zig cc` and no args should print the "no input files" message. 1807 return punt_to_clang(arena, all_args); 1808 } 1809 if (out_path) |p| { 1810 emit_bin = .{ .yes = p }; 1811 clang_preprocessor_mode = .yes; 1812 } else { 1813 clang_preprocessor_mode = .stdout; 1814 } 1815 }, 1816 } 1817 if (c_source_files.items.len == 0 and link_objects.items.len == 0) { 1818 // For example `zig cc` and no args should print the "no input files" message. 1819 return punt_to_clang(arena, all_args); 1820 } 1821 }, 1822 } 1823 1824 if (arg_mode == .translate_c and c_source_files.items.len != 1) { 1825 fatal("translate-c expects exactly 1 source file (found {d})", .{c_source_files.items.len}); 1826 } 1827 1828 if (root_src_file == null and arg_mode == .zig_test) { 1829 fatal("`zig test` expects a zig source file argument", .{}); 1830 } 1831 1832 const root_name = if (provided_name) |n| n else blk: { 1833 if (arg_mode == .zig_test) { 1834 break :blk "test"; 1835 } else if (root_src_file) |file| { 1836 const basename = fs.path.basename(file); 1837 break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; 1838 } else if (c_source_files.items.len >= 1) { 1839 const basename = fs.path.basename(c_source_files.items[0].src_path); 1840 break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; 1841 } else if (link_objects.items.len >= 1) { 1842 const basename = fs.path.basename(link_objects.items[0]); 1843 break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; 1844 } else if (emit_bin == .yes) { 1845 const basename = fs.path.basename(emit_bin.yes); 1846 break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; 1847 } else if (show_builtin) { 1848 break :blk "builtin"; 1849 } else if (arg_mode == .run) { 1850 fatal("`zig run` expects at least one positional argument", .{}); 1851 // TODO once the attempt to unwrap error: LinkingWithoutZigSourceUnimplemented 1852 // is solved, remove the above fatal() and uncomment the `break` below. 1853 //break :blk "run"; 1854 } else { 1855 fatal("expected a positional argument, -femit-bin=[path], --show-builtin, or --name [name]", .{}); 1856 } 1857 }; 1858 1859 var target_parse_options: std.zig.CrossTarget.ParseOptions = .{ 1860 .arch_os_abi = target_arch_os_abi, 1861 .cpu_features = target_mcpu, 1862 .dynamic_linker = target_dynamic_linker, 1863 }; 1864 1865 // Before passing the mcpu string in for parsing, we convert any -m flags that were 1866 // passed in via zig cc to zig-style. 1867 if (llvm_m_args.items.len != 0) { 1868 // If this returns null, we let it fall through to the case below which will 1869 // run the full parse function and do proper error handling. 1870 if (std.zig.CrossTarget.parseCpuArch(target_parse_options)) |cpu_arch| { 1871 var llvm_to_zig_name = std.StringHashMap([]const u8).init(gpa); 1872 defer llvm_to_zig_name.deinit(); 1873 1874 for (cpu_arch.allFeaturesList()) |feature| { 1875 const llvm_name = feature.llvm_name orelse continue; 1876 try llvm_to_zig_name.put(llvm_name, feature.name); 1877 } 1878 1879 var mcpu_buffer = std.ArrayList(u8).init(gpa); 1880 defer mcpu_buffer.deinit(); 1881 1882 try mcpu_buffer.appendSlice(target_mcpu orelse "baseline"); 1883 1884 for (llvm_m_args.items) |llvm_m_arg| { 1885 if (mem.startsWith(u8, llvm_m_arg, "mno-")) { 1886 const llvm_name = llvm_m_arg["mno-".len..]; 1887 const zig_name = llvm_to_zig_name.get(llvm_name) orelse { 1888 fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{ 1889 @tagName(cpu_arch), llvm_name, 1890 }); 1891 }; 1892 try mcpu_buffer.append('-'); 1893 try mcpu_buffer.appendSlice(zig_name); 1894 } else if (mem.startsWith(u8, llvm_m_arg, "m")) { 1895 const llvm_name = llvm_m_arg["m".len..]; 1896 const zig_name = llvm_to_zig_name.get(llvm_name) orelse { 1897 fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{ 1898 @tagName(cpu_arch), llvm_name, 1899 }); 1900 }; 1901 try mcpu_buffer.append('+'); 1902 try mcpu_buffer.appendSlice(zig_name); 1903 } else { 1904 unreachable; 1905 } 1906 } 1907 1908 const adjusted_target_mcpu = try arena.dupe(u8, mcpu_buffer.items); 1909 std.log.debug("adjusted target_mcpu: {s}", .{adjusted_target_mcpu}); 1910 target_parse_options.cpu_features = adjusted_target_mcpu; 1911 } 1912 } 1913 1914 const cross_target = try parseCrossTargetOrReportFatalError(arena, target_parse_options); 1915 const target_info = try detectNativeTargetInfo(gpa, cross_target); 1916 1917 if (target_info.target.os.tag != .freestanding) { 1918 if (ensure_libc_on_non_freestanding) 1919 link_libc = true; 1920 if (ensure_libcpp_on_non_freestanding) 1921 link_libcpp = true; 1922 } 1923 1924 // Now that we have target info, we can find out if any of the system libraries 1925 // are part of libc or libc++. We remove them from the list and communicate their 1926 // existence via flags instead. 1927 { 1928 var i: usize = 0; 1929 while (i < system_libs.count()) { 1930 const lib_name = system_libs.keys()[i]; 1931 1932 if (target_util.is_libc_lib_name(target_info.target, lib_name)) { 1933 link_libc = true; 1934 _ = system_libs.orderedRemove(lib_name); 1935 continue; 1936 } 1937 if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) { 1938 link_libcpp = true; 1939 _ = system_libs.orderedRemove(lib_name); 1940 continue; 1941 } 1942 if (mem.eql(u8, lib_name, "unwind")) { 1943 link_libunwind = true; 1944 _ = system_libs.orderedRemove(lib_name); 1945 continue; 1946 } 1947 if (std.fs.path.isAbsolute(lib_name)) { 1948 fatal("cannot use absolute path as a system library: {s}", .{lib_name}); 1949 } 1950 if (target_info.target.os.tag == .wasi) { 1951 if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| { 1952 try wasi_emulated_libs.append(crt_file); 1953 _ = system_libs.orderedRemove(lib_name); 1954 continue; 1955 } 1956 } 1957 i += 1; 1958 } 1959 } 1960 1961 if (use_lld) |opt| { 1962 if (opt and cross_target.isDarwin()) { 1963 fatal("LLD requested with Mach-O object format. Only the self-hosted linker is supported for this target.", .{}); 1964 } 1965 } 1966 1967 if (want_lto) |opt| { 1968 if (opt and cross_target.isDarwin()) { 1969 fatal("LTO is not yet supported with the Mach-O object format. More details: https://github.com/ziglang/zig/issues/8680", .{}); 1970 } 1971 } 1972 1973 if (comptime builtin.target.isDarwin()) { 1974 // If we want to link against frameworks, we need system headers. 1975 if (framework_dirs.items.len > 0 or frameworks.items.len > 0) 1976 want_native_include_dirs = true; 1977 } 1978 1979 if (sysroot == null and cross_target.isNativeOs() and (system_libs.count() != 0 or want_native_include_dirs)) { 1980 const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| { 1981 fatal("unable to detect native system paths: {s}", .{@errorName(err)}); 1982 }; 1983 for (paths.warnings.items) |warning| { 1984 warn("{s}", .{warning}); 1985 } 1986 1987 const has_sysroot = if (comptime builtin.target.isDarwin()) outer: { 1988 if (std.zig.system.darwin.isDarwinSDKInstalled(arena)) { 1989 const sdk = std.zig.system.darwin.getDarwinSDK(arena, target_info.target) orelse 1990 break :outer false; 1991 native_darwin_sdk = sdk; 1992 try clang_argv.ensureUnusedCapacity(2); 1993 clang_argv.appendAssumeCapacity("-isysroot"); 1994 clang_argv.appendAssumeCapacity(sdk.path); 1995 break :outer true; 1996 } else break :outer false; 1997 } else false; 1998 1999 try clang_argv.ensureUnusedCapacity(paths.include_dirs.items.len * 2); 2000 const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem"; 2001 for (paths.include_dirs.items) |include_dir| { 2002 clang_argv.appendAssumeCapacity(isystem_flag); 2003 clang_argv.appendAssumeCapacity(include_dir); 2004 } 2005 2006 try clang_argv.ensureUnusedCapacity(paths.framework_dirs.items.len * 2); 2007 try framework_dirs.ensureUnusedCapacity(paths.framework_dirs.items.len); 2008 const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework"; 2009 for (paths.framework_dirs.items) |framework_dir| { 2010 clang_argv.appendAssumeCapacity(iframework_flag); 2011 clang_argv.appendAssumeCapacity(framework_dir); 2012 framework_dirs.appendAssumeCapacity(framework_dir); 2013 } 2014 2015 for (paths.lib_dirs.items) |lib_dir| { 2016 try lib_dirs.append(lib_dir); 2017 } 2018 for (paths.rpaths.items) |rpath| { 2019 try rpath_list.append(rpath); 2020 } 2021 } 2022 2023 { 2024 // Resolve static libraries into full paths. 2025 const sep = fs.path.sep_str; 2026 2027 var test_path = std.ArrayList(u8).init(gpa); 2028 defer test_path.deinit(); 2029 2030 for (static_libs.items) |static_lib| { 2031 for (lib_dirs.items) |lib_dir_path| { 2032 test_path.clearRetainingCapacity(); 2033 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{ 2034 lib_dir_path, 2035 target_info.target.libPrefix(), 2036 static_lib, 2037 target_info.target.staticLibSuffix(), 2038 }); 2039 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { 2040 error.FileNotFound => continue, 2041 else => |e| fatal("unable to search for static library '{s}': {s}", .{ 2042 test_path.items, @errorName(e), 2043 }), 2044 }; 2045 try link_objects.append(try arena.dupe(u8, test_path.items)); 2046 break; 2047 } else { 2048 var search_paths = std.ArrayList(u8).init(arena); 2049 for (lib_dirs.items) |lib_dir_path| { 2050 try search_paths.writer().print("\n {s}" ++ sep ++ "{s}{s}{s}", .{ 2051 lib_dir_path, 2052 target_info.target.libPrefix(), 2053 static_lib, 2054 target_info.target.staticLibSuffix(), 2055 }); 2056 } 2057 try search_paths.appendSlice("\n suggestion: use full paths to static libraries on the command line rather than using -l and -L arguments"); 2058 fatal("static library '{s}' not found. search paths: {s}", .{ 2059 static_lib, search_paths.items, 2060 }); 2061 } 2062 } 2063 } 2064 2065 const object_format: std.Target.ObjectFormat = blk: { 2066 const ofmt = target_ofmt orelse break :blk target_info.target.getObjectFormat(); 2067 if (mem.eql(u8, ofmt, "elf")) { 2068 break :blk .elf; 2069 } else if (mem.eql(u8, ofmt, "c")) { 2070 break :blk .c; 2071 } else if (mem.eql(u8, ofmt, "coff")) { 2072 break :blk .coff; 2073 } else if (mem.eql(u8, ofmt, "macho")) { 2074 break :blk .macho; 2075 } else if (mem.eql(u8, ofmt, "wasm")) { 2076 break :blk .wasm; 2077 } else if (mem.eql(u8, ofmt, "hex")) { 2078 break :blk .hex; 2079 } else if (mem.eql(u8, ofmt, "raw")) { 2080 break :blk .raw; 2081 } else if (mem.eql(u8, ofmt, "spirv")) { 2082 break :blk .spirv; 2083 } else { 2084 fatal("unsupported object format: {s}", .{ofmt}); 2085 } 2086 }; 2087 2088 if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) { 2089 const total_obj_count = c_source_files.items.len + 2090 @boolToInt(root_src_file != null) + 2091 link_objects.items.len; 2092 if (total_obj_count > 1) { 2093 fatal("{s} does not support linking multiple objects into one", .{@tagName(object_format)}); 2094 } 2095 } 2096 2097 var cleanup_emit_bin_dir: ?fs.Dir = null; 2098 defer if (cleanup_emit_bin_dir) |*dir| dir.close(); 2099 2100 const have_enable_cache = enable_cache orelse false; 2101 const optional_version = if (have_version) version else null; 2102 2103 const resolved_soname: ?[]const u8 = switch (soname) { 2104 .yes => |explicit| explicit, 2105 .no => null, 2106 .yes_default_value => switch (object_format) { 2107 .elf => if (have_version) 2108 try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major }) 2109 else 2110 try std.fmt.allocPrint(arena, "lib{s}.so", .{root_name}), 2111 else => null, 2112 }, 2113 }; 2114 2115 const a_out_basename = switch (object_format) { 2116 .coff => "a.exe", 2117 else => "a.out", 2118 }; 2119 2120 const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) { 2121 .no => null, 2122 .yes_default_path => Compilation.EmitLoc{ 2123 .directory = blk: { 2124 switch (arg_mode) { 2125 .run, .zig_test => break :blk null, 2126 else => { 2127 if (have_enable_cache) { 2128 break :blk null; 2129 } else { 2130 break :blk .{ .path = null, .handle = fs.cwd() }; 2131 } 2132 }, 2133 } 2134 }, 2135 .basename = try std.zig.binNameAlloc(arena, .{ 2136 .root_name = root_name, 2137 .target = target_info.target, 2138 .output_mode = output_mode, 2139 .link_mode = link_mode, 2140 .object_format = object_format, 2141 .version = optional_version, 2142 }), 2143 }, 2144 .yes => |full_path| b: { 2145 const basename = fs.path.basename(full_path); 2146 if (have_enable_cache) { 2147 break :b Compilation.EmitLoc{ 2148 .basename = basename, 2149 .directory = null, 2150 }; 2151 } 2152 if (fs.path.dirname(full_path)) |dirname| { 2153 const handle = fs.cwd().openDir(dirname, .{}) catch |err| { 2154 fatal("unable to open output directory '{s}': {s}", .{ dirname, @errorName(err) }); 2155 }; 2156 cleanup_emit_bin_dir = handle; 2157 break :b Compilation.EmitLoc{ 2158 .basename = basename, 2159 .directory = .{ 2160 .path = dirname, 2161 .handle = handle, 2162 }, 2163 }; 2164 } else { 2165 break :b Compilation.EmitLoc{ 2166 .basename = basename, 2167 .directory = .{ .path = null, .handle = fs.cwd() }, 2168 }; 2169 } 2170 }, 2171 .yes_a_out => Compilation.EmitLoc{ 2172 .directory = null, 2173 .basename = a_out_basename, 2174 }, 2175 }; 2176 2177 const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name}); 2178 var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| { 2179 switch (emit_h) { 2180 .yes => |p| { 2181 fatal("unable to open directory from argument '-femit-h', '{s}': {s}", .{ 2182 p, @errorName(err), 2183 }); 2184 }, 2185 .yes_default_path => { 2186 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ 2187 default_h_basename, @errorName(err), 2188 }); 2189 }, 2190 .no => unreachable, 2191 } 2192 }; 2193 defer emit_h_resolved.deinit(); 2194 2195 const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name}); 2196 var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| { 2197 switch (emit_asm) { 2198 .yes => |p| { 2199 fatal("unable to open directory from argument '-femit-asm', '{s}': {s}", .{ 2200 p, @errorName(err), 2201 }); 2202 }, 2203 .yes_default_path => { 2204 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ 2205 default_asm_basename, @errorName(err), 2206 }); 2207 }, 2208 .no => unreachable, 2209 } 2210 }; 2211 defer emit_asm_resolved.deinit(); 2212 2213 const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name}); 2214 var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| { 2215 switch (emit_llvm_ir) { 2216 .yes => |p| { 2217 fatal("unable to open directory from argument '-femit-llvm-ir', '{s}': {s}", .{ 2218 p, @errorName(err), 2219 }); 2220 }, 2221 .yes_default_path => { 2222 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ 2223 default_llvm_ir_basename, @errorName(err), 2224 }); 2225 }, 2226 .no => unreachable, 2227 } 2228 }; 2229 defer emit_llvm_ir_resolved.deinit(); 2230 2231 const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name}); 2232 var emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename) catch |err| { 2233 switch (emit_llvm_bc) { 2234 .yes => |p| { 2235 fatal("unable to open directory from argument '-femit-llvm-bc', '{s}': {s}", .{ 2236 p, @errorName(err), 2237 }); 2238 }, 2239 .yes_default_path => { 2240 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ 2241 default_llvm_bc_basename, @errorName(err), 2242 }); 2243 }, 2244 .no => unreachable, 2245 } 2246 }; 2247 defer emit_llvm_bc_resolved.deinit(); 2248 2249 const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name}); 2250 var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| { 2251 switch (emit_analysis) { 2252 .yes => |p| { 2253 fatal("unable to open directory from argument '-femit-analysis', '{s}': {s}", .{ 2254 p, @errorName(err), 2255 }); 2256 }, 2257 .yes_default_path => { 2258 fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ 2259 default_analysis_basename, @errorName(err), 2260 }); 2261 }, 2262 .no => unreachable, 2263 } 2264 }; 2265 defer emit_analysis_resolved.deinit(); 2266 2267 var emit_docs_resolved = emit_docs.resolve("docs") catch |err| { 2268 switch (emit_docs) { 2269 .yes => |p| { 2270 fatal("unable to open directory from argument '-femit-docs', '{s}': {s}", .{ 2271 p, @errorName(err), 2272 }); 2273 }, 2274 .yes_default_path => { 2275 fatal("unable to open directory 'docs': {s}", .{@errorName(err)}); 2276 }, 2277 .no => unreachable, 2278 } 2279 }; 2280 defer emit_docs_resolved.deinit(); 2281 2282 const is_exe_or_dyn_lib = switch (output_mode) { 2283 .Obj => false, 2284 .Lib => (link_mode orelse .Static) == .Dynamic, 2285 .Exe => true, 2286 }; 2287 // Note that cmake when targeting Windows will try to execute 2288 // zig cc to make an executable and output an implib too. 2289 const implib_eligible = is_exe_or_dyn_lib and 2290 emit_bin_loc != null and target_info.target.os.tag == .windows; 2291 if (!implib_eligible) { 2292 if (!emit_implib_arg_provided) { 2293 emit_implib = .no; 2294 } else if (emit_implib != .no) { 2295 fatal("the argument -femit-implib is allowed only when building a Windows DLL", .{}); 2296 } 2297 } 2298 const default_implib_basename = try std.fmt.allocPrint(arena, "{s}.lib", .{root_name}); 2299 var emit_implib_resolved = switch (emit_implib) { 2300 .no => Emit.Resolved{ .data = null, .dir = null }, 2301 .yes => |p| emit_implib.resolve(default_implib_basename) catch |err| { 2302 fatal("unable to open directory from argument '-femit-implib', '{s}': {s}", .{ 2303 p, @errorName(err), 2304 }); 2305 }, 2306 .yes_default_path => Emit.Resolved{ 2307 .data = Compilation.EmitLoc{ 2308 .directory = emit_bin_loc.?.directory, 2309 .basename = default_implib_basename, 2310 }, 2311 .dir = null, 2312 }, 2313 }; 2314 defer emit_implib_resolved.deinit(); 2315 2316 const main_pkg: ?*Package = if (root_src_file) |src_path| blk: { 2317 if (main_pkg_path) |p| { 2318 const rel_src_path = try fs.path.relative(gpa, p, src_path); 2319 defer gpa.free(rel_src_path); 2320 break :blk try Package.create(gpa, p, rel_src_path); 2321 } else { 2322 break :blk try Package.create(gpa, fs.path.dirname(src_path), fs.path.basename(src_path)); 2323 } 2324 } else null; 2325 defer if (main_pkg) |p| p.destroy(gpa); 2326 2327 // Transfer packages added with --pkg-begin/--pkg-end to the root package 2328 if (main_pkg) |pkg| { 2329 pkg.table = pkg_tree_root.table; 2330 pkg_tree_root.table = .{}; 2331 } 2332 2333 const self_exe_path = try fs.selfExePathAlloc(arena); 2334 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{ 2335 .path = lib_dir, 2336 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| { 2337 fatal("unable to open zig lib directory from 'zig-lib-dir' argument or env, '{s}': {s}", .{ lib_dir, @errorName(err) }); 2338 }, 2339 } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { 2340 fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); 2341 }; 2342 defer zig_lib_directory.handle.close(); 2343 2344 var thread_pool: ThreadPool = undefined; 2345 try thread_pool.init(gpa); 2346 defer thread_pool.deinit(); 2347 2348 var libc_installation: ?LibCInstallation = null; 2349 defer if (libc_installation) |*l| l.deinit(gpa); 2350 2351 if (libc_paths_file) |paths_file| { 2352 libc_installation = LibCInstallation.parse(gpa, paths_file, cross_target) catch |err| { 2353 fatal("unable to parse libc paths file at path {s}: {s}", .{ paths_file, @errorName(err) }); 2354 }; 2355 } 2356 2357 var global_cache_directory: Compilation.Directory = l: { 2358 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); 2359 break :l .{ 2360 .handle = try fs.cwd().makeOpenPath(p, .{}), 2361 .path = p, 2362 }; 2363 }; 2364 defer global_cache_directory.handle.close(); 2365 2366 var cleanup_local_cache_dir: ?fs.Dir = null; 2367 defer if (cleanup_local_cache_dir) |*dir| dir.close(); 2368 2369 var local_cache_directory: Compilation.Directory = l: { 2370 if (override_local_cache_dir) |local_cache_dir_path| { 2371 const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}); 2372 cleanup_local_cache_dir = dir; 2373 break :l .{ 2374 .handle = dir, 2375 .path = local_cache_dir_path, 2376 }; 2377 } 2378 if (arg_mode == .run) { 2379 break :l global_cache_directory; 2380 } 2381 if (main_pkg) |pkg| { 2382 const cache_dir_path = try pkg.root_src_directory.join(arena, &[_][]const u8{"zig-cache"}); 2383 const dir = try pkg.root_src_directory.handle.makeOpenPath("zig-cache", .{}); 2384 cleanup_local_cache_dir = dir; 2385 break :l .{ 2386 .handle = dir, 2387 .path = cache_dir_path, 2388 }; 2389 } 2390 // Otherwise we really don't have a reasonable place to put the local cache directory, 2391 // so we utilize the global one. 2392 break :l global_cache_directory; 2393 }; 2394 2395 if (build_options.have_llvm and emit_asm != .no) { 2396 // LLVM has no way to set this non-globally. 2397 const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" }; 2398 @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv); 2399 } 2400 2401 const clang_passthrough_mode = switch (arg_mode) { 2402 .cc, .cpp, .translate_c => true, 2403 else => false, 2404 }; 2405 2406 gimmeMoreOfThoseSweetSweetFileDescriptors(); 2407 2408 const comp = Compilation.create(gpa, .{ 2409 .zig_lib_directory = zig_lib_directory, 2410 .local_cache_directory = local_cache_directory, 2411 .global_cache_directory = global_cache_directory, 2412 .root_name = root_name, 2413 .target = target_info.target, 2414 .is_native_os = cross_target.isNativeOs(), 2415 .is_native_abi = cross_target.isNativeAbi(), 2416 .dynamic_linker = target_info.dynamic_linker.get(), 2417 .sysroot = sysroot, 2418 .output_mode = output_mode, 2419 .main_pkg = main_pkg, 2420 .emit_bin = emit_bin_loc, 2421 .emit_h = emit_h_resolved.data, 2422 .emit_asm = emit_asm_resolved.data, 2423 .emit_llvm_ir = emit_llvm_ir_resolved.data, 2424 .emit_llvm_bc = emit_llvm_bc_resolved.data, 2425 .emit_docs = emit_docs_resolved.data, 2426 .emit_analysis = emit_analysis_resolved.data, 2427 .emit_implib = emit_implib_resolved.data, 2428 .link_mode = link_mode, 2429 .dll_export_fns = dll_export_fns, 2430 .object_format = object_format, 2431 .optimize_mode = optimize_mode, 2432 .keep_source_files_loaded = false, 2433 .clang_argv = clang_argv.items, 2434 .lib_dirs = lib_dirs.items, 2435 .rpath_list = rpath_list.items, 2436 .c_source_files = c_source_files.items, 2437 .link_objects = link_objects.items, 2438 .framework_dirs = framework_dirs.items, 2439 .frameworks = frameworks.items, 2440 .system_lib_names = system_libs.keys(), 2441 .system_lib_infos = system_libs.values(), 2442 .wasi_emulated_libs = wasi_emulated_libs.items, 2443 .link_libc = link_libc, 2444 .link_libcpp = link_libcpp, 2445 .link_libunwind = link_libunwind, 2446 .want_pic = want_pic, 2447 .want_pie = want_pie, 2448 .want_lto = want_lto, 2449 .want_unwind_tables = want_unwind_tables, 2450 .want_sanitize_c = want_sanitize_c, 2451 .want_stack_check = want_stack_check, 2452 .want_red_zone = want_red_zone, 2453 .omit_frame_pointer = omit_frame_pointer, 2454 .want_valgrind = want_valgrind, 2455 .want_tsan = want_tsan, 2456 .want_compiler_rt = want_compiler_rt, 2457 .use_llvm = use_llvm, 2458 .use_lld = use_lld, 2459 .use_clang = use_clang, 2460 .use_stage1 = use_stage1, 2461 .rdynamic = rdynamic, 2462 .linker_script = linker_script, 2463 .version_script = version_script, 2464 .disable_c_depfile = disable_c_depfile, 2465 .soname = resolved_soname, 2466 .linker_gc_sections = linker_gc_sections, 2467 .linker_allow_shlib_undefined = linker_allow_shlib_undefined, 2468 .linker_bind_global_refs_locally = linker_bind_global_refs_locally, 2469 .linker_import_memory = linker_import_memory, 2470 .linker_import_table = linker_import_table, 2471 .linker_export_table = linker_export_table, 2472 .linker_initial_memory = linker_initial_memory, 2473 .linker_max_memory = linker_max_memory, 2474 .linker_global_base = linker_global_base, 2475 .linker_export_symbol_names = linker_export_symbol_names.items, 2476 .linker_z_nodelete = linker_z_nodelete, 2477 .linker_z_notext = linker_z_notext, 2478 .linker_z_defs = linker_z_defs, 2479 .linker_z_origin = linker_z_origin, 2480 .linker_z_noexecstack = linker_z_noexecstack, 2481 .linker_z_now = linker_z_now, 2482 .linker_z_relro = linker_z_relro, 2483 .linker_tsaware = linker_tsaware, 2484 .linker_nxcompat = linker_nxcompat, 2485 .linker_dynamicbase = linker_dynamicbase, 2486 .linker_optimization = linker_optimization, 2487 .major_subsystem_version = major_subsystem_version, 2488 .minor_subsystem_version = minor_subsystem_version, 2489 .link_eh_frame_hdr = link_eh_frame_hdr, 2490 .link_emit_relocs = link_emit_relocs, 2491 .stack_size_override = stack_size_override, 2492 .image_base_override = image_base_override, 2493 .strip = strip, 2494 .single_threaded = single_threaded, 2495 .function_sections = function_sections, 2496 .self_exe_path = self_exe_path, 2497 .thread_pool = &thread_pool, 2498 .clang_passthrough_mode = clang_passthrough_mode, 2499 .clang_preprocessor_mode = clang_preprocessor_mode, 2500 .version = optional_version, 2501 .libc_installation = if (libc_installation) |*lci| lci else null, 2502 .verbose_cc = verbose_cc, 2503 .verbose_link = verbose_link, 2504 .verbose_air = verbose_air, 2505 .verbose_mir = verbose_mir, 2506 .verbose_llvm_ir = verbose_llvm_ir, 2507 .verbose_cimport = verbose_cimport, 2508 .verbose_llvm_cpu_features = verbose_llvm_cpu_features, 2509 .machine_code_model = machine_code_model, 2510 .color = color, 2511 .time_report = time_report, 2512 .stack_report = stack_report, 2513 .is_test = arg_mode == .zig_test, 2514 .each_lib_rpath = each_lib_rpath, 2515 .test_evented_io = test_evented_io, 2516 .test_filter = test_filter, 2517 .test_name_prefix = test_name_prefix, 2518 .disable_lld_caching = !have_enable_cache, 2519 .subsystem = subsystem, 2520 .wasi_exec_model = wasi_exec_model, 2521 .debug_compile_errors = debug_compile_errors, 2522 .enable_link_snapshots = enable_link_snapshots, 2523 .native_darwin_sdk = native_darwin_sdk, 2524 .install_name = install_name, 2525 }) catch |err| switch (err) { 2526 error.LibCUnavailable => { 2527 const target = target_info.target; 2528 const triple_name = try target.zigTriple(arena); 2529 std.log.err("unable to find or provide libc for target '{s}'", .{triple_name}); 2530 2531 for (target_util.available_libcs) |t| { 2532 if (t.arch == target.cpu.arch and t.os == target.os.tag) { 2533 if (t.os_ver) |os_ver| { 2534 std.log.info("zig can provide libc for related target {s}-{s}.{d}-{s}", .{ 2535 @tagName(t.arch), @tagName(t.os), os_ver.major, @tagName(t.abi), 2536 }); 2537 } else { 2538 std.log.info("zig can provide libc for related target {s}-{s}-{s}", .{ 2539 @tagName(t.arch), @tagName(t.os), @tagName(t.abi), 2540 }); 2541 } 2542 } 2543 } 2544 process.exit(1); 2545 }, 2546 error.ExportTableAndImportTableConflict => { 2547 fatal("--import-table and --export-table may not be used together", .{}); 2548 }, 2549 else => fatal("unable to create compilation: {s}", .{@errorName(err)}), 2550 }; 2551 var comp_destroyed = false; 2552 defer if (!comp_destroyed) comp.destroy(); 2553 2554 if (show_builtin) { 2555 return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); 2556 } 2557 if (arg_mode == .translate_c) { 2558 return cmdTranslateC(comp, arena, have_enable_cache); 2559 } 2560 2561 const hook: AfterUpdateHook = blk: { 2562 if (!have_enable_cache) 2563 break :blk .none; 2564 2565 switch (emit_bin) { 2566 .no => break :blk .none, 2567 .yes_default_path => break :blk .{ 2568 .print = comp.bin_file.options.emit.?.directory.path orelse ".", 2569 }, 2570 .yes => |full_path| break :blk .{ .update = full_path }, 2571 .yes_a_out => break :blk .{ .update = a_out_basename }, 2572 } 2573 }; 2574 2575 updateModule(gpa, comp, hook) catch |err| switch (err) { 2576 error.SemanticAnalyzeFail => if (!watch) process.exit(1), 2577 else => |e| return e, 2578 }; 2579 try comp.makeBinFileExecutable(); 2580 2581 if (build_options.is_stage1 and comp.stage1_lock != null and watch) { 2582 warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{}); 2583 } 2584 2585 if (test_exec_args.items.len == 0 and object_format == .c) default_exec_args: { 2586 // Default to using `zig run` to execute the produced .c code from `zig test`. 2587 const c_code_loc = emit_bin_loc orelse break :default_exec_args; 2588 const c_code_directory = c_code_loc.directory orelse comp.bin_file.options.emit.?.directory; 2589 const c_code_path = try fs.path.join(arena, &[_][]const u8{ 2590 c_code_directory.path orelse ".", c_code_loc.basename, 2591 }); 2592 try test_exec_args.appendSlice(&.{ self_exe_path, "run", "-lc", c_code_path }); 2593 } 2594 2595 const run_or_test = switch (arg_mode) { 2596 .run => true, 2597 .zig_test => !test_no_exec, 2598 else => false, 2599 }; 2600 if (run_or_test) { 2601 try runOrTest( 2602 comp, 2603 gpa, 2604 arena, 2605 emit_bin_loc, 2606 test_exec_args.items, 2607 self_exe_path, 2608 arg_mode, 2609 target_info, 2610 watch, 2611 &comp_destroyed, 2612 all_args, 2613 runtime_args_start, 2614 link_libc, 2615 ); 2616 } 2617 2618 const stdin = std.io.getStdIn().reader(); 2619 const stderr = std.io.getStdErr().writer(); 2620 var repl_buf: [1024]u8 = undefined; 2621 2622 const ReplCmd = enum { 2623 update, 2624 help, 2625 run, 2626 update_and_run, 2627 }; 2628 2629 var last_cmd: ReplCmd = .help; 2630 2631 while (watch) { 2632 try stderr.print("(zig) ", .{}); 2633 try comp.makeBinFileExecutable(); 2634 if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { 2635 try stderr.print("\nUnable to parse command: {s}\n", .{@errorName(err)}); 2636 continue; 2637 }) |line| { 2638 const actual_line = mem.trimRight(u8, line, "\r\n "); 2639 const cmd: ReplCmd = blk: { 2640 if (mem.eql(u8, actual_line, "update")) { 2641 break :blk .update; 2642 } else if (mem.eql(u8, actual_line, "exit")) { 2643 break; 2644 } else if (mem.eql(u8, actual_line, "help")) { 2645 break :blk .help; 2646 } else if (mem.eql(u8, actual_line, "run")) { 2647 break :blk .run; 2648 } else if (mem.eql(u8, actual_line, "update-and-run")) { 2649 break :blk .update_and_run; 2650 } else if (actual_line.len == 0) { 2651 break :blk last_cmd; 2652 } else { 2653 try stderr.print("unknown command: {s}\n", .{actual_line}); 2654 continue; 2655 } 2656 }; 2657 last_cmd = cmd; 2658 switch (cmd) { 2659 .update => { 2660 tracy.frameMark(); 2661 if (output_mode == .Exe) { 2662 try comp.makeBinFileWritable(); 2663 } 2664 updateModule(gpa, comp, hook) catch |err| switch (err) { 2665 error.SemanticAnalyzeFail => continue, 2666 else => |e| return e, 2667 }; 2668 }, 2669 .help => { 2670 try stderr.writeAll(repl_help); 2671 }, 2672 .run => { 2673 tracy.frameMark(); 2674 try runOrTest( 2675 comp, 2676 gpa, 2677 arena, 2678 emit_bin_loc, 2679 test_exec_args.items, 2680 self_exe_path, 2681 arg_mode, 2682 target_info, 2683 watch, 2684 &comp_destroyed, 2685 all_args, 2686 runtime_args_start, 2687 link_libc, 2688 ); 2689 }, 2690 .update_and_run => { 2691 tracy.frameMark(); 2692 if (output_mode == .Exe) { 2693 try comp.makeBinFileWritable(); 2694 } 2695 updateModule(gpa, comp, hook) catch |err| switch (err) { 2696 error.SemanticAnalyzeFail => continue, 2697 else => |e| return e, 2698 }; 2699 try comp.makeBinFileExecutable(); 2700 try runOrTest( 2701 comp, 2702 gpa, 2703 arena, 2704 emit_bin_loc, 2705 test_exec_args.items, 2706 self_exe_path, 2707 arg_mode, 2708 target_info, 2709 watch, 2710 &comp_destroyed, 2711 all_args, 2712 runtime_args_start, 2713 link_libc, 2714 ); 2715 }, 2716 } 2717 } else { 2718 break; 2719 } 2720 } 2721 // Skip resource deallocation in release builds; let the OS do it. 2722 return cleanExit(); 2723 } 2724 2725 fn parseCrossTargetOrReportFatalError( 2726 allocator: Allocator, 2727 opts: std.zig.CrossTarget.ParseOptions, 2728 ) !std.zig.CrossTarget { 2729 var opts_with_diags = opts; 2730 var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{}; 2731 if (opts_with_diags.diagnostics == null) { 2732 opts_with_diags.diagnostics = &diags; 2733 } 2734 return std.zig.CrossTarget.parse(opts_with_diags) catch |err| switch (err) { 2735 error.UnknownCpuModel => { 2736 help: { 2737 var help_text = std.ArrayList(u8).init(allocator); 2738 defer help_text.deinit(); 2739 for (diags.arch.?.allCpuModels()) |cpu| { 2740 help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help; 2741 } 2742 std.log.info("Available CPUs for architecture '{s}':\n{s}", .{ 2743 @tagName(diags.arch.?), help_text.items, 2744 }); 2745 } 2746 fatal("Unknown CPU: '{s}'", .{diags.cpu_name.?}); 2747 }, 2748 error.UnknownCpuFeature => { 2749 help: { 2750 var help_text = std.ArrayList(u8).init(allocator); 2751 defer help_text.deinit(); 2752 for (diags.arch.?.allFeaturesList()) |feature| { 2753 help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help; 2754 } 2755 std.log.info("Available CPU features for architecture '{s}':\n{s}", .{ 2756 @tagName(diags.arch.?), help_text.items, 2757 }); 2758 } 2759 fatal("Unknown CPU feature: '{s}'", .{diags.unknown_feature_name}); 2760 }, 2761 else => |e| return e, 2762 }; 2763 } 2764 2765 fn runOrTest( 2766 comp: *Compilation, 2767 gpa: Allocator, 2768 arena: Allocator, 2769 emit_bin_loc: ?Compilation.EmitLoc, 2770 test_exec_args: []const ?[]const u8, 2771 self_exe_path: []const u8, 2772 arg_mode: ArgMode, 2773 target_info: std.zig.system.NativeTargetInfo, 2774 watch: bool, 2775 comp_destroyed: *bool, 2776 all_args: []const []const u8, 2777 runtime_args_start: ?usize, 2778 link_libc: bool, 2779 ) !void { 2780 const exe_loc = emit_bin_loc orelse return; 2781 const exe_directory = exe_loc.directory orelse comp.bin_file.options.emit.?.directory; 2782 const exe_path = try fs.path.join(arena, &[_][]const u8{ 2783 exe_directory.path orelse ".", exe_loc.basename, 2784 }); 2785 2786 var argv = std.ArrayList([]const u8).init(gpa); 2787 defer argv.deinit(); 2788 2789 if (test_exec_args.len == 0) { 2790 // when testing pass the zig_exe_path to argv 2791 if (arg_mode == .zig_test) 2792 try argv.appendSlice(&[_][]const u8{ 2793 exe_path, self_exe_path, 2794 }) 2795 // when running just pass the current exe 2796 else 2797 try argv.appendSlice(&[_][]const u8{ 2798 exe_path, 2799 }); 2800 } else { 2801 for (test_exec_args) |arg| { 2802 if (arg) |a| { 2803 try argv.append(a); 2804 } else { 2805 try argv.appendSlice(&[_][]const u8{ 2806 exe_path, self_exe_path, 2807 }); 2808 } 2809 } 2810 } 2811 if (runtime_args_start) |i| { 2812 try argv.appendSlice(all_args[i..]); 2813 } 2814 // We do not execve for tests because if the test fails we want to print 2815 // the error message and invocation below. 2816 if (std.process.can_execv and arg_mode == .run and !watch) { 2817 // execv releases the locks; no need to destroy the Compilation here. 2818 const err = std.process.execv(gpa, argv.items); 2819 try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc); 2820 const cmd = try argvCmd(arena, argv.items); 2821 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); 2822 } else { 2823 const child = try std.ChildProcess.init(argv.items, gpa); 2824 defer child.deinit(); 2825 2826 child.stdin_behavior = .Inherit; 2827 child.stdout_behavior = .Inherit; 2828 child.stderr_behavior = .Inherit; 2829 2830 if (!watch) { 2831 // Here we release all the locks associated with the Compilation so 2832 // that whatever this child process wants to do won't deadlock. 2833 comp.destroy(); 2834 comp_destroyed.* = true; 2835 } 2836 2837 const term = child.spawnAndWait() catch |err| { 2838 try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc); 2839 const cmd = try argvCmd(arena, argv.items); 2840 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd }); 2841 }; 2842 switch (arg_mode) { 2843 .run, .build => { 2844 switch (term) { 2845 .Exited => |code| { 2846 if (code == 0) { 2847 if (!watch) return cleanExit(); 2848 } else if (watch) { 2849 warn("process exited with code {d}", .{code}); 2850 } else { 2851 // TODO https://github.com/ziglang/zig/issues/6342 2852 process.exit(1); 2853 } 2854 }, 2855 else => { 2856 if (watch) { 2857 warn("process aborted abnormally", .{}); 2858 } else { 2859 process.exit(1); 2860 } 2861 }, 2862 } 2863 }, 2864 .zig_test => { 2865 switch (term) { 2866 .Exited => |code| { 2867 if (code == 0) { 2868 if (!watch) return cleanExit(); 2869 } else { 2870 const cmd = try argvCmd(arena, argv.items); 2871 fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); 2872 } 2873 }, 2874 else => { 2875 const cmd = try argvCmd(arena, argv.items); 2876 fatal("the following test command crashed:\n{s}", .{cmd}); 2877 }, 2878 } 2879 }, 2880 else => unreachable, 2881 } 2882 } 2883 } 2884 2885 const AfterUpdateHook = union(enum) { 2886 none, 2887 print: []const u8, 2888 update: []const u8, 2889 }; 2890 2891 fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void { 2892 try comp.update(); 2893 2894 var errors = try comp.getAllErrorsAlloc(); 2895 defer errors.deinit(comp.gpa); 2896 2897 if (errors.list.len != 0) { 2898 const ttyconf: std.debug.TTY.Config = switch (comp.color) { 2899 .auto => std.debug.detectTTYConfig(), 2900 .on => .escape_codes, 2901 .off => .no_color, 2902 }; 2903 for (errors.list) |full_err_msg| { 2904 full_err_msg.renderToStdErr(ttyconf); 2905 } 2906 const log_text = comp.getCompileLogOutput(); 2907 if (log_text.len != 0) { 2908 std.debug.print("\nCompile Log Output:\n{s}", .{log_text}); 2909 } 2910 return error.SemanticAnalyzeFail; 2911 } else switch (hook) { 2912 .none => {}, 2913 .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}), 2914 .update => |full_path| { 2915 const bin_sub_path = comp.bin_file.options.emit.?.sub_path; 2916 const cwd = fs.cwd(); 2917 const cache_dir = comp.bin_file.options.emit.?.directory.handle; 2918 _ = try cache_dir.updateFile(bin_sub_path, cwd, full_path, .{}); 2919 2920 // If a .pdb file is part of the expected output, we must also copy 2921 // it into place here. 2922 const is_coff = comp.bin_file.options.object_format == .coff; 2923 const have_pdb = is_coff and !comp.bin_file.options.strip; 2924 if (have_pdb) { 2925 // Replace `.out` or `.exe` with `.pdb` on both the source and destination 2926 const src_bin_ext = fs.path.extension(bin_sub_path); 2927 const dst_bin_ext = fs.path.extension(full_path); 2928 2929 const src_pdb_path = try std.fmt.allocPrint(gpa, "{s}.pdb", .{ 2930 bin_sub_path[0 .. bin_sub_path.len - src_bin_ext.len], 2931 }); 2932 defer gpa.free(src_pdb_path); 2933 2934 const dst_pdb_path = try std.fmt.allocPrint(gpa, "{s}.pdb", .{ 2935 full_path[0 .. full_path.len - dst_bin_ext.len], 2936 }); 2937 defer gpa.free(dst_pdb_path); 2938 2939 _ = try cache_dir.updateFile(src_pdb_path, cwd, dst_pdb_path, .{}); 2940 } 2941 }, 2942 } 2943 } 2944 2945 fn freePkgTree(gpa: Allocator, pkg: *Package, free_parent: bool) void { 2946 { 2947 var it = pkg.table.valueIterator(); 2948 while (it.next()) |value| { 2949 freePkgTree(gpa, value.*, true); 2950 } 2951 } 2952 if (free_parent) { 2953 pkg.destroy(gpa); 2954 } 2955 } 2956 2957 fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void { 2958 if (!build_options.have_llvm) 2959 fatal("cannot translate-c: compiler built without LLVM extensions", .{}); 2960 2961 assert(comp.c_source_files.len == 1); 2962 const c_source_file = comp.c_source_files[0]; 2963 2964 const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.bin_file.options.root_name}); 2965 2966 var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); 2967 defer if (enable_cache) man.deinit(); 2968 2969 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects 2970 man.hashCSource(c_source_file) catch |err| { 2971 fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); 2972 }; 2973 2974 const digest = if (try man.hit()) man.final() else digest: { 2975 var argv = std.ArrayList([]const u8).init(arena); 2976 try argv.append(""); // argv[0] is program name, actual args start at [1] 2977 2978 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); 2979 defer zig_cache_tmp_dir.close(); 2980 2981 const ext = Compilation.classifyFileExt(c_source_file.src_path); 2982 const out_dep_path: ?[]const u8 = blk: { 2983 if (comp.disable_c_depfile or !ext.clangSupportsDepFile()) 2984 break :blk null; 2985 2986 const c_src_basename = fs.path.basename(c_source_file.src_path); 2987 const dep_basename = try std.fmt.allocPrint(arena, "{s}.d", .{c_src_basename}); 2988 const out_dep_path = try comp.tmpFilePath(arena, dep_basename); 2989 break :blk out_dep_path; 2990 }; 2991 2992 try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path); 2993 try argv.append(c_source_file.src_path); 2994 2995 if (comp.verbose_cc) { 2996 std.debug.print("clang ", .{}); 2997 Compilation.dump_argv(argv.items); 2998 } 2999 3000 // Convert to null terminated args. 3001 const clang_args_len = argv.items.len + c_source_file.extra_flags.len; 3002 const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, clang_args_len + 1); 3003 new_argv_with_sentinel[clang_args_len] = null; 3004 const new_argv = new_argv_with_sentinel[0..clang_args_len :null]; 3005 for (argv.items) |arg, i| { 3006 new_argv[i] = try arena.dupeZ(u8, arg); 3007 } 3008 for (c_source_file.extra_flags) |arg, i| { 3009 new_argv[argv.items.len + i] = try arena.dupeZ(u8, arg); 3010 } 3011 3012 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); 3013 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); 3014 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; 3015 var tree = translate_c.translate( 3016 comp.gpa, 3017 new_argv.ptr, 3018 new_argv.ptr + new_argv.len, 3019 &clang_errors, 3020 c_headers_dir_path_z, 3021 ) catch |err| switch (err) { 3022 error.OutOfMemory => return error.OutOfMemory, 3023 error.ASTUnitFailure => fatal("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", .{}), 3024 error.SemanticAnalyzeFail => { 3025 for (clang_errors) |clang_err| { 3026 std.debug.print("{s}:{d}:{d}: {s}\n", .{ 3027 if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", 3028 clang_err.line + 1, 3029 clang_err.column + 1, 3030 clang_err.msg_ptr[0..clang_err.msg_len], 3031 }); 3032 } 3033 process.exit(1); 3034 }, 3035 }; 3036 defer tree.deinit(comp.gpa); 3037 3038 if (out_dep_path) |dep_file_path| { 3039 const dep_basename = std.fs.path.basename(dep_file_path); 3040 // Add the files depended on to the cache system. 3041 try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); 3042 // Just to save disk space, we delete the file because it is never needed again. 3043 zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { 3044 warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }); 3045 }; 3046 } 3047 3048 const digest = man.final(); 3049 const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest }); 3050 3051 var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); 3052 defer o_dir.close(); 3053 3054 var zig_file = try o_dir.createFile(translated_zig_basename, .{}); 3055 defer zig_file.close(); 3056 3057 const formatted = try tree.render(comp.gpa); 3058 defer comp.gpa.free(formatted); 3059 3060 try zig_file.writeAll(formatted); 3061 3062 man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{ 3063 @errorName(err), 3064 }); 3065 3066 break :digest digest; 3067 }; 3068 3069 if (enable_cache) { 3070 const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ 3071 "o", &digest, translated_zig_basename, 3072 }); 3073 try io.getStdOut().writer().print("{s}\n", .{full_zig_path}); 3074 return cleanExit(); 3075 } else { 3076 const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); 3077 const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| { 3078 fatal("unable to open cached translated zig file '{s}{s}{s}': {s}", .{ comp.local_cache_directory.path, fs.path.sep_str, out_zig_path, @errorName(err) }); 3079 }; 3080 defer zig_file.close(); 3081 try io.getStdOut().writeFileAll(zig_file, .{}); 3082 return cleanExit(); 3083 } 3084 } 3085 3086 pub const usage_libc = 3087 \\Usage: zig libc 3088 \\ 3089 \\ Detect the native libc installation and print the resulting 3090 \\ paths to stdout. You can save this into a file and then edit 3091 \\ the paths to create a cross compilation libc kit. Then you 3092 \\ can pass `--libc [file]` for Zig to use it. 3093 \\ 3094 \\Usage: zig libc [paths_file] 3095 \\ 3096 \\ Parse a libc installation text file and validate it. 3097 \\ 3098 \\Options: 3099 \\ -h, --help Print this help and exit 3100 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command 3101 \\ 3102 ; 3103 3104 pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { 3105 var input_file: ?[]const u8 = null; 3106 var target_arch_os_abi: []const u8 = "native"; 3107 { 3108 var i: usize = 0; 3109 while (i < args.len) : (i += 1) { 3110 const arg = args[i]; 3111 if (mem.startsWith(u8, arg, "-")) { 3112 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { 3113 const stdout = io.getStdOut().writer(); 3114 try stdout.writeAll(usage_libc); 3115 return cleanExit(); 3116 } else if (mem.eql(u8, arg, "-target")) { 3117 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); 3118 i += 1; 3119 target_arch_os_abi = args[i]; 3120 } else { 3121 fatal("unrecognized parameter: '{s}'", .{arg}); 3122 } 3123 } else if (input_file != null) { 3124 fatal("unexpected extra parameter: '{s}'", .{arg}); 3125 } else { 3126 input_file = arg; 3127 } 3128 } 3129 } 3130 3131 const cross_target = try parseCrossTargetOrReportFatalError(gpa, .{ 3132 .arch_os_abi = target_arch_os_abi, 3133 }); 3134 3135 if (input_file) |libc_file| { 3136 var libc = LibCInstallation.parse(gpa, libc_file, cross_target) catch |err| { 3137 fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) }); 3138 }; 3139 defer libc.deinit(gpa); 3140 } else { 3141 if (!cross_target.isNative()) { 3142 fatal("unable to detect libc for non-native target", .{}); 3143 } 3144 3145 var libc = LibCInstallation.findNative(.{ 3146 .allocator = gpa, 3147 .verbose = true, 3148 }) catch |err| { 3149 fatal("unable to detect native libc: {s}", .{@errorName(err)}); 3150 }; 3151 defer libc.deinit(gpa); 3152 3153 var bw = io.bufferedWriter(io.getStdOut().writer()); 3154 try libc.render(bw.writer()); 3155 try bw.flush(); 3156 } 3157 } 3158 3159 pub const usage_init = 3160 \\Usage: zig init-exe 3161 \\ zig init-lib 3162 \\ 3163 \\ Initializes a `zig build` project in the current working 3164 \\ directory. 3165 \\ 3166 \\Options: 3167 \\ -h, --help Print this help and exit 3168 \\ 3169 \\ 3170 ; 3171 3172 pub fn cmdInit( 3173 gpa: Allocator, 3174 arena: Allocator, 3175 args: []const []const u8, 3176 output_mode: std.builtin.OutputMode, 3177 ) !void { 3178 _ = gpa; 3179 { 3180 var i: usize = 0; 3181 while (i < args.len) : (i += 1) { 3182 const arg = args[i]; 3183 if (mem.startsWith(u8, arg, "-")) { 3184 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { 3185 try io.getStdOut().writeAll(usage_init); 3186 return cleanExit(); 3187 } else { 3188 fatal("unrecognized parameter: '{s}'", .{arg}); 3189 } 3190 } else { 3191 fatal("unexpected extra parameter: '{s}'", .{arg}); 3192 } 3193 } 3194 } 3195 const self_exe_path = try fs.selfExePathAlloc(arena); 3196 var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { 3197 fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); 3198 }; 3199 defer zig_lib_directory.handle.close(); 3200 3201 const s = fs.path.sep_str; 3202 const template_sub_path = switch (output_mode) { 3203 .Obj => unreachable, 3204 .Lib => "std" ++ s ++ "special" ++ s ++ "init-lib", 3205 .Exe => "std" ++ s ++ "special" ++ s ++ "init-exe", 3206 }; 3207 var template_dir = zig_lib_directory.handle.openDir(template_sub_path, .{}) catch |err| { 3208 fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{ zig_lib_directory.path, s, template_sub_path, @errorName(err) }); 3209 }; 3210 defer template_dir.close(); 3211 3212 const cwd_path = try process.getCwdAlloc(arena); 3213 const cwd_basename = fs.path.basename(cwd_path); 3214 3215 const max_bytes = 10 * 1024 * 1024; 3216 const build_zig_contents = template_dir.readFileAlloc(arena, "build.zig", max_bytes) catch |err| { 3217 fatal("unable to read template file 'build.zig': {s}", .{@errorName(err)}); 3218 }; 3219 var modified_build_zig_contents = try std.ArrayList(u8).initCapacity(arena, build_zig_contents.len); 3220 for (build_zig_contents) |c| { 3221 if (c == '$') { 3222 try modified_build_zig_contents.appendSlice(cwd_basename); 3223 } else { 3224 try modified_build_zig_contents.append(c); 3225 } 3226 } 3227 const main_zig_contents = template_dir.readFileAlloc(arena, "src" ++ s ++ "main.zig", max_bytes) catch |err| { 3228 fatal("unable to read template file 'main.zig': {s}", .{@errorName(err)}); 3229 }; 3230 if (fs.cwd().access("build.zig", .{})) |_| { 3231 fatal("existing build.zig file would be overwritten", .{}); 3232 } else |err| switch (err) { 3233 error.FileNotFound => {}, 3234 else => fatal("unable to test existence of build.zig: {s}\n", .{@errorName(err)}), 3235 } 3236 if (fs.cwd().access("src" ++ s ++ "main.zig", .{})) |_| { 3237 fatal("existing src" ++ s ++ "main.zig file would be overwritten", .{}); 3238 } else |err| switch (err) { 3239 error.FileNotFound => {}, 3240 else => fatal("unable to test existence of src" ++ s ++ "main.zig: {s}\n", .{@errorName(err)}), 3241 } 3242 var src_dir = try fs.cwd().makeOpenPath("src", .{}); 3243 defer src_dir.close(); 3244 3245 try src_dir.writeFile("main.zig", main_zig_contents); 3246 try fs.cwd().writeFile("build.zig", modified_build_zig_contents.items); 3247 3248 std.log.info("Created build.zig", .{}); 3249 std.log.info("Created src" ++ s ++ "main.zig", .{}); 3250 3251 switch (output_mode) { 3252 .Lib => std.log.info("Next, try `zig build --help` or `zig build test`", .{}), 3253 .Exe => std.log.info("Next, try `zig build --help` or `zig build run`", .{}), 3254 .Obj => unreachable, 3255 } 3256 } 3257 3258 pub const usage_build = 3259 \\Usage: zig build [steps] [options] 3260 \\ 3261 \\ Build a project from build.zig. 3262 \\ 3263 \\Options: 3264 \\ -h, --help Print this help and exit 3265 \\ 3266 \\ 3267 ; 3268 3269 pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { 3270 var prominent_compile_errors: bool = false; 3271 3272 // We want to release all the locks before executing the child process, so we make a nice 3273 // big block here to ensure the cleanup gets run when we extract out our argv. 3274 const child_argv = argv: { 3275 const self_exe_path = try fs.selfExePathAlloc(arena); 3276 3277 var build_file: ?[]const u8 = null; 3278 var override_lib_dir: ?[]const u8 = null; 3279 var override_global_cache_dir: ?[]const u8 = null; 3280 var override_local_cache_dir: ?[]const u8 = null; 3281 var child_argv = std.ArrayList([]const u8).init(arena); 3282 3283 const argv_index_exe = child_argv.items.len; 3284 _ = try child_argv.addOne(); 3285 3286 try child_argv.append(self_exe_path); 3287 3288 const argv_index_build_file = child_argv.items.len; 3289 _ = try child_argv.addOne(); 3290 3291 const argv_index_cache_dir = child_argv.items.len; 3292 _ = try child_argv.addOne(); 3293 3294 const argv_index_global_cache_dir = child_argv.items.len; 3295 _ = try child_argv.addOne(); 3296 3297 { 3298 var i: usize = 0; 3299 while (i < args.len) : (i += 1) { 3300 const arg = args[i]; 3301 if (mem.startsWith(u8, arg, "-")) { 3302 if (mem.eql(u8, arg, "--build-file")) { 3303 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); 3304 i += 1; 3305 build_file = args[i]; 3306 continue; 3307 } else if (mem.eql(u8, arg, "--zig-lib-dir")) { 3308 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); 3309 i += 1; 3310 override_lib_dir = args[i]; 3311 try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); 3312 continue; 3313 } else if (mem.eql(u8, arg, "--cache-dir")) { 3314 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); 3315 i += 1; 3316 override_local_cache_dir = args[i]; 3317 continue; 3318 } else if (mem.eql(u8, arg, "--global-cache-dir")) { 3319 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); 3320 i += 1; 3321 override_global_cache_dir = args[i]; 3322 continue; 3323 } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { 3324 prominent_compile_errors = true; 3325 } 3326 } 3327 try child_argv.append(arg); 3328 } 3329 } 3330 3331 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{ 3332 .path = lib_dir, 3333 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| { 3334 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) }); 3335 }, 3336 } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { 3337 fatal("unable to find zig installation directory '{s}': {s}", .{ self_exe_path, @errorName(err) }); 3338 }; 3339 defer zig_lib_directory.handle.close(); 3340 3341 const std_special = "std" ++ fs.path.sep_str ++ "special"; 3342 const special_dir_path = try zig_lib_directory.join(arena, &[_][]const u8{std_special}); 3343 3344 var main_pkg: Package = .{ 3345 .root_src_directory = .{ 3346 .path = special_dir_path, 3347 .handle = zig_lib_directory.handle.openDir(std_special, .{}) catch |err| { 3348 fatal("unable to open directory '{s}{s}{s}': {s}", .{ override_lib_dir, fs.path.sep_str, std_special, @errorName(err) }); 3349 }, 3350 }, 3351 .root_src_path = "build_runner.zig", 3352 }; 3353 defer main_pkg.root_src_directory.handle.close(); 3354 3355 var cleanup_build_dir: ?fs.Dir = null; 3356 defer if (cleanup_build_dir) |*dir| dir.close(); 3357 3358 const cwd_path = try process.getCwdAlloc(arena); 3359 const build_zig_basename = if (build_file) |bf| fs.path.basename(bf) else "build.zig"; 3360 const build_directory: Compilation.Directory = blk: { 3361 if (build_file) |bf| { 3362 if (fs.path.dirname(bf)) |dirname| { 3363 const dir = fs.cwd().openDir(dirname, .{}) catch |err| { 3364 fatal("unable to open directory to build file from argument 'build-file', '{s}': {s}", .{ dirname, @errorName(err) }); 3365 }; 3366 cleanup_build_dir = dir; 3367 break :blk .{ .path = dirname, .handle = dir }; 3368 } 3369 3370 break :blk .{ .path = null, .handle = fs.cwd() }; 3371 } 3372 // Search up parent directories until we find build.zig. 3373 var dirname: []const u8 = cwd_path; 3374 while (true) { 3375 const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename }); 3376 if (fs.cwd().access(joined_path, .{})) |_| { 3377 const dir = fs.cwd().openDir(dirname, .{}) catch |err| { 3378 fatal("unable to open directory while searching for build.zig file, '{s}': {s}", .{ dirname, @errorName(err) }); 3379 }; 3380 break :blk .{ .path = dirname, .handle = dir }; 3381 } else |err| switch (err) { 3382 error.FileNotFound => { 3383 dirname = fs.path.dirname(dirname) orelse { 3384 std.log.info("{s}", .{ 3385 \\Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`, 3386 \\or see `zig --help` for more options. 3387 }); 3388 fatal("No 'build.zig' file found, in the current directory or any parent directories.", .{}); 3389 }; 3390 continue; 3391 }, 3392 else => |e| return e, 3393 } 3394 } 3395 }; 3396 child_argv.items[argv_index_build_file] = build_directory.path orelse cwd_path; 3397 3398 var build_pkg: Package = .{ 3399 .root_src_directory = build_directory, 3400 .root_src_path = build_zig_basename, 3401 }; 3402 try main_pkg.addAndAdopt(arena, "@build", &build_pkg); 3403 3404 var global_cache_directory: Compilation.Directory = l: { 3405 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); 3406 break :l .{ 3407 .handle = try fs.cwd().makeOpenPath(p, .{}), 3408 .path = p, 3409 }; 3410 }; 3411 defer global_cache_directory.handle.close(); 3412 3413 child_argv.items[argv_index_global_cache_dir] = global_cache_directory.path orelse cwd_path; 3414 3415 var local_cache_directory: Compilation.Directory = l: { 3416 if (override_local_cache_dir) |local_cache_dir_path| { 3417 break :l .{ 3418 .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}), 3419 .path = local_cache_dir_path, 3420 }; 3421 } 3422 const cache_dir_path = try build_directory.join(arena, &[_][]const u8{"zig-cache"}); 3423 break :l .{ 3424 .handle = try build_directory.handle.makeOpenPath("zig-cache", .{}), 3425 .path = cache_dir_path, 3426 }; 3427 }; 3428 defer local_cache_directory.handle.close(); 3429 3430 child_argv.items[argv_index_cache_dir] = local_cache_directory.path orelse cwd_path; 3431 3432 gimmeMoreOfThoseSweetSweetFileDescriptors(); 3433 3434 const cross_target: std.zig.CrossTarget = .{}; 3435 const target_info = try detectNativeTargetInfo(gpa, cross_target); 3436 3437 const exe_basename = try std.zig.binNameAlloc(arena, .{ 3438 .root_name = "build", 3439 .target = target_info.target, 3440 .output_mode = .Exe, 3441 }); 3442 const emit_bin: Compilation.EmitLoc = .{ 3443 .directory = null, // Use the local zig-cache. 3444 .basename = exe_basename, 3445 }; 3446 var thread_pool: ThreadPool = undefined; 3447 try thread_pool.init(gpa); 3448 defer thread_pool.deinit(); 3449 const comp = Compilation.create(gpa, .{ 3450 .zig_lib_directory = zig_lib_directory, 3451 .local_cache_directory = local_cache_directory, 3452 .global_cache_directory = global_cache_directory, 3453 .root_name = "build", 3454 .target = target_info.target, 3455 .is_native_os = cross_target.isNativeOs(), 3456 .is_native_abi = cross_target.isNativeAbi(), 3457 .dynamic_linker = target_info.dynamic_linker.get(), 3458 .output_mode = .Exe, 3459 .main_pkg = &main_pkg, 3460 .emit_bin = emit_bin, 3461 .emit_h = null, 3462 .optimize_mode = .Debug, 3463 .self_exe_path = self_exe_path, 3464 .thread_pool = &thread_pool, 3465 }) catch |err| { 3466 fatal("unable to create compilation: {s}", .{@errorName(err)}); 3467 }; 3468 defer comp.destroy(); 3469 3470 updateModule(gpa, comp, .none) catch |err| switch (err) { 3471 error.SemanticAnalyzeFail => process.exit(1), 3472 else => |e| return e, 3473 }; 3474 try comp.makeBinFileExecutable(); 3475 3476 child_argv.items[argv_index_exe] = try comp.bin_file.options.emit.?.directory.join( 3477 arena, 3478 &[_][]const u8{exe_basename}, 3479 ); 3480 3481 break :argv child_argv.items; 3482 }; 3483 const child = try std.ChildProcess.init(child_argv, gpa); 3484 defer child.deinit(); 3485 3486 child.stdin_behavior = .Inherit; 3487 child.stdout_behavior = .Inherit; 3488 child.stderr_behavior = .Inherit; 3489 3490 const term = try child.spawnAndWait(); 3491 switch (term) { 3492 .Exited => |code| { 3493 if (code == 0) return cleanExit(); 3494 3495 if (prominent_compile_errors) { 3496 fatal("the build command failed with exit code {d}", .{code}); 3497 } else { 3498 const cmd = try argvCmd(arena, child_argv); 3499 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); 3500 } 3501 }, 3502 else => { 3503 const cmd = try argvCmd(arena, child_argv); 3504 fatal("the following build command crashed:\n{s}", .{cmd}); 3505 }, 3506 } 3507 } 3508 3509 fn argvCmd(allocator: Allocator, argv: []const []const u8) ![]u8 { 3510 var cmd = std.ArrayList(u8).init(allocator); 3511 defer cmd.deinit(); 3512 for (argv[0 .. argv.len - 1]) |arg| { 3513 try cmd.appendSlice(arg); 3514 try cmd.append(' '); 3515 } 3516 try cmd.appendSlice(argv[argv.len - 1]); 3517 return cmd.toOwnedSlice(); 3518 } 3519 3520 fn readSourceFileToEndAlloc( 3521 allocator: mem.Allocator, 3522 input: *const fs.File, 3523 size_hint: ?usize, 3524 ) ![:0]u8 { 3525 const source_code = input.readToEndAllocOptions( 3526 allocator, 3527 max_src_size, 3528 size_hint, 3529 @alignOf(u16), 3530 0, 3531 ) catch |err| switch (err) { 3532 error.ConnectionResetByPeer => unreachable, 3533 error.ConnectionTimedOut => unreachable, 3534 error.NotOpenForReading => unreachable, 3535 else => |e| return e, 3536 }; 3537 errdefer allocator.free(source_code); 3538 3539 // Detect unsupported file types with their Byte Order Mark 3540 const unsupported_boms = [_][]const u8{ 3541 "\xff\xfe\x00\x00", // UTF-32 little endian 3542 "\xfe\xff\x00\x00", // UTF-32 big endian 3543 "\xfe\xff", // UTF-16 big endian 3544 }; 3545 for (unsupported_boms) |bom| { 3546 if (mem.startsWith(u8, source_code, bom)) { 3547 return error.UnsupportedEncoding; 3548 } 3549 } 3550 3551 // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8 3552 if (mem.startsWith(u8, source_code, "\xff\xfe")) { 3553 const source_code_utf16_le = mem.bytesAsSlice(u16, source_code); 3554 const source_code_utf8 = std.unicode.utf16leToUtf8AllocZ(allocator, source_code_utf16_le) catch |err| switch (err) { 3555 error.DanglingSurrogateHalf => error.UnsupportedEncoding, 3556 error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding, 3557 error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding, 3558 else => |e| return e, 3559 }; 3560 3561 allocator.free(source_code); 3562 return source_code_utf8; 3563 } 3564 3565 return source_code; 3566 } 3567 3568 pub const usage_fmt = 3569 \\Usage: zig fmt [file]... 3570 \\ 3571 \\ Formats the input files and modifies them in-place. 3572 \\ Arguments can be files or directories, which are searched 3573 \\ recursively. 3574 \\ 3575 \\Options: 3576 \\ -h, --help Print this help and exit 3577 \\ --color [auto|off|on] Enable or disable colored error messages 3578 \\ --stdin Format code from stdin; output to stdout 3579 \\ --check List non-conforming files and exit with an error 3580 \\ if the list is non-empty 3581 \\ --ast-check Run zig ast-check on every file 3582 \\ 3583 \\ 3584 ; 3585 3586 const Fmt = struct { 3587 seen: SeenMap, 3588 any_error: bool, 3589 check_ast: bool, 3590 color: Color, 3591 gpa: Allocator, 3592 arena: Allocator, 3593 out_buffer: std.ArrayList(u8), 3594 3595 const SeenMap = std.AutoHashMap(fs.File.INode, void); 3596 }; 3597 3598 pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { 3599 var color: Color = .auto; 3600 var stdin_flag: bool = false; 3601 var check_flag: bool = false; 3602 var check_ast_flag: bool = false; 3603 var input_files = ArrayList([]const u8).init(gpa); 3604 defer input_files.deinit(); 3605 3606 { 3607 var i: usize = 0; 3608 while (i < args.len) : (i += 1) { 3609 const arg = args[i]; 3610 if (mem.startsWith(u8, arg, "-")) { 3611 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { 3612 const stdout = io.getStdOut().writer(); 3613 try stdout.writeAll(usage_fmt); 3614 return cleanExit(); 3615 } else if (mem.eql(u8, arg, "--color")) { 3616 if (i + 1 >= args.len) { 3617 fatal("expected [auto|on|off] after --color", .{}); 3618 } 3619 i += 1; 3620 const next_arg = args[i]; 3621 color = std.meta.stringToEnum(Color, next_arg) orelse { 3622 fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); 3623 }; 3624 } else if (mem.eql(u8, arg, "--stdin")) { 3625 stdin_flag = true; 3626 } else if (mem.eql(u8, arg, "--check")) { 3627 check_flag = true; 3628 } else if (mem.eql(u8, arg, "--ast-check")) { 3629 check_ast_flag = true; 3630 } else { 3631 fatal("unrecognized parameter: '{s}'", .{arg}); 3632 } 3633 } else { 3634 try input_files.append(arg); 3635 } 3636 } 3637 } 3638 3639 if (stdin_flag) { 3640 if (input_files.items.len != 0) { 3641 fatal("cannot use --stdin with positional arguments", .{}); 3642 } 3643 3644 const stdin = io.getStdIn(); 3645 const source_code = readSourceFileToEndAlloc(gpa, &stdin, null) catch |err| { 3646 fatal("unable to read stdin: {s}", .{err}); 3647 }; 3648 defer gpa.free(source_code); 3649 3650 var tree = std.zig.parse(gpa, source_code) catch |err| { 3651 fatal("error parsing stdin: {s}", .{err}); 3652 }; 3653 defer tree.deinit(gpa); 3654 3655 for (tree.errors) |parse_error| { 3656 try printErrMsgToStdErr(gpa, arena, parse_error, tree, "<stdin>", color); 3657 } 3658 var has_ast_error = false; 3659 if (check_ast_flag) { 3660 const Module = @import("Module.zig"); 3661 const AstGen = @import("AstGen.zig"); 3662 3663 var file: Module.File = .{ 3664 .status = .never_loaded, 3665 .source_loaded = true, 3666 .zir_loaded = false, 3667 .sub_file_path = "<stdin>", 3668 .source = source_code, 3669 .stat_size = undefined, 3670 .stat_inode = undefined, 3671 .stat_mtime = undefined, 3672 .tree = tree, 3673 .tree_loaded = true, 3674 .zir = undefined, 3675 .pkg = undefined, 3676 .root_decl = null, 3677 }; 3678 3679 file.pkg = try Package.create(gpa, null, file.sub_file_path); 3680 defer file.pkg.destroy(gpa); 3681 3682 file.zir = try AstGen.generate(gpa, file.tree); 3683 file.zir_loaded = true; 3684 defer file.zir.deinit(gpa); 3685 3686 if (file.zir.hasCompileErrors()) { 3687 var arena_instance = std.heap.ArenaAllocator.init(gpa); 3688 defer arena_instance.deinit(); 3689 var errors = std.ArrayList(Compilation.AllErrors.Message).init(gpa); 3690 defer errors.deinit(); 3691 3692 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file); 3693 const ttyconf: std.debug.TTY.Config = switch (color) { 3694 .auto => std.debug.detectTTYConfig(), 3695 .on => .escape_codes, 3696 .off => .no_color, 3697 }; 3698 for (errors.items) |full_err_msg| { 3699 full_err_msg.renderToStdErr(ttyconf); 3700 } 3701 has_ast_error = true; 3702 } 3703 } 3704 if (tree.errors.len != 0 or has_ast_error) { 3705 process.exit(1); 3706 } 3707 const formatted = try tree.render(gpa); 3708 defer gpa.free(formatted); 3709 3710 if (check_flag) { 3711 const code: u8 = @boolToInt(mem.eql(u8, formatted, source_code)); 3712 process.exit(code); 3713 } 3714 3715 return io.getStdOut().writeAll(formatted); 3716 } 3717 3718 if (input_files.items.len == 0) { 3719 fatal("expected at least one source file argument", .{}); 3720 } 3721 3722 var fmt = Fmt{ 3723 .gpa = gpa, 3724 .arena = arena, 3725 .seen = Fmt.SeenMap.init(gpa), 3726 .any_error = false, 3727 .check_ast = check_ast_flag, 3728 .color = color, 3729 .out_buffer = std.ArrayList(u8).init(gpa), 3730 }; 3731 defer fmt.seen.deinit(); 3732 defer fmt.out_buffer.deinit(); 3733 3734 for (input_files.items) |file_path| { 3735 try fmtPath(&fmt, file_path, check_flag, fs.cwd(), file_path); 3736 } 3737 if (fmt.any_error) { 3738 process.exit(1); 3739 } 3740 } 3741 3742 const FmtError = error{ 3743 SystemResources, 3744 OperationAborted, 3745 IoPending, 3746 BrokenPipe, 3747 Unexpected, 3748 WouldBlock, 3749 FileClosed, 3750 DestinationAddressRequired, 3751 DiskQuota, 3752 FileTooBig, 3753 InputOutput, 3754 NoSpaceLeft, 3755 AccessDenied, 3756 OutOfMemory, 3757 RenameAcrossMountPoints, 3758 ReadOnlyFileSystem, 3759 LinkQuotaExceeded, 3760 FileBusy, 3761 EndOfStream, 3762 Unseekable, 3763 NotOpenForWriting, 3764 UnsupportedEncoding, 3765 ConnectionResetByPeer, 3766 } || fs.File.OpenError; 3767 3768 fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { 3769 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) { 3770 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path), 3771 else => { 3772 warn("unable to format '{s}': {s}", .{ file_path, @errorName(err) }); 3773 fmt.any_error = true; 3774 return; 3775 }, 3776 }; 3777 } 3778 3779 fn fmtPathDir( 3780 fmt: *Fmt, 3781 file_path: []const u8, 3782 check_mode: bool, 3783 parent_dir: fs.Dir, 3784 parent_sub_path: []const u8, 3785 ) FmtError!void { 3786 var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true }); 3787 defer dir.close(); 3788 3789 const stat = try dir.stat(); 3790 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; 3791 3792 var dir_it = dir.iterate(); 3793 while (try dir_it.next()) |entry| { 3794 const is_dir = entry.kind == .Directory; 3795 3796 if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue; 3797 3798 if (is_dir or mem.endsWith(u8, entry.name, ".zig")) { 3799 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); 3800 defer fmt.gpa.free(full_path); 3801 3802 if (is_dir) { 3803 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name); 3804 } else { 3805 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| { 3806 warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) }); 3807 fmt.any_error = true; 3808 return; 3809 }; 3810 } 3811 } 3812 } 3813 } 3814 3815 fn fmtPathFile( 3816 fmt: *Fmt, 3817 file_path: []const u8, 3818 check_mode: bool, 3819 dir: fs.Dir, 3820 sub_path: []const u8, 3821 ) FmtError!void { 3822 const source_file = try dir.openFile(sub_path, .{}); 3823 var file_closed = false; 3824 errdefer if (!file_closed) source_file.close(); 3825 3826 const stat = try source_file.stat(); 3827 3828 if (stat.kind == .Directory) 3829 return error.IsDir; 3830 3831 const source_code = try readSourceFileToEndAlloc( 3832 fmt.gpa, 3833 &source_file, 3834 std.math.cast(usize, stat.size) catch return error.FileTooBig, 3835 ); 3836 defer fmt.gpa.free(source_code); 3837 3838 source_file.close(); 3839 file_closed = true; 3840 3841 // Add to set after no longer possible to get error.IsDir. 3842 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; 3843 3844 var tree = try std.zig.parse(fmt.gpa, source_code); 3845 defer tree.deinit(fmt.gpa); 3846 3847 for (tree.errors) |parse_error| { 3848 try printErrMsgToStdErr(fmt.gpa, fmt.arena, parse_error, tree, file_path, fmt.color); 3849 } 3850 if (tree.errors.len != 0) { 3851 fmt.any_error = true; 3852 return; 3853 } 3854 3855 if (fmt.check_ast) { 3856 const Module = @import("Module.zig"); 3857 const AstGen = @import("AstGen.zig"); 3858 3859 var file: Module.File = .{ 3860 .status = .never_loaded, 3861 .source_loaded = true, 3862 .zir_loaded = false, 3863 .sub_file_path = file_path, 3864 .source = source_code, 3865 .stat_size = stat.size, 3866 .stat_inode = stat.inode, 3867 .stat_mtime = stat.mtime, 3868 .tree = tree, 3869 .tree_loaded = true, 3870 .zir = undefined, 3871 .pkg = undefined, 3872 .root_decl = null, 3873 }; 3874 3875 file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path); 3876 defer file.pkg.destroy(fmt.gpa); 3877 3878 if (stat.size > max_src_size) 3879 return error.FileTooBig; 3880 3881 file.zir = try AstGen.generate(fmt.gpa, file.tree); 3882 file.zir_loaded = true; 3883 defer file.zir.deinit(fmt.gpa); 3884 3885 if (file.zir.hasCompileErrors()) { 3886 var arena_instance = std.heap.ArenaAllocator.init(fmt.gpa); 3887 defer arena_instance.deinit(); 3888 var errors = std.ArrayList(Compilation.AllErrors.Message).init(fmt.gpa); 3889 defer errors.deinit(); 3890 3891 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file); 3892 const ttyconf: std.debug.TTY.Config = switch (fmt.color) { 3893 .auto => std.debug.detectTTYConfig(), 3894 .on => .escape_codes, 3895 .off => .no_color, 3896 }; 3897 for (errors.items) |full_err_msg| { 3898 full_err_msg.renderToStdErr(ttyconf); 3899 } 3900 fmt.any_error = true; 3901 } 3902 } 3903 3904 // As a heuristic, we make enough capacity for the same as the input source. 3905 fmt.out_buffer.shrinkRetainingCapacity(0); 3906 try fmt.out_buffer.ensureTotalCapacity(source_code.len); 3907 3908 try tree.renderToArrayList(&fmt.out_buffer); 3909 if (mem.eql(u8, fmt.out_buffer.items, source_code)) 3910 return; 3911 3912 if (check_mode) { 3913 const stdout = io.getStdOut().writer(); 3914 try stdout.print("{s}\n", .{file_path}); 3915 fmt.any_error = true; 3916 } else { 3917 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode }); 3918 defer af.deinit(); 3919 3920 try af.file.writeAll(fmt.out_buffer.items); 3921 try af.finish(); 3922 const stdout = io.getStdOut().writer(); 3923 try stdout.print("{s}\n", .{file_path}); 3924 } 3925 } 3926 3927 fn printErrMsgToStdErr( 3928 gpa: mem.Allocator, 3929 arena: mem.Allocator, 3930 parse_error: Ast.Error, 3931 tree: Ast, 3932 path: []const u8, 3933 color: Color, 3934 ) !void { 3935 const lok_token = parse_error.token; 3936 const token_tags = tree.tokens.items(.tag); 3937 const start_loc = tree.tokenLocation(0, lok_token); 3938 const source_line = tree.source[start_loc.line_start..start_loc.line_end]; 3939 3940 var text_buf = std.ArrayList(u8).init(gpa); 3941 defer text_buf.deinit(); 3942 const writer = text_buf.writer(); 3943 try tree.renderError(parse_error, writer); 3944 const text = text_buf.items; 3945 3946 var notes_buffer: [1]Compilation.AllErrors.Message = undefined; 3947 var notes_len: usize = 0; 3948 3949 if (token_tags[parse_error.token] == .invalid) { 3950 const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token).len); 3951 const byte_offset = @intCast(u32, start_loc.line_start) + bad_off; 3952 notes_buffer[notes_len] = .{ 3953 .src = .{ 3954 .src_path = path, 3955 .msg = try std.fmt.allocPrint(arena, "invalid byte: '{'}'", .{ 3956 std.zig.fmtEscapes(tree.source[byte_offset..][0..1]), 3957 }), 3958 .byte_offset = byte_offset, 3959 .line = @intCast(u32, start_loc.line), 3960 .column = @intCast(u32, start_loc.column) + bad_off, 3961 .source_line = source_line, 3962 }, 3963 }; 3964 notes_len += 1; 3965 } 3966 3967 const message: Compilation.AllErrors.Message = .{ 3968 .src = .{ 3969 .src_path = path, 3970 .msg = text, 3971 .byte_offset = @intCast(u32, start_loc.line_start), 3972 .line = @intCast(u32, start_loc.line), 3973 .column = @intCast(u32, start_loc.column), 3974 .source_line = source_line, 3975 .notes = notes_buffer[0..notes_len], 3976 }, 3977 }; 3978 3979 const ttyconf: std.debug.TTY.Config = switch (color) { 3980 .auto => std.debug.detectTTYConfig(), 3981 .on => .escape_codes, 3982 .off => .no_color, 3983 }; 3984 3985 message.renderToStdErr(ttyconf); 3986 } 3987 3988 pub const info_zen = 3989 \\ 3990 \\ * Communicate intent precisely. 3991 \\ * Edge cases matter. 3992 \\ * Favor reading code over writing code. 3993 \\ * Only one obvious way to do things. 3994 \\ * Runtime crashes are better than bugs. 3995 \\ * Compile errors are better than runtime crashes. 3996 \\ * Incremental improvements. 3997 \\ * Avoid local maximums. 3998 \\ * Reduce the amount one must remember. 3999 \\ * Focus on code rather than style. 4000 \\ * Resource allocation may fail; resource deallocation must succeed. 4001 \\ * Memory is a resource. 4002 \\ * Together we serve the users. 4003 \\ 4004 \\ 4005 ; 4006 4007 extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; 4008 extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; 4009 4010 /// TODO https://github.com/ziglang/zig/issues/3257 4011 fn punt_to_clang(arena: Allocator, args: []const []const u8) error{OutOfMemory} { 4012 if (!build_options.have_llvm) 4013 fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{}); 4014 // Convert the args to the format Clang expects. 4015 const argv = try arena.alloc(?[*:0]u8, args.len + 1); 4016 for (args) |arg, i| { 4017 argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. 4018 } 4019 argv[args.len] = null; 4020 const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr); 4021 process.exit(@bitCast(u8, @truncate(i8, exit_code))); 4022 } 4023 4024 /// TODO https://github.com/ziglang/zig/issues/3257 4025 fn punt_to_llvm_ar(arena: Allocator, args: []const []const u8) error{OutOfMemory} { 4026 if (!build_options.have_llvm) 4027 fatal("`zig ar`, `zig dlltool`, `zig ranlib', and `zig lib` unavailable: compiler built without LLVM extensions", .{}); 4028 4029 // Convert the args to the format llvm-ar expects. 4030 // We subtract 1 to shave off the zig binary from args[0]. 4031 const argv = try arena.allocSentinel(?[*:0]u8, args.len - 1, null); 4032 for (args[1..]) |arg, i| { 4033 // TODO If there was an argsAllocZ we could avoid this allocation. 4034 argv[i] = try arena.dupeZ(u8, arg); 4035 } 4036 const argc = @intCast(c_int, argv.len); 4037 const exit_code = ZigLlvmAr_main(argc, argv.ptr); 4038 process.exit(@bitCast(u8, @truncate(i8, exit_code))); 4039 } 4040 4041 /// The first argument determines which backend is invoked. The options are: 4042 /// * `ld.lld` - ELF 4043 /// * `lld-link` - COFF 4044 /// * `wasm-ld` - WebAssembly 4045 /// TODO https://github.com/ziglang/zig/issues/3257 4046 pub fn punt_to_lld(arena: Allocator, args: []const []const u8) error{OutOfMemory} { 4047 if (!build_options.have_llvm) 4048 fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]}); 4049 // Convert the args to the format LLD expects. 4050 // We subtract 1 to shave off the zig binary from args[0]. 4051 const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null); 4052 for (args[1..]) |arg, i| { 4053 argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. 4054 } 4055 const exit_code = rc: { 4056 const llvm = @import("codegen/llvm/bindings.zig"); 4057 const argc = @intCast(c_int, argv.len); 4058 if (mem.eql(u8, args[1], "ld.lld")) { 4059 break :rc llvm.LinkELF(argc, argv.ptr, true); 4060 } else if (mem.eql(u8, args[1], "lld-link")) { 4061 break :rc llvm.LinkCOFF(argc, argv.ptr, true); 4062 } else if (mem.eql(u8, args[1], "wasm-ld")) { 4063 break :rc llvm.LinkWasm(argc, argv.ptr, true); 4064 } else { 4065 unreachable; 4066 } 4067 }; 4068 process.exit(@bitCast(u8, @truncate(i8, exit_code))); 4069 } 4070 4071 const clang_args = @import("clang_options.zig").list; 4072 4073 pub const ClangArgIterator = struct { 4074 has_next: bool, 4075 zig_equivalent: ZigEquivalent, 4076 only_arg: []const u8, 4077 second_arg: []const u8, 4078 other_args: []const []const u8, 4079 argv: []const []const u8, 4080 next_index: usize, 4081 root_args: ?*Args, 4082 allocator: Allocator, 4083 4084 pub const ZigEquivalent = enum { 4085 target, 4086 o, 4087 c, 4088 m, 4089 other, 4090 positional, 4091 l, 4092 ignore, 4093 driver_punt, 4094 pic, 4095 no_pic, 4096 pie, 4097 no_pie, 4098 lto, 4099 no_lto, 4100 unwind_tables, 4101 no_unwind_tables, 4102 nostdlib, 4103 nostdlib_cpp, 4104 shared, 4105 rdynamic, 4106 wl, 4107 preprocess_only, 4108 asm_only, 4109 optimize, 4110 debug, 4111 sanitize, 4112 linker_script, 4113 dry_run, 4114 verbose, 4115 for_linker, 4116 linker_input_z, 4117 lib_dir, 4118 mcpu, 4119 dep_file, 4120 dep_file_mm, 4121 framework_dir, 4122 framework, 4123 nostdlibinc, 4124 red_zone, 4125 no_red_zone, 4126 omit_frame_pointer, 4127 no_omit_frame_pointer, 4128 function_sections, 4129 no_function_sections, 4130 color_diagnostics, 4131 no_color_diagnostics, 4132 strip, 4133 exec_model, 4134 emit_llvm, 4135 }; 4136 4137 const Args = struct { 4138 next_index: usize, 4139 argv: []const []const u8, 4140 }; 4141 4142 fn init(allocator: Allocator, argv: []const []const u8) ClangArgIterator { 4143 return .{ 4144 .next_index = 2, // `zig cc foo` this points to `foo` 4145 .has_next = argv.len > 2, 4146 .zig_equivalent = undefined, 4147 .only_arg = undefined, 4148 .second_arg = undefined, 4149 .other_args = undefined, 4150 .argv = argv, 4151 .root_args = null, 4152 .allocator = allocator, 4153 }; 4154 } 4155 4156 fn next(self: *ClangArgIterator) !void { 4157 assert(self.has_next); 4158 assert(self.next_index < self.argv.len); 4159 // In this state we know that the parameter we are looking at is a root parameter 4160 // rather than an argument to a parameter. 4161 // We adjust the len below when necessary. 4162 self.other_args = (self.argv.ptr + self.next_index)[0..1]; 4163 var arg = mem.span(self.argv[self.next_index]); 4164 self.incrementArgIndex(); 4165 4166 if (mem.startsWith(u8, arg, "@")) { 4167 if (self.root_args != null) return error.NestedResponseFile; 4168 4169 // This is a "compiler response file". We must parse the file and treat its 4170 // contents as command line parameters. 4171 const allocator = self.allocator; 4172 const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit 4173 const resp_file_path = arg[1..]; 4174 const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| { 4175 fatal("unable to read response file '{s}': {s}", .{ resp_file_path, @errorName(err) }); 4176 }; 4177 defer allocator.free(resp_contents); 4178 // TODO is there a specification for this file format? Let's find it and make this parsing more robust 4179 // at the very least I'm guessing this needs to handle quotes and `#` comments. 4180 var it = mem.tokenize(u8, resp_contents, " \t\r\n"); 4181 var resp_arg_list = std.ArrayList([]const u8).init(allocator); 4182 defer resp_arg_list.deinit(); 4183 { 4184 errdefer { 4185 for (resp_arg_list.items) |item| { 4186 allocator.free(mem.span(item)); 4187 } 4188 } 4189 while (it.next()) |token| { 4190 const dupe_token = try allocator.dupeZ(u8, token); 4191 errdefer allocator.free(dupe_token); 4192 try resp_arg_list.append(dupe_token); 4193 } 4194 const args = try allocator.create(Args); 4195 errdefer allocator.destroy(args); 4196 args.* = .{ 4197 .next_index = self.next_index, 4198 .argv = self.argv, 4199 }; 4200 self.root_args = args; 4201 } 4202 const resp_arg_slice = resp_arg_list.toOwnedSlice(); 4203 self.next_index = 0; 4204 self.argv = resp_arg_slice; 4205 4206 if (resp_arg_slice.len == 0) { 4207 self.resolveRespFileArgs(); 4208 return; 4209 } 4210 4211 self.has_next = true; 4212 self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary. 4213 arg = mem.span(self.argv[self.next_index]); 4214 self.incrementArgIndex(); 4215 } 4216 if (mem.eql(u8, arg, "-") or !mem.startsWith(u8, arg, "-")) { 4217 self.zig_equivalent = .positional; 4218 self.only_arg = arg; 4219 return; 4220 } 4221 4222 find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) { 4223 .flag => { 4224 const prefix_len = clang_arg.matchEql(arg); 4225 if (prefix_len > 0) { 4226 self.zig_equivalent = clang_arg.zig_equivalent; 4227 self.only_arg = arg[prefix_len..]; 4228 4229 break :find_clang_arg; 4230 } 4231 }, 4232 .joined, .comma_joined => { 4233 // joined example: --target=foo 4234 // comma_joined example: -Wl,-soname,libsoundio.so.2 4235 const prefix_len = clang_arg.matchStartsWith(arg); 4236 if (prefix_len != 0) { 4237 self.zig_equivalent = clang_arg.zig_equivalent; 4238 self.only_arg = arg[prefix_len..]; // This will skip over the "--target=" part. 4239 4240 break :find_clang_arg; 4241 } 4242 }, 4243 .joined_or_separate => { 4244 // Examples: `-lfoo`, `-l foo` 4245 const prefix_len = clang_arg.matchStartsWith(arg); 4246 if (prefix_len == arg.len) { 4247 if (self.next_index >= self.argv.len) { 4248 fatal("Expected parameter after '{s}'", .{arg}); 4249 } 4250 self.only_arg = self.argv[self.next_index]; 4251 self.incrementArgIndex(); 4252 self.other_args.len += 1; 4253 self.zig_equivalent = clang_arg.zig_equivalent; 4254 4255 break :find_clang_arg; 4256 } else if (prefix_len != 0) { 4257 self.zig_equivalent = clang_arg.zig_equivalent; 4258 self.only_arg = arg[prefix_len..]; 4259 4260 break :find_clang_arg; 4261 } 4262 }, 4263 .joined_and_separate => { 4264 // Example: `-Xopenmp-target=riscv64-linux-unknown foo` 4265 const prefix_len = clang_arg.matchStartsWith(arg); 4266 if (prefix_len != 0) { 4267 self.only_arg = arg[prefix_len..]; 4268 if (self.next_index >= self.argv.len) { 4269 fatal("Expected parameter after '{s}'", .{arg}); 4270 } 4271 self.second_arg = self.argv[self.next_index]; 4272 self.incrementArgIndex(); 4273 self.other_args.len += 1; 4274 self.zig_equivalent = clang_arg.zig_equivalent; 4275 break :find_clang_arg; 4276 } 4277 }, 4278 .separate => if (clang_arg.matchEql(arg) > 0) { 4279 if (self.next_index >= self.argv.len) { 4280 fatal("Expected parameter after '{s}'", .{arg}); 4281 } 4282 self.only_arg = self.argv[self.next_index]; 4283 self.incrementArgIndex(); 4284 self.other_args.len += 1; 4285 self.zig_equivalent = clang_arg.zig_equivalent; 4286 break :find_clang_arg; 4287 }, 4288 .remaining_args_joined => { 4289 const prefix_len = clang_arg.matchStartsWith(arg); 4290 if (prefix_len != 0) { 4291 @panic("TODO"); 4292 } 4293 }, 4294 .multi_arg => |num_args| if (clang_arg.matchEql(arg) > 0) { 4295 // Example `-sectcreate <arg1> <arg2> <arg3>`. 4296 var i: usize = 0; 4297 while (i < num_args) : (i += 1) { 4298 self.incrementArgIndex(); 4299 self.other_args.len += 1; 4300 } 4301 self.zig_equivalent = clang_arg.zig_equivalent; 4302 break :find_clang_arg; 4303 }, 4304 } else { 4305 fatal("Unknown Clang option: '{s}'", .{arg}); 4306 } 4307 } 4308 4309 fn incrementArgIndex(self: *ClangArgIterator) void { 4310 self.next_index += 1; 4311 self.resolveRespFileArgs(); 4312 } 4313 4314 fn resolveRespFileArgs(self: *ClangArgIterator) void { 4315 const allocator = self.allocator; 4316 if (self.next_index >= self.argv.len) { 4317 if (self.root_args) |root_args| { 4318 self.next_index = root_args.next_index; 4319 self.argv = root_args.argv; 4320 4321 allocator.destroy(root_args); 4322 self.root_args = null; 4323 } 4324 if (self.next_index >= self.argv.len) { 4325 self.has_next = false; 4326 } 4327 } 4328 } 4329 }; 4330 4331 fn parseCodeModel(arg: []const u8) std.builtin.CodeModel { 4332 return std.meta.stringToEnum(std.builtin.CodeModel, arg) orelse 4333 fatal("unsupported machine code model: '{s}'", .{arg}); 4334 } 4335 4336 /// Raise the open file descriptor limit. Ask and ye shall receive. 4337 /// For one example of why this is handy, consider the case of building musl libc. 4338 /// We keep a lock open for each of the object files in the form of a file descriptor 4339 /// until they are finally put into an archive file. This is to allow a zig-cache 4340 /// garbage collector to run concurrently to zig processes, and to allow multiple 4341 /// zig processes to run concurrently with each other, without clobbering each other. 4342 fn gimmeMoreOfThoseSweetSweetFileDescriptors() void { 4343 if (!@hasDecl(std.os.system, "rlimit")) return; 4344 const posix = std.os; 4345 4346 var lim = posix.getrlimit(.NOFILE) catch return; // Oh well; we tried. 4347 if (comptime builtin.target.isDarwin()) { 4348 // On Darwin, `NOFILE` is bounded by a hardcoded value `OPEN_MAX`. 4349 // According to the man pages for setrlimit(): 4350 // setrlimit() now returns with errno set to EINVAL in places that historically succeeded. 4351 // It no longer accepts "rlim_cur = RLIM.INFINITY" for RLIM.NOFILE. 4352 // Use "rlim_cur = min(OPEN_MAX, rlim_max)". 4353 lim.max = std.math.min(std.os.darwin.OPEN_MAX, lim.max); 4354 } 4355 if (lim.cur == lim.max) return; 4356 4357 // Do a binary search for the limit. 4358 var min: posix.rlim_t = lim.cur; 4359 var max: posix.rlim_t = 1 << 20; 4360 // But if there's a defined upper bound, don't search, just set it. 4361 if (lim.max != posix.RLIM.INFINITY) { 4362 min = lim.max; 4363 max = lim.max; 4364 } 4365 4366 while (true) { 4367 lim.cur = min + @divTrunc(max - min, 2); // on freebsd rlim_t is signed 4368 if (posix.setrlimit(.NOFILE, lim)) |_| { 4369 min = lim.cur; 4370 } else |_| { 4371 max = lim.cur; 4372 } 4373 if (min + 1 >= max) break; 4374 } 4375 } 4376 4377 test "fds" { 4378 gimmeMoreOfThoseSweetSweetFileDescriptors(); 4379 } 4380 4381 fn detectNativeTargetInfo(gpa: Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo { 4382 return std.zig.system.NativeTargetInfo.detect(gpa, cross_target); 4383 } 4384 4385 /// Indicate that we are now terminating with a successful exit code. 4386 /// In debug builds, this is a no-op, so that the calling code's 4387 /// cleanup mechanisms are tested and so that external tools that 4388 /// check for resource leaks can be accurate. In release builds, this 4389 /// calls exit(0), and does not return. 4390 pub fn cleanExit() void { 4391 if (builtin.mode == .Debug) { 4392 return; 4393 } else { 4394 process.exit(0); 4395 } 4396 } 4397 4398 const usage_ast_check = 4399 \\Usage: zig ast-check [file] 4400 \\ 4401 \\ Given a .zig source file, reports any compile errors that can be 4402 \\ ascertained on the basis of the source code alone, without target 4403 \\ information or type checking. 4404 \\ 4405 \\ If [file] is omitted, stdin is used. 4406 \\ 4407 \\Options: 4408 \\ -h, --help Print this help and exit 4409 \\ --color [auto|off|on] Enable or disable colored error messages 4410 \\ -t (debug option) Output ZIR in text form to stdout 4411 \\ 4412 \\ 4413 ; 4414 4415 pub fn cmdAstCheck( 4416 gpa: Allocator, 4417 arena: Allocator, 4418 args: []const []const u8, 4419 ) !void { 4420 const Module = @import("Module.zig"); 4421 const AstGen = @import("AstGen.zig"); 4422 const Zir = @import("Zir.zig"); 4423 4424 var color: Color = .auto; 4425 var want_output_text = false; 4426 var zig_source_file: ?[]const u8 = null; 4427 4428 var i: usize = 0; 4429 while (i < args.len) : (i += 1) { 4430 const arg = args[i]; 4431 if (mem.startsWith(u8, arg, "-")) { 4432 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { 4433 try io.getStdOut().writeAll(usage_ast_check); 4434 return cleanExit(); 4435 } else if (mem.eql(u8, arg, "-t")) { 4436 want_output_text = true; 4437 } else if (mem.eql(u8, arg, "--color")) { 4438 if (i + 1 >= args.len) { 4439 fatal("expected [auto|on|off] after --color", .{}); 4440 } 4441 i += 1; 4442 const next_arg = args[i]; 4443 color = std.meta.stringToEnum(Color, next_arg) orelse { 4444 fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); 4445 }; 4446 } else { 4447 fatal("unrecognized parameter: '{s}'", .{arg}); 4448 } 4449 } else if (zig_source_file == null) { 4450 zig_source_file = arg; 4451 } else { 4452 fatal("extra positional parameter: '{s}'", .{arg}); 4453 } 4454 } 4455 4456 var file: Module.File = .{ 4457 .status = .never_loaded, 4458 .source_loaded = false, 4459 .tree_loaded = false, 4460 .zir_loaded = false, 4461 .sub_file_path = undefined, 4462 .source = undefined, 4463 .stat_size = undefined, 4464 .stat_inode = undefined, 4465 .stat_mtime = undefined, 4466 .tree = undefined, 4467 .zir = undefined, 4468 .pkg = undefined, 4469 .root_decl = null, 4470 }; 4471 if (zig_source_file) |file_name| { 4472 var f = fs.cwd().openFile(file_name, .{}) catch |err| { 4473 fatal("unable to open file for ast-check '{s}': {s}", .{ file_name, @errorName(err) }); 4474 }; 4475 defer f.close(); 4476 4477 const stat = try f.stat(); 4478 4479 if (stat.size > max_src_size) 4480 return error.FileTooBig; 4481 4482 const source = try arena.allocSentinel(u8, @intCast(usize, stat.size), 0); 4483 const amt = try f.readAll(source); 4484 if (amt != stat.size) 4485 return error.UnexpectedEndOfFile; 4486 4487 file.sub_file_path = file_name; 4488 file.source = source; 4489 file.source_loaded = true; 4490 file.stat_size = stat.size; 4491 file.stat_inode = stat.inode; 4492 file.stat_mtime = stat.mtime; 4493 } else { 4494 const stdin = io.getStdIn(); 4495 const source = readSourceFileToEndAlloc(arena, &stdin, null) catch |err| { 4496 fatal("unable to read stdin: {s}", .{err}); 4497 }; 4498 file.sub_file_path = "<stdin>"; 4499 file.source = source; 4500 file.source_loaded = true; 4501 file.stat_size = source.len; 4502 } 4503 4504 file.pkg = try Package.create(gpa, null, file.sub_file_path); 4505 defer file.pkg.destroy(gpa); 4506 4507 file.tree = try std.zig.parse(gpa, file.source); 4508 file.tree_loaded = true; 4509 defer file.tree.deinit(gpa); 4510 4511 for (file.tree.errors) |parse_error| { 4512 try printErrMsgToStdErr(gpa, arena, parse_error, file.tree, file.sub_file_path, color); 4513 } 4514 if (file.tree.errors.len != 0) { 4515 process.exit(1); 4516 } 4517 4518 file.zir = try AstGen.generate(gpa, file.tree); 4519 file.zir_loaded = true; 4520 defer file.zir.deinit(gpa); 4521 4522 if (file.zir.hasCompileErrors()) { 4523 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); 4524 try Compilation.AllErrors.addZir(arena, &errors, &file); 4525 const ttyconf: std.debug.TTY.Config = switch (color) { 4526 .auto => std.debug.detectTTYConfig(), 4527 .on => .escape_codes, 4528 .off => .no_color, 4529 }; 4530 for (errors.items) |full_err_msg| { 4531 full_err_msg.renderToStdErr(ttyconf); 4532 } 4533 process.exit(1); 4534 } 4535 4536 if (!want_output_text) { 4537 return cleanExit(); 4538 } 4539 if (!debug_extensions_enabled) { 4540 fatal("-t option only available in debug builds of zig", .{}); 4541 } 4542 4543 { 4544 const token_bytes = @sizeOf(Ast.TokenList) + 4545 file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset)); 4546 const tree_bytes = @sizeOf(Ast) + file.tree.nodes.len * 4547 (@sizeOf(Ast.Node.Tag) + 4548 @sizeOf(Ast.Node.Data) + 4549 @sizeOf(Ast.TokenIndex)); 4550 const instruction_bytes = file.zir.instructions.len * 4551 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include 4552 // the debug safety tag but we want to measure release size. 4553 (@sizeOf(Zir.Inst.Tag) + 8); 4554 const extra_bytes = file.zir.extra.len * @sizeOf(u32); 4555 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes + 4556 file.zir.string_bytes.len * @sizeOf(u8); 4557 const stdout = io.getStdOut(); 4558 const fmtIntSizeBin = std.fmt.fmtIntSizeBin; 4559 // zig fmt: off 4560 try stdout.writer().print( 4561 \\# Source bytes: {} 4562 \\# Tokens: {} ({}) 4563 \\# AST Nodes: {} ({}) 4564 \\# Total ZIR bytes: {} 4565 \\# Instructions: {d} ({}) 4566 \\# String Table Bytes: {} 4567 \\# Extra Data Items: {d} ({}) 4568 \\ 4569 , .{ 4570 fmtIntSizeBin(file.source.len), 4571 file.tree.tokens.len, fmtIntSizeBin(token_bytes), 4572 file.tree.nodes.len, fmtIntSizeBin(tree_bytes), 4573 fmtIntSizeBin(total_bytes), 4574 file.zir.instructions.len, fmtIntSizeBin(instruction_bytes), 4575 fmtIntSizeBin(file.zir.string_bytes.len), 4576 file.zir.extra.len, fmtIntSizeBin(extra_bytes), 4577 }); 4578 // zig fmt: on 4579 } 4580 4581 return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut()); 4582 } 4583 4584 /// This is only enabled for debug builds. 4585 pub fn cmdChangelist( 4586 gpa: Allocator, 4587 arena: Allocator, 4588 args: []const []const u8, 4589 ) !void { 4590 const Module = @import("Module.zig"); 4591 const AstGen = @import("AstGen.zig"); 4592 const Zir = @import("Zir.zig"); 4593 4594 const old_source_file = args[0]; 4595 const new_source_file = args[1]; 4596 4597 var f = fs.cwd().openFile(old_source_file, .{}) catch |err| { 4598 fatal("unable to open old source file for comparison '{s}': {s}", .{ old_source_file, @errorName(err) }); 4599 }; 4600 defer f.close(); 4601 4602 const stat = try f.stat(); 4603 4604 if (stat.size > max_src_size) 4605 return error.FileTooBig; 4606 4607 var file: Module.File = .{ 4608 .status = .never_loaded, 4609 .source_loaded = false, 4610 .tree_loaded = false, 4611 .zir_loaded = false, 4612 .sub_file_path = old_source_file, 4613 .source = undefined, 4614 .stat_size = stat.size, 4615 .stat_inode = stat.inode, 4616 .stat_mtime = stat.mtime, 4617 .tree = undefined, 4618 .zir = undefined, 4619 .pkg = undefined, 4620 .root_decl = null, 4621 }; 4622 4623 file.pkg = try Package.create(gpa, null, file.sub_file_path); 4624 defer file.pkg.destroy(gpa); 4625 4626 const source = try arena.allocSentinel(u8, @intCast(usize, stat.size), 0); 4627 const amt = try f.readAll(source); 4628 if (amt != stat.size) 4629 return error.UnexpectedEndOfFile; 4630 file.source = source; 4631 file.source_loaded = true; 4632 4633 file.tree = try std.zig.parse(gpa, file.source); 4634 file.tree_loaded = true; 4635 defer file.tree.deinit(gpa); 4636 4637 for (file.tree.errors) |parse_error| { 4638 try printErrMsgToStdErr(gpa, arena, parse_error, file.tree, old_source_file, .auto); 4639 } 4640 if (file.tree.errors.len != 0) { 4641 process.exit(1); 4642 } 4643 4644 file.zir = try AstGen.generate(gpa, file.tree); 4645 file.zir_loaded = true; 4646 defer file.zir.deinit(gpa); 4647 4648 if (file.zir.hasCompileErrors()) { 4649 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); 4650 try Compilation.AllErrors.addZir(arena, &errors, &file); 4651 const ttyconf = std.debug.detectTTYConfig(); 4652 for (errors.items) |full_err_msg| { 4653 full_err_msg.renderToStdErr(ttyconf); 4654 } 4655 process.exit(1); 4656 } 4657 4658 var new_f = fs.cwd().openFile(new_source_file, .{}) catch |err| { 4659 fatal("unable to open new source file for comparison '{s}': {s}", .{ new_source_file, @errorName(err) }); 4660 }; 4661 defer new_f.close(); 4662 4663 const new_stat = try new_f.stat(); 4664 4665 if (new_stat.size > max_src_size) 4666 return error.FileTooBig; 4667 4668 const new_source = try arena.allocSentinel(u8, @intCast(usize, new_stat.size), 0); 4669 const new_amt = try new_f.readAll(new_source); 4670 if (new_amt != new_stat.size) 4671 return error.UnexpectedEndOfFile; 4672 4673 var new_tree = try std.zig.parse(gpa, new_source); 4674 defer new_tree.deinit(gpa); 4675 4676 for (new_tree.errors) |parse_error| { 4677 try printErrMsgToStdErr(gpa, arena, parse_error, new_tree, new_source_file, .auto); 4678 } 4679 if (new_tree.errors.len != 0) { 4680 process.exit(1); 4681 } 4682 4683 var old_zir = file.zir; 4684 defer old_zir.deinit(gpa); 4685 file.zir_loaded = false; 4686 file.zir = try AstGen.generate(gpa, new_tree); 4687 file.zir_loaded = true; 4688 4689 if (file.zir.hasCompileErrors()) { 4690 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); 4691 try Compilation.AllErrors.addZir(arena, &errors, &file); 4692 const ttyconf = std.debug.detectTTYConfig(); 4693 for (errors.items) |full_err_msg| { 4694 full_err_msg.renderToStdErr(ttyconf); 4695 } 4696 process.exit(1); 4697 } 4698 4699 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; 4700 defer inst_map.deinit(gpa); 4701 4702 var extra_map: std.AutoHashMapUnmanaged(u32, u32) = .{}; 4703 defer extra_map.deinit(gpa); 4704 4705 try Module.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map, &extra_map); 4706 4707 var bw = io.bufferedWriter(io.getStdOut().writer()); 4708 const stdout = bw.writer(); 4709 { 4710 try stdout.print("Instruction mappings:\n", .{}); 4711 var it = inst_map.iterator(); 4712 while (it.next()) |entry| { 4713 try stdout.print(" %{d} => %{d}\n", .{ 4714 entry.key_ptr.*, entry.value_ptr.*, 4715 }); 4716 } 4717 } 4718 { 4719 try stdout.print("Extra mappings:\n", .{}); 4720 var it = extra_map.iterator(); 4721 while (it.next()) |entry| { 4722 try stdout.print(" {d} => {d}\n", .{ 4723 entry.key_ptr.*, entry.value_ptr.*, 4724 }); 4725 } 4726 } 4727 try bw.flush(); 4728 } 4729 4730 fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 { 4731 return std.fmt.parseUnsigned(u64, arg[prefix_len..], 0) catch |err| { 4732 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); 4733 }; 4734 } 4735 4736 fn warnAboutForeignBinaries( 4737 gpa: Allocator, 4738 arena: Allocator, 4739 arg_mode: ArgMode, 4740 target_info: std.zig.system.NativeTargetInfo, 4741 link_libc: bool, 4742 ) !void { 4743 const host_cross_target: std.zig.CrossTarget = .{}; 4744 const host_target_info = try detectNativeTargetInfo(gpa, host_cross_target); 4745 4746 switch (host_target_info.getExternalExecutor(target_info, .{ .link_libc = link_libc })) { 4747 .native => return, 4748 .rosetta => { 4749 const host_name = try host_target_info.target.zigTriple(arena); 4750 const foreign_name = try target_info.target.zigTriple(arena); 4751 warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}). Consider installing Rosetta.", .{ 4752 host_name, foreign_name, 4753 }); 4754 }, 4755 .qemu => |qemu| { 4756 const host_name = try host_target_info.target.zigTriple(arena); 4757 const foreign_name = try target_info.target.zigTriple(arena); 4758 switch (arg_mode) { 4759 .zig_test => warn( 4760 "the host system ({s}) does not appear to be capable of executing binaries " ++ 4761 "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ 4762 "to run the tests", 4763 .{ host_name, foreign_name, qemu }, 4764 ), 4765 else => warn( 4766 "the host system ({s}) does not appear to be capable of executing binaries " ++ 4767 "from the target ({s}). Consider using '{s}' to run the binary", 4768 .{ host_name, foreign_name, qemu }, 4769 ), 4770 } 4771 }, 4772 .wine => |wine| { 4773 const host_name = try host_target_info.target.zigTriple(arena); 4774 const foreign_name = try target_info.target.zigTriple(arena); 4775 switch (arg_mode) { 4776 .zig_test => warn( 4777 "the host system ({s}) does not appear to be capable of executing binaries " ++ 4778 "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ 4779 "to run the tests", 4780 .{ host_name, foreign_name, wine }, 4781 ), 4782 else => warn( 4783 "the host system ({s}) does not appear to be capable of executing binaries " ++ 4784 "from the target ({s}). Consider using '{s}' to run the binary", 4785 .{ host_name, foreign_name, wine }, 4786 ), 4787 } 4788 }, 4789 .wasmtime => |wasmtime| { 4790 const host_name = try host_target_info.target.zigTriple(arena); 4791 const foreign_name = try target_info.target.zigTriple(arena); 4792 switch (arg_mode) { 4793 .zig_test => warn( 4794 "the host system ({s}) does not appear to be capable of executing binaries " ++ 4795 "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ 4796 "to run the tests", 4797 .{ host_name, foreign_name, wasmtime }, 4798 ), 4799 else => warn( 4800 "the host system ({s}) does not appear to be capable of executing binaries " ++ 4801 "from the target ({s}). Consider using '{s}' to run the binary", 4802 .{ host_name, foreign_name, wasmtime }, 4803 ), 4804 } 4805 }, 4806 .darling => |darling| { 4807 const host_name = try host_target_info.target.zigTriple(arena); 4808 const foreign_name = try target_info.target.zigTriple(arena); 4809 switch (arg_mode) { 4810 .zig_test => warn( 4811 "the host system ({s}) does not appear to be capable of executing binaries " ++ 4812 "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++ 4813 "to run the tests", 4814 .{ host_name, foreign_name, darling }, 4815 ), 4816 else => warn( 4817 "the host system ({s}) does not appear to be capable of executing binaries " ++ 4818 "from the target ({s}). Consider using '{s}' to run the binary", 4819 .{ host_name, foreign_name, darling }, 4820 ), 4821 } 4822 }, 4823 .bad_dl => |foreign_dl| { 4824 const host_dl = host_target_info.dynamic_linker.get() orelse "(none)"; 4825 const tip_suffix = switch (arg_mode) { 4826 .zig_test => ", '--test-no-exec', or '--test-cmd'", 4827 else => "", 4828 }; 4829 warn("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider using '--dynamic-linker'{s}", .{ 4830 host_dl, foreign_dl, tip_suffix, 4831 }); 4832 }, 4833 .bad_os_or_cpu => { 4834 const host_name = try host_target_info.target.zigTriple(arena); 4835 const foreign_name = try target_info.target.zigTriple(arena); 4836 const tip_suffix = switch (arg_mode) { 4837 .zig_test => ". Consider using '--test-no-exec' or '--test-cmd'", 4838 else => "", 4839 }; 4840 warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}){s}", .{ 4841 host_name, foreign_name, tip_suffix, 4842 }); 4843 }, 4844 } 4845 }