master
1const builtin = @import("builtin");
2const native_os = builtin.os.tag;
3
4const std = @import("std");
5const Io = std.Io;
6const assert = std.debug.assert;
7const fs = std.fs;
8const mem = std.mem;
9const process = std.process;
10const Allocator = mem.Allocator;
11const Ast = std.zig.Ast;
12const Color = std.zig.Color;
13const warn = std.log.warn;
14const ThreadPool = std.Thread.Pool;
15const cleanExit = std.process.cleanExit;
16const Cache = std.Build.Cache;
17const Path = std.Build.Cache.Path;
18const Directory = std.Build.Cache.Directory;
19const EnvVar = std.zig.EnvVar;
20const LibCInstallation = std.zig.LibCInstallation;
21const AstGen = std.zig.AstGen;
22const ZonGen = std.zig.ZonGen;
23const Server = std.zig.Server;
24
25const tracy = @import("tracy.zig");
26const Compilation = @import("Compilation.zig");
27const link = @import("link.zig");
28const Package = @import("Package.zig");
29const build_options = @import("build_options");
30const introspect = @import("introspect.zig");
31const wasi_libc = @import("libs/wasi_libc.zig");
32const target_util = @import("target.zig");
33const crash_report = @import("crash_report.zig");
34const Zcu = @import("Zcu.zig");
35const mingw = @import("libs/mingw.zig");
36const dev = @import("dev.zig");
37
38test {
39 _ = Package;
40 _ = @import("codegen.zig");
41}
42
43const thread_stack_size = 60 << 20;
44
45pub const std_options: std.Options = .{
46 .wasiCwd = wasi_cwd,
47 .logFn = log,
48
49 .log_level = switch (builtin.mode) {
50 .Debug => .debug,
51 .ReleaseSafe, .ReleaseFast => .info,
52 .ReleaseSmall => .err,
53 },
54};
55
56pub const panic = crash_report.panic;
57pub const debug = crash_report.debug;
58
59var wasi_preopens: fs.wasi.Preopens = undefined;
60pub fn wasi_cwd() std.os.wasi.fd_t {
61 // Expect the first preopen to be current working directory.
62 const cwd_fd: std.posix.fd_t = 3;
63 assert(mem.eql(u8, wasi_preopens.names[cwd_fd], "."));
64 return cwd_fd;
65}
66
67const fatal = std.process.fatal;
68
69/// This can be global since stdin is a singleton.
70var stdin_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
71/// This can be global since stdout is a singleton.
72var stdout_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
73
74/// Shaming all the locations that inappropriately use an O(N) search algorithm.
75/// Please delete this and fix the compilation errors!
76pub const @"bad O(N)" = void;
77
78const normal_usage =
79 \\Usage: zig [command] [options]
80 \\
81 \\Commands:
82 \\
83 \\ build Build project from build.zig
84 \\ fetch Copy a package into global cache and print its hash
85 \\ init Initialize a Zig package in the current directory
86 \\
87 \\ build-exe Create executable from source or object files
88 \\ build-lib Create library from source or object files
89 \\ build-obj Create object from source or object files
90 \\ test Perform unit testing
91 \\ test-obj Create object for unit testing
92 \\ run Create executable and run immediately
93 \\
94 \\ ast-check Look for simple compile errors in any set of files
95 \\ fmt Reformat Zig source into canonical form
96 \\ reduce Minimize a bug report
97 \\ translate-c Convert C code to Zig code
98 \\
99 \\ ar Use Zig as a drop-in archiver
100 \\ cc Use Zig as a drop-in C compiler
101 \\ c++ Use Zig as a drop-in C++ compiler
102 \\ dlltool Use Zig as a drop-in dlltool.exe
103 \\ lib Use Zig as a drop-in lib.exe
104 \\ ranlib Use Zig as a drop-in ranlib
105 \\ objcopy Use Zig as a drop-in objcopy
106 \\ rc Use Zig as a drop-in rc.exe
107 \\
108 \\ env Print lib path, std path, cache directory, and version
109 \\ help Print this help and exit
110 \\ std View standard library documentation in a browser
111 \\ libc Display native libc paths file or validate one
112 \\ targets List available compilation targets
113 \\ version Print version number and exit
114 \\ zen Print Zen of Zig and exit
115 \\
116 \\General Options:
117 \\
118 \\ -h, --help Print command-specific usage
119 \\
120;
121
122const debug_usage = normal_usage ++
123 \\
124 \\Debug Commands:
125 \\
126 \\ changelist Compute mappings from old ZIR to new ZIR
127 \\ dump-zir Dump a file containing cached ZIR
128 \\ detect-cpu Compare Zig's CPU feature detection vs LLVM
129 \\ llvm-ints Dump a list of LLVMABIAlignmentOfType for all integers
130 \\
131;
132
133const usage = if (build_options.enable_debug_extensions) debug_usage else normal_usage;
134
135var log_scopes: std.ArrayList([]const u8) = .empty;
136
137pub fn log(
138 comptime level: std.log.Level,
139 comptime scope: @EnumLiteral(),
140 comptime format: []const u8,
141 args: anytype,
142) void {
143 // Hide debug messages unless:
144 // * logging enabled with `-Dlog`.
145 // * the --debug-log arg for the scope has been provided
146 if (@intFromEnum(level) > @intFromEnum(std.options.log_level) or
147 @intFromEnum(level) > @intFromEnum(std.log.Level.info))
148 {
149 if (!build_options.enable_logging) return;
150
151 const scope_name = @tagName(scope);
152 for (log_scopes.items) |log_scope| {
153 if (mem.eql(u8, log_scope, scope_name))
154 break;
155 } else return;
156 }
157
158 // Otherwise, use the default implementation.
159 std.log.defaultLog(level, scope, format, args);
160}
161
162var debug_allocator: std.heap.DebugAllocator(.{
163 .stack_trace_frames = build_options.mem_leak_frames,
164}) = .init;
165
166pub fn main() anyerror!void {
167 const gpa, const is_debug = gpa: {
168 if (build_options.debug_gpa) break :gpa .{ debug_allocator.allocator(), true };
169 if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false };
170 if (builtin.link_libc) {
171 // We would prefer to use raw libc allocator here, but cannot use
172 // it if it won't support the alignment we need.
173 if (@alignOf(std.c.max_align_t) < @max(@alignOf(i128), std.atomic.cache_line)) {
174 break :gpa .{ std.heap.c_allocator, false };
175 }
176 break :gpa .{ std.heap.raw_c_allocator, false };
177 }
178 break :gpa switch (builtin.mode) {
179 .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true },
180 .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false },
181 };
182 };
183 defer if (is_debug) {
184 _ = debug_allocator.deinit();
185 };
186 var arena_instance = std.heap.ArenaAllocator.init(gpa);
187 defer arena_instance.deinit();
188 const arena = arena_instance.allocator();
189
190 const args = try process.argsAlloc(arena);
191
192 if (args.len > 0) crash_report.zig_argv0 = args[0];
193
194 if (tracy.enable_allocation) {
195 var gpa_tracy = tracy.tracyAllocator(gpa);
196 return mainArgs(gpa_tracy.allocator(), arena, args);
197 }
198
199 if (native_os == .wasi) {
200 wasi_preopens = try fs.wasi.preopensAlloc(arena);
201 }
202
203 return mainArgs(gpa, arena, args);
204}
205
206fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
207 const tr = tracy.trace(@src());
208 defer tr.end();
209
210 if (args.len <= 1) {
211 std.log.info("{s}", .{usage});
212 fatal("expected command argument", .{});
213 }
214
215 if (process.can_execv and std.posix.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) {
216 dev.check(.cc_command);
217 // In this case we have accidentally invoked ourselves as "the system C compiler"
218 // to figure out where libc is installed. This is essentially infinite recursion
219 // via child process execution due to the CC environment variable pointing to Zig.
220 // Here we ignore the CC environment variable and exec `cc` as a child process.
221 // However it's possible Zig is installed as *that* C compiler as well, which is
222 // why we have this additional environment variable here to check.
223 var env_map = try process.getEnvMap(arena);
224
225 const inf_loop_env_key = "ZIG_IS_TRYING_TO_NOT_CALL_ITSELF";
226 if (env_map.get(inf_loop_env_key) != null) {
227 fatal("The compilation links against libc, but Zig is unable to provide a libc " ++
228 "for this operating system, and no --libc " ++
229 "parameter was provided, so Zig attempted to invoke the system C compiler " ++
230 "in order to determine where libc is installed. However the system C " ++
231 "compiler is `zig cc`, so no libc installation was found.", .{});
232 }
233 try env_map.put(inf_loop_env_key, "1");
234
235 // Some programs such as CMake will strip the `cc` and subsequent args from the
236 // CC environment variable. We detect and support this scenario here because of
237 // the ZIG_IS_DETECTING_LIBC_PATHS environment variable.
238 if (mem.eql(u8, args[1], "cc")) {
239 return process.execve(arena, args[1..], &env_map);
240 } else {
241 const modified_args = try arena.dupe([]const u8, args);
242 modified_args[0] = "cc";
243 return process.execve(arena, modified_args, &env_map);
244 }
245 }
246
247 var threaded: Io.Threaded = .init(gpa);
248 defer threaded.deinit();
249 const io = threaded.io();
250
251 const cmd = args[1];
252 const cmd_args = args[2..];
253 if (mem.eql(u8, cmd, "build-exe")) {
254 dev.check(.build_exe_command);
255 return buildOutputType(gpa, arena, io, args, .{ .build = .Exe });
256 } else if (mem.eql(u8, cmd, "build-lib")) {
257 dev.check(.build_lib_command);
258 return buildOutputType(gpa, arena, io, args, .{ .build = .Lib });
259 } else if (mem.eql(u8, cmd, "build-obj")) {
260 dev.check(.build_obj_command);
261 return buildOutputType(gpa, arena, io, args, .{ .build = .Obj });
262 } else if (mem.eql(u8, cmd, "test")) {
263 dev.check(.test_command);
264 return buildOutputType(gpa, arena, io, args, .zig_test);
265 } else if (mem.eql(u8, cmd, "test-obj")) {
266 dev.check(.test_command);
267 return buildOutputType(gpa, arena, io, args, .zig_test_obj);
268 } else if (mem.eql(u8, cmd, "run")) {
269 dev.check(.run_command);
270 return buildOutputType(gpa, arena, io, args, .run);
271 } else if (mem.eql(u8, cmd, "dlltool") or
272 mem.eql(u8, cmd, "ranlib") or
273 mem.eql(u8, cmd, "lib") or
274 mem.eql(u8, cmd, "ar"))
275 {
276 dev.check(.ar_command);
277 return process.exit(try llvmArMain(arena, args));
278 } else if (mem.eql(u8, cmd, "build")) {
279 dev.check(.build_command);
280 return cmdBuild(gpa, arena, io, cmd_args);
281 } else if (mem.eql(u8, cmd, "clang") or
282 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
283 {
284 dev.check(.clang_command);
285 return process.exit(try clangMain(arena, args));
286 } else if (mem.eql(u8, cmd, "ld.lld") or
287 mem.eql(u8, cmd, "lld-link") or
288 mem.eql(u8, cmd, "wasm-ld"))
289 {
290 dev.check(.lld_linker);
291 return process.exit(try lldMain(arena, args, true));
292 } else if (mem.eql(u8, cmd, "cc")) {
293 dev.check(.cc_command);
294 return buildOutputType(gpa, arena, io, args, .cc);
295 } else if (mem.eql(u8, cmd, "c++")) {
296 dev.check(.cc_command);
297 return buildOutputType(gpa, arena, io, args, .cpp);
298 } else if (mem.eql(u8, cmd, "translate-c")) {
299 dev.check(.translate_c_command);
300 return buildOutputType(gpa, arena, io, args, .translate_c);
301 } else if (mem.eql(u8, cmd, "rc")) {
302 const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration");
303 return jitCmd(gpa, arena, io, cmd_args, .{
304 .cmd_name = "resinator",
305 .root_src_path = "resinator/main.zig",
306 .depend_on_aro = true,
307 .prepend_zig_lib_dir_path = true,
308 .server = use_server,
309 });
310 } else if (mem.eql(u8, cmd, "fmt")) {
311 dev.check(.fmt_command);
312 return @import("fmt.zig").run(gpa, arena, io, cmd_args);
313 } else if (mem.eql(u8, cmd, "objcopy")) {
314 return jitCmd(gpa, arena, io, cmd_args, .{
315 .cmd_name = "objcopy",
316 .root_src_path = "objcopy.zig",
317 });
318 } else if (mem.eql(u8, cmd, "fetch")) {
319 return cmdFetch(gpa, arena, io, cmd_args);
320 } else if (mem.eql(u8, cmd, "libc")) {
321 return jitCmd(gpa, arena, io, cmd_args, .{
322 .cmd_name = "libc",
323 .root_src_path = "libc.zig",
324 .prepend_zig_lib_dir_path = true,
325 });
326 } else if (mem.eql(u8, cmd, "std")) {
327 return jitCmd(gpa, arena, io, cmd_args, .{
328 .cmd_name = "std",
329 .root_src_path = "std-docs.zig",
330 .prepend_zig_lib_dir_path = true,
331 .prepend_zig_exe_path = true,
332 .prepend_global_cache_path = true,
333 });
334 } else if (mem.eql(u8, cmd, "init")) {
335 return cmdInit(gpa, arena, cmd_args);
336 } else if (mem.eql(u8, cmd, "targets")) {
337 dev.check(.targets_command);
338 const host = std.zig.resolveTargetQueryOrFatal(io, .{});
339 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
340 try @import("print_targets.zig").cmdTargets(arena, cmd_args, &stdout_writer.interface, &host);
341 return stdout_writer.interface.flush();
342 } else if (mem.eql(u8, cmd, "version")) {
343 dev.check(.version_command);
344 try fs.File.stdout().writeAll(build_options.version ++ "\n");
345 return;
346 } else if (mem.eql(u8, cmd, "env")) {
347 dev.check(.env_command);
348 const host = std.zig.resolveTargetQueryOrFatal(io, .{});
349 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
350 try @import("print_env.zig").cmdEnv(
351 arena,
352 &stdout_writer.interface,
353 args,
354 if (native_os == .wasi) wasi_preopens,
355 &host,
356 );
357 return stdout_writer.interface.flush();
358 } else if (mem.eql(u8, cmd, "reduce")) {
359 return jitCmd(gpa, arena, io, cmd_args, .{
360 .cmd_name = "reduce",
361 .root_src_path = "reduce.zig",
362 });
363 } else if (mem.eql(u8, cmd, "zen")) {
364 dev.check(.zen_command);
365 return fs.File.stdout().writeAll(info_zen);
366 } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
367 dev.check(.help_command);
368 return fs.File.stdout().writeAll(usage);
369 } else if (mem.eql(u8, cmd, "ast-check")) {
370 return cmdAstCheck(arena, io, cmd_args);
371 } else if (mem.eql(u8, cmd, "detect-cpu")) {
372 return cmdDetectCpu(io, cmd_args);
373 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "changelist")) {
374 return cmdChangelist(arena, io, cmd_args);
375 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "dump-zir")) {
376 return cmdDumpZir(arena, io, cmd_args);
377 } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "llvm-ints")) {
378 return cmdDumpLlvmInts(gpa, arena, cmd_args);
379 } else {
380 std.log.info("{s}", .{usage});
381 fatal("unknown command: {s}", .{args[1]});
382 }
383}
384
385const usage_build_generic =
386 \\Usage: zig build-exe [options] [files]
387 \\ zig build-lib [options] [files]
388 \\ zig build-obj [options] [files]
389 \\ zig test [options] [files]
390 \\ zig run [options] [files] [-- [args]]
391 \\ zig translate-c [options] [file]
392 \\
393 \\Supported file types:
394 \\ .zig Zig source code
395 \\ .o ELF object file
396 \\ .o Mach-O (macOS) object file
397 \\ .o WebAssembly object file
398 \\ .obj COFF (Windows) object file
399 \\ .lib COFF (Windows) static library
400 \\ .a ELF static library
401 \\ .a Mach-O (macOS) static library
402 \\ .a WebAssembly static library
403 \\ .so ELF shared object (dynamic link)
404 \\ .dll Windows Dynamic Link Library
405 \\ .dylib Mach-O (macOS) dynamic library
406 \\ .tbd (macOS) text-based dylib definition
407 \\ .s Target-specific assembly source code
408 \\ .S Assembly with C preprocessor (requires LLVM extensions)
409 \\ .c C source code (requires LLVM extensions)
410 \\ .cxx .cc .C .cpp .c++ C++ source code (requires LLVM extensions)
411 \\ .m Objective-C source code (requires LLVM extensions)
412 \\ .mm Objective-C++ source code (requires LLVM extensions)
413 \\ .bc LLVM IR Module (requires LLVM extensions)
414 \\
415 \\General Options:
416 \\ -h, --help Print this help and exit
417 \\ --color [auto|off|on] Enable or disable colored error messages
418 \\ -j<N> Limit concurrent jobs (default is to use all CPU cores)
419 \\ -fincremental Enable incremental compilation
420 \\ -fno-incremental Disable incremental compilation
421 \\ -femit-bin[=path] (default) Output machine code
422 \\ -fno-emit-bin Do not output machine code
423 \\ -femit-asm[=path] Output .s (assembly code)
424 \\ -fno-emit-asm (default) Do not output .s (assembly code)
425 \\ -femit-llvm-ir[=path] Produce a .ll file with optimized LLVM IR (requires LLVM extensions)
426 \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with optimized LLVM IR
427 \\ -femit-llvm-bc[=path] Produce an optimized LLVM module as a .bc file (requires LLVM extensions)
428 \\ -fno-emit-llvm-bc (default) Do not produce an optimized LLVM module as a .bc file
429 \\ -femit-h[=path] Generate a C header file (.h)
430 \\ -fno-emit-h (default) Do not generate a C header file (.h)
431 \\ -femit-docs[=path] Create a docs/ dir with html documentation
432 \\ -fno-emit-docs (default) Do not produce docs/ dir with html documentation
433 \\ -femit-implib[=path] (default) Produce an import .lib when building a Windows DLL
434 \\ -fno-emit-implib Do not produce an import .lib when building a Windows DLL
435 \\ --show-builtin Output the source of @import("builtin") then exit
436 \\ --cache-dir [path] Override the local cache directory
437 \\ --global-cache-dir [path] Override the global cache directory
438 \\ --zig-lib-dir [path] Override path to Zig installation lib directory
439 \\
440 \\Global Compile Options:
441 \\ --name [name] Compilation unit name (not a file path)
442 \\ --libc [file] Provide a file which specifies libc paths
443 \\ -x language Treat subsequent input files as having type <language>
444 \\ --dep [[import=]name] Add an entry to the next module's import table
445 \\ -M[name][=src] Create a module based on the current per-module settings.
446 \\ The first module is the main module.
447 \\ "std" can be configured by omitting src
448 \\ After a -M argument, per-module settings are reset.
449 \\ --error-limit [num] Set the maximum amount of distinct error values
450 \\ -fllvm Force using LLVM as the codegen backend
451 \\ -fno-llvm Prevent using LLVM as the codegen backend
452 \\ -flibllvm Force using the LLVM API in the codegen backend
453 \\ -fno-libllvm Prevent using the LLVM API in the codegen backend
454 \\ -fclang Force using Clang as the C/C++ compilation backend
455 \\ -fno-clang Prevent using Clang as the C/C++ compilation backend
456 \\ -fPIE Force-enable Position Independent Executable
457 \\ -fno-PIE Force-disable Position Independent Executable
458 \\ -flto Force-enable Link Time Optimization (requires LLVM extensions)
459 \\ -fno-lto Force-disable Link Time Optimization
460 \\ -fdll-export-fns Mark exported functions as DLL exports (Windows)
461 \\ -fno-dll-export-fns Force-disable marking exported functions as DLL exports
462 \\ -freference-trace[=num] Show num lines of reference trace per compile error
463 \\ -fno-reference-trace Disable reference trace
464 \\ -ffunction-sections Places each function in a separate section
465 \\ -fno-function-sections All functions go into same section
466 \\ -fdata-sections Places each data in a separate section
467 \\ -fno-data-sections All data go into same section
468 \\ -fformatted-panics Enable formatted safety panics
469 \\ -fno-formatted-panics Disable formatted safety panics
470 \\ -fstructured-cfg (SPIR-V) force SPIR-V kernels to use structured control flow
471 \\ -fno-structured-cfg (SPIR-V) force SPIR-V kernels to not use structured control flow
472 \\ -mexec-model=[value] (WASI) Execution model
473 \\ -municode (Windows) Use wmain/wWinMain as entry point
474 \\ --time-report Send timing diagnostics to '--listen' clients
475 \\
476 \\Per-Module Compile Options:
477 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
478 \\ -O [mode] Choose what to optimize for
479 \\ Debug (default) Optimizations off, safety on
480 \\ ReleaseFast Optimize for performance, safety off
481 \\ ReleaseSafe Optimize for performance, safety on
482 \\ ReleaseSmall Optimize for small binary, safety off
483 \\ -ofmt=[fmt] Override target object format
484 \\ elf Executable and Linking Format
485 \\ c C source code
486 \\ wasm WebAssembly
487 \\ coff Common Object File Format (Windows)
488 \\ macho macOS relocatables
489 \\ spirv Standard, Portable Intermediate Representation V (SPIR-V)
490 \\ plan9 Plan 9 from Bell Labs object format
491 \\ hex (planned feature) Intel IHEX
492 \\ raw (planned feature) Dump machine code directly
493 \\ -mcpu [cpu] Specify target CPU and feature set
494 \\ -mcmodel=[model] Limit range of code and data virtual addresses
495 \\ default
496 \\ extreme
497 \\ kernel
498 \\ large
499 \\ medany
500 \\ medium
501 \\ medlow
502 \\ medmid
503 \\ normal
504 \\ small
505 \\ tiny
506 \\ -mred-zone Force-enable the "red-zone"
507 \\ -mno-red-zone Force-disable the "red-zone"
508 \\ -fomit-frame-pointer Omit the stack frame pointer
509 \\ -fno-omit-frame-pointer Store the stack frame pointer
510 \\ -fPIC Force-enable Position Independent Code
511 \\ -fno-PIC Force-disable Position Independent Code
512 \\ -fstack-check Enable stack probing in unsafe builds
513 \\ -fno-stack-check Disable stack probing in safe builds
514 \\ -fstack-protector Enable stack protection in unsafe builds
515 \\ -fno-stack-protector Disable stack protection in safe builds
516 \\ -fvalgrind Include valgrind client requests in release builds
517 \\ -fno-valgrind Omit valgrind client requests in debug builds
518 \\ -fsanitize-c[=mode] Enable C undefined behavior detection in unsafe builds
519 \\ trap Insert trap instructions on undefined behavior
520 \\ full (Default) Insert runtime calls on undefined behavior
521 \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds
522 \\ -fsanitize-thread Enable Thread Sanitizer
523 \\ -fno-sanitize-thread Disable Thread Sanitizer
524 \\ -ffuzz Enable fuzz testing instrumentation
525 \\ -fno-fuzz Disable fuzz testing instrumentation
526 \\ -fbuiltin Enable implicit builtin knowledge of functions
527 \\ -fno-builtin Disable implicit builtin knowledge of functions
528 \\ -funwind-tables Always produce unwind table entries for all functions
529 \\ -fasync-unwind-tables Always produce asynchronous unwind table entries for all functions
530 \\ -fno-unwind-tables Never produce unwind table entries
531 \\ -ferror-tracing Enable error tracing in release builds
532 \\ -fno-error-tracing Disable error tracing in debug builds
533 \\ -fsingle-threaded Code assumes there is only one thread
534 \\ -fno-single-threaded Code may not assume there is only one thread
535 \\ -fstrip Omit debug symbols
536 \\ -fno-strip Keep debug symbols
537 \\ -idirafter [dir] Add directory to AFTER include search path
538 \\ -isystem [dir] Add directory to SYSTEM include search path
539 \\ -I[dir] Add directory to include search path
540 \\ --embed-dir=[dir] Add directory to embed search path
541 \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted)
542 \\ -cflags [flags] -- Set extra flags for the next positional C source files
543 \\ -rcflags [flags] -- Set extra flags for the next positional .rc source files
544 \\ -rcincludes=[type] Set the type of includes to use when compiling .rc source files
545 \\ any (default) Use msvc if available, fall back to gnu
546 \\ msvc Use msvc include paths (must be present on the system)
547 \\ gnu Use mingw include paths (distributed with Zig)
548 \\ none Do not use any autodetected include paths
549 \\
550 \\Global Link Options:
551 \\ -T[script], --script [script] Use a custom linker script
552 \\ --version-script [path] Provide a version .map file
553 \\ --undefined-version Allow version scripts to refer to undefined symbols
554 \\ --no-undefined-version (default) Disallow version scripts from referring to undefined symbols
555 \\ --enable-new-dtags Use the new behavior for dynamic tags (RUNPATH)
556 \\ --disable-new-dtags Use the old behavior for dynamic tags (RPATH)
557 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
558 \\ --no-dynamic-linker Do not set any dynamic interpreter path
559 \\ --sysroot [path] Set the system root directory (usually /)
560 \\ --version [ver] Dynamic library semver
561 \\ -fentry Enable entry point with default symbol name
562 \\ -fentry=[name] Override the entry point symbol name
563 \\ -fno-entry Do not output any entry point
564 \\ --force_undefined [name] Specify the symbol must be defined for the link to succeed
565 \\ -fsoname[=name] Override the default SONAME value
566 \\ -fno-soname Disable emitting a SONAME
567 \\ -flld Force using LLD as the linker
568 \\ -fno-lld Prevent using LLD as the linker
569 \\ -fcompiler-rt Always include compiler-rt symbols in output
570 \\ -fno-compiler-rt Prevent including compiler-rt symbols in output
571 \\ -fubsan-rt Always include ubsan-rt symbols in the output
572 \\ -fno-ubsan-rt Prevent including ubsan-rt symbols in the output
573 \\ -rdynamic Add all symbols to the dynamic symbol table
574 \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library
575 \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library
576 \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries
577 \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries
578 \\ -fallow-so-scripts Allows .so files to be GNU ld scripts
579 \\ -fno-allow-so-scripts (default) .so files must be ELF files
580 \\ --build-id[=style] At a minor link-time expense, embeds a build ID in binaries
581 \\ fast 8-byte non-cryptographic hash (COFF, ELF, WASM)
582 \\ sha1, tree 20-byte cryptographic hash (ELF, WASM)
583 \\ md5 16-byte cryptographic hash (ELF)
584 \\ uuid 16-byte random UUID (ELF, WASM)
585 \\ 0x[hexstring] Constant ID, maximum 32 bytes (ELF, WASM)
586 \\ none (default) No build ID
587 \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker
588 \\ --no-eh-frame-hdr Disable C++ exception handling by passing --no-eh-frame-hdr to linker
589 \\ --emit-relocs Enable output of relocation sections for post build tools
590 \\ -z [arg] Set linker extension flags
591 \\ nodelete Indicate that the object cannot be deleted from a process
592 \\ notext Permit read-only relocations in read-only segments
593 \\ defs Force a fatal error if any undefined symbols remain
594 \\ undefs Reverse of -z defs
595 \\ origin Indicate that the object must have its origin processed
596 \\ nocopyreloc Disable the creation of copy relocations
597 \\ now (default) Force all relocations to be processed on load
598 \\ lazy Don't force all relocations to be processed on load
599 \\ relro (default) Force all relocations to be read-only after processing
600 \\ norelro Don't force all relocations to be read-only after processing
601 \\ common-page-size=[bytes] Set the common page size for ELF binaries
602 \\ max-page-size=[bytes] Set the max page size for ELF binaries
603 \\ -dynamic Force output to be dynamically linked
604 \\ -static Force output to be statically linked
605 \\ -Bsymbolic Bind global references locally
606 \\ --compress-debug-sections=[e] Debug section compression settings
607 \\ none No compression
608 \\ zlib Compression with deflate/inflate
609 \\ zstd Compression with zstandard
610 \\ --gc-sections Force removal of functions and data that are unreachable by the entry point or exported symbols
611 \\ --no-gc-sections Don't force removal of unreachable functions and data
612 \\ --sort-section=[value] Sort wildcard section patterns by 'name' or 'alignment'
613 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
614 \\ --stack [size] Override default stack size
615 \\ --image-base [addr] Set base address for executable image
616 \\ -install_name=[value] (Darwin) add dylib's install name
617 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
618 \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation
619 \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
620 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
621 \\ -dead_strip (Darwin) remove functions and data that are unreachable by the entry point or exported symbols
622 \\ -dead_strip_dylibs (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
623 \\ -ObjC (Darwin) force load all members of static archives that implement an Objective-C class or category
624 \\ --import-memory (WebAssembly) import memory from the environment
625 \\ --export-memory (WebAssembly) export memory to the host (Default unless --import-memory used)
626 \\ --import-symbols (WebAssembly) import missing symbols from the host environment
627 \\ --import-table (WebAssembly) import function table from the host environment
628 \\ --export-table (WebAssembly) export function table to the host environment
629 \\ --initial-memory=[bytes] (WebAssembly) initial size of the linear memory
630 \\ --max-memory=[bytes] (WebAssembly) maximum size of the linear memory
631 \\ --shared-memory (WebAssembly) use shared linear memory
632 \\ --global-base=[addr] (WebAssembly) where to start to place global data
633 \\
634 \\Per-Module Link Options:
635 \\ -l[lib], --library [lib] Link against system library (only if actually used)
636 \\ -needed-l[lib], Link against system library (even if unused)
637 \\ --needed-library [lib]
638 \\ -weak-l[lib] link against system library marking it and all
639 \\ -weak_library [lib] referenced symbols as weak
640 \\ -L[d], --library-directory [d] Add a directory to the library search path
641 \\ -search_paths_first For each library search path, check for dynamic
642 \\ lib then static lib before proceeding to next path.
643 \\ -search_paths_first_static For each library search path, check for static
644 \\ lib then dynamic lib before proceeding to next path.
645 \\ -search_dylibs_first Search for dynamic libs in all library search
646 \\ paths, then static libs.
647 \\ -search_static_first Search for static libs in all library search
648 \\ paths, then dynamic libs.
649 \\ -search_dylibs_only Only search for dynamic libs.
650 \\ -search_static_only Only search for static libs.
651 \\ -rpath [path] Add directory to the runtime library search path
652 \\ -framework [name] (Darwin) link against framework
653 \\ -needed_framework [name] (Darwin) link against framework (even if unused)
654 \\ -needed_library [lib] (Darwin) link against system library (even if unused)
655 \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak
656 \\ -F[dir] (Darwin) add search path for frameworks
657 \\ --export=[value] (WebAssembly) Force a symbol to be exported
658 \\
659 \\Test Options:
660 \\ --test-filter [text] Skip tests that do not match any filter
661 \\ --test-cmd [arg] Specify test execution command one arg at a time
662 \\ --test-cmd-bin Appends test binary path to test cmd args
663 \\ --test-no-exec Compiles test binary without running it
664 \\ --test-runner [path] Specify a custom test runner
665 \\
666 \\Debug Options (Zig Compiler Development):
667 \\ -fopt-bisect-limit=[limit] Only run [limit] first LLVM optimization passes
668 \\ -fstack-report Print stack size diagnostics
669 \\ --verbose-link Display linker invocations
670 \\ --verbose-cc Display C compiler invocations
671 \\ --verbose-air Enable compiler debug output for Zig AIR
672 \\ --verbose-intern-pool Enable compiler debug output for InternPool
673 \\ --verbose-generic-instances Enable compiler debug output for generic instance generation
674 \\ --verbose-llvm-ir[=path] Enable compiler debug output for unoptimized LLVM IR
675 \\ --verbose-llvm-bc=[path] Enable compiler debug output for unoptimized LLVM BC
676 \\ --verbose-cimport Enable compiler debug output for C imports
677 \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features
678 \\ --debug-log [scope] Enable printing debug/info log messages for scope
679 \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error
680 \\ --debug-link-snapshot Enable dumping of the linker's state in JSON format
681 \\ --debug-rt Debug compiler runtime libraries
682 \\ --debug-incremental Enable incremental compilation debug features
683 \\
684;
685
686const SOName = union(enum) {
687 no,
688 yes_default_value,
689 yes: []const u8,
690};
691
692const EmitBin = union(enum) {
693 no,
694 yes_default_path,
695 yes: []const u8,
696 yes_a_out,
697};
698
699const Emit = union(enum) {
700 no,
701 yes_default_path,
702 yes: []const u8,
703
704 const OutputToCacheReason = enum { listen, @"zig run", @"zig test" };
705 fn resolve(emit: Emit, default_basename: []const u8, output_to_cache: ?OutputToCacheReason) Compilation.CreateOptions.Emit {
706 return switch (emit) {
707 .no => .no,
708 .yes_default_path => if (output_to_cache != null) .yes_cache else .{ .yes_path = default_basename },
709 .yes => |path| if (output_to_cache) |reason| {
710 switch (reason) {
711 .listen => fatal("--listen incompatible with explicit output path '{s}'", .{path}),
712 .@"zig run", .@"zig test" => fatal(
713 "'{s}' with explicit output path '{s}' requires explicit '-femit-bin=path' or '-fno-emit-bin'",
714 .{ @tagName(reason), path },
715 ),
716 }
717 } else e: {
718 // If there's a dirname, check that dir exists. This will give a more descriptive error than `Compilation` otherwise would.
719 if (fs.path.dirname(path)) |dir_path| {
720 var dir = fs.cwd().openDir(dir_path, .{}) catch |err| {
721 fatal("unable to open output directory '{s}': {s}", .{ dir_path, @errorName(err) });
722 };
723 dir.close();
724 }
725 break :e .{ .yes_path = path };
726 },
727 };
728 }
729};
730
731const ArgMode = union(enum) {
732 build: std.builtin.OutputMode,
733 cc,
734 cpp,
735 translate_c,
736 zig_test,
737 zig_test_obj,
738 run,
739};
740
741const Listen = union(enum) {
742 none,
743 stdio: if (dev.env.supports(.stdio_listen)) void else noreturn,
744 ip4: if (dev.env.supports(.network_listen)) Io.net.Ip4Address else noreturn,
745};
746
747const ArgsIterator = struct {
748 resp_file: ?ArgIteratorResponseFile = null,
749 args: []const []const u8,
750 i: usize = 0,
751 fn next(it: *@This()) ?[]const u8 {
752 if (it.i >= it.args.len) {
753 if (it.resp_file) |*resp| return resp.next();
754 return null;
755 }
756 defer it.i += 1;
757 return it.args[it.i];
758 }
759 fn nextOrFatal(it: *@This()) []const u8 {
760 if (it.i >= it.args.len) {
761 if (it.resp_file) |*resp| if (resp.next()) |ret| return ret;
762 fatal("expected parameter after {s}", .{it.args[it.i - 1]});
763 }
764 defer it.i += 1;
765 return it.args[it.i];
766 }
767};
768
769/// Similar to `link.Framework` except it doesn't store yet unresolved
770/// path to the framework.
771const Framework = struct {
772 needed: bool = false,
773 weak: bool = false,
774};
775
776const CliModule = struct {
777 root_path: []const u8,
778 root_src_path: []const u8,
779 cc_argv: []const []const u8,
780 inherited: Package.Module.CreateOptions.Inherited,
781 target_arch_os_abi: ?[]const u8,
782 target_mcpu: ?[]const u8,
783
784 deps: []const Dep,
785 resolved: ?*Package.Module,
786
787 c_source_files_start: usize,
788 c_source_files_end: usize,
789 rc_source_files_start: usize,
790 rc_source_files_end: usize,
791
792 const Dep = struct {
793 key: []const u8,
794 value: []const u8,
795 };
796};
797
798fn buildOutputType(
799 gpa: Allocator,
800 arena: Allocator,
801 io: Io,
802 all_args: []const []const u8,
803 arg_mode: ArgMode,
804) !void {
805 var provided_name: ?[]const u8 = null;
806 var root_src_file: ?[]const u8 = null;
807 var version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 };
808 var have_version = false;
809 var compatibility_version: ?std.SemanticVersion = null;
810 var function_sections = false;
811 var data_sections = false;
812 var listen: Listen = .none;
813 var debug_compile_errors = false;
814 var debug_incremental = false;
815 var verbose_link = (native_os != .wasi or builtin.link_libc) and
816 EnvVar.ZIG_VERBOSE_LINK.isSet();
817 var verbose_cc = (native_os != .wasi or builtin.link_libc) and
818 EnvVar.ZIG_VERBOSE_CC.isSet();
819 var verbose_air = false;
820 var verbose_intern_pool = false;
821 var verbose_generic_instances = false;
822 var verbose_llvm_ir: ?[]const u8 = null;
823 var verbose_llvm_bc: ?[]const u8 = null;
824 var verbose_cimport = false;
825 var verbose_llvm_cpu_features = false;
826 var time_report = false;
827 var stack_report = false;
828 var show_builtin = false;
829 var emit_bin: EmitBin = .yes_default_path;
830 var emit_asm: Emit = .no;
831 var emit_llvm_ir: Emit = .no;
832 var emit_llvm_bc: Emit = .no;
833 var emit_docs: Emit = .no;
834 var emit_implib: Emit = .yes_default_path;
835 var emit_implib_arg_provided = false;
836 var target_arch_os_abi: ?[]const u8 = null;
837 var target_mcpu: ?[]const u8 = null;
838 var emit_h: Emit = .no;
839 var soname: SOName = undefined;
840 var want_compiler_rt: ?bool = null;
841 var want_ubsan_rt: ?bool = null;
842 var linker_script: ?[]const u8 = null;
843 var version_script: ?[]const u8 = null;
844 var linker_repro: ?bool = null;
845 var linker_allow_undefined_version: bool = false;
846 var linker_enable_new_dtags: ?bool = null;
847 var disable_c_depfile = false;
848 var linker_sort_section: ?link.File.Lld.Elf.SortSection = null;
849 var linker_gc_sections: ?bool = null;
850 var linker_compress_debug_sections: ?std.zig.CompressDebugSections = null;
851 var linker_allow_shlib_undefined: ?bool = null;
852 var allow_so_scripts: bool = false;
853 var linker_bind_global_refs_locally: ?bool = null;
854 var linker_import_symbols: bool = false;
855 var linker_import_table: bool = false;
856 var linker_export_table: bool = false;
857 var linker_initial_memory: ?u64 = null;
858 var linker_max_memory: ?u64 = null;
859 var linker_global_base: ?u64 = null;
860 var linker_print_gc_sections: bool = false;
861 var linker_print_icf_sections: bool = false;
862 var linker_print_map: bool = false;
863 var llvm_opt_bisect_limit: c_int = -1;
864 var linker_z_nocopyreloc = false;
865 var linker_z_nodelete = false;
866 var linker_z_notext = false;
867 var linker_z_defs = false;
868 var linker_z_origin = false;
869 var linker_z_now = true;
870 var linker_z_relro = true;
871 var linker_z_common_page_size: ?u64 = null;
872 var linker_z_max_page_size: ?u64 = null;
873 var linker_tsaware = false;
874 var linker_nxcompat = false;
875 var linker_dynamicbase = true;
876 var linker_optimization: ?[]const u8 = null;
877 var linker_module_definition_file: ?[]const u8 = null;
878 var test_no_exec = false;
879 var entry: Compilation.CreateOptions.Entry = .default;
880 var force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .empty;
881 var stack_size: ?u64 = null;
882 var image_base: ?u64 = null;
883 var link_eh_frame_hdr = false;
884 var link_emit_relocs = false;
885 var build_id: ?std.zig.BuildId = null;
886 var runtime_args_start: ?usize = null;
887 var test_filters: std.ArrayList([]const u8) = .empty;
888 var test_runner_path: ?[]const u8 = null;
889 var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena);
890 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
891 var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
892 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
893 var subsystem: ?std.zig.Subsystem = null;
894 var major_subsystem_version: ?u16 = null;
895 var minor_subsystem_version: ?u16 = null;
896 var mingw_unicode_entry_point: bool = false;
897 var enable_link_snapshots: bool = false;
898 var debug_compiler_runtime_libs = false;
899 var install_name: ?[]const u8 = null;
900 var hash_style: link.File.Lld.Elf.HashStyle = .both;
901 var entitlements: ?[]const u8 = null;
902 var pagezero_size: ?u64 = null;
903 var lib_search_strategy: link.UnresolvedInput.SearchStrategy = .paths_first;
904 var lib_preferred_mode: std.builtin.LinkMode = .dynamic;
905 var headerpad_size: ?u32 = null;
906 var headerpad_max_install_names: bool = false;
907 var dead_strip_dylibs: bool = false;
908 var force_load_objc: bool = false;
909 var discard_local_symbols: bool = false;
910 var contains_res_file: bool = false;
911 var reference_trace: ?u32 = null;
912 var pdb_out_path: ?[]const u8 = null;
913 var error_limit: ?Zcu.ErrorInt = null;
914 // These are before resolving sysroot.
915 var extra_cflags: std.ArrayList([]const u8) = .empty;
916 var extra_rcflags: std.ArrayList([]const u8) = .empty;
917 var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty;
918 var rc_includes: std.zig.RcIncludes = .any;
919 var manifest_file: ?[]const u8 = null;
920 var linker_export_symbol_names: std.ArrayList([]const u8) = .empty;
921
922 // Tracks the position in c_source_files which have already their owner populated.
923 var c_source_files_owner_index: usize = 0;
924 // Tracks the position in rc_source_files which have already their owner populated.
925 var rc_source_files_owner_index: usize = 0;
926
927 // null means replace with the test executable binary
928 var test_exec_args: std.ArrayList(?[]const u8) = .empty;
929
930 // These get set by CLI flags and then snapshotted when a `-M` flag is
931 // encountered.
932 var mod_opts: Package.Module.CreateOptions.Inherited = .{};
933
934 // These get appended to by CLI flags and then slurped when a `-M` flag
935 // is encountered.
936 var cssan: ClangSearchSanitizer = .{};
937 var cc_argv: std.ArrayList([]const u8) = .empty;
938 var deps: std.ArrayList(CliModule.Dep) = .empty;
939
940 // Contains every module specified via -M. The dependencies are added
941 // after argument parsing is completed. We use a StringArrayHashMap to make
942 // error output consistent. "root" is special.
943 var create_module: CreateModule = .{
944 // Populated just before the call to `createModule`.
945 .dirs = undefined,
946 .object_format = null,
947 .dynamic_linker = null,
948 .modules = .{},
949 .opts = .{
950 .is_test = switch (arg_mode) {
951 .zig_test, .zig_test_obj => true,
952 .build, .cc, .cpp, .translate_c, .run => false,
953 },
954 // Populated while parsing CLI args.
955 .output_mode = undefined,
956 // Populated in the call to `createModule` for the root module.
957 .resolved_target = undefined,
958 .have_zcu = false,
959 // Populated just before the call to `createModule`.
960 .emit_llvm_ir = undefined,
961 // Populated just before the call to `createModule`.
962 .emit_llvm_bc = undefined,
963 // Populated just before the call to `createModule`.
964 .emit_bin = undefined,
965 // Populated just before the call to `createModule`.
966 .any_c_source_files = undefined,
967 },
968 // Populated in the call to `createModule` for the root module.
969 .resolved_options = undefined,
970
971 .cli_link_inputs = .empty,
972 .windows_libs = .empty,
973 .link_inputs = .empty,
974
975 .c_source_files = .{},
976 .rc_source_files = .{},
977
978 .llvm_m_args = .{},
979 .sysroot = null,
980 .lib_directories = .{}, // populated by createModule()
981 .lib_dir_args = .{}, // populated from CLI arg parsing
982 .libc_installation = null,
983 .want_native_include_dirs = false,
984 .frameworks = .{},
985 .framework_dirs = .{},
986 .rpath_list = .{},
987 .each_lib_rpath = null,
988 .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena),
989 .native_system_include_paths = &.{},
990 };
991 defer create_module.link_inputs.deinit(gpa);
992
993 // before arg parsing, check for the NO_COLOR and CLICOLOR_FORCE environment variables
994 // if set, default the color setting to .off or .on, respectively
995 // explicit --color arguments will still override this setting.
996 // Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162
997 var color: Color = if (native_os == .wasi or EnvVar.NO_COLOR.isSet())
998 .off
999 else if (EnvVar.CLICOLOR_FORCE.isSet())
1000 .on
1001 else
1002 .auto;
1003 var n_jobs: ?u32 = null;
1004
1005 switch (arg_mode) {
1006 .build, .translate_c, .zig_test, .zig_test_obj, .run => {
1007 switch (arg_mode) {
1008 .build => |m| {
1009 create_module.opts.output_mode = m;
1010 },
1011 .translate_c => {
1012 emit_bin = .no;
1013 create_module.opts.output_mode = .Obj;
1014 },
1015 .zig_test, .run => {
1016 create_module.opts.output_mode = .Exe;
1017 },
1018 .zig_test_obj => {
1019 create_module.opts.output_mode = .Obj;
1020 },
1021 else => unreachable,
1022 }
1023
1024 soname = .yes_default_value;
1025
1026 var args_iter = ArgsIterator{
1027 .args = all_args[2..],
1028 };
1029
1030 var file_ext: ?Compilation.FileExt = null;
1031 args_loop: while (args_iter.next()) |arg| {
1032 if (mem.cutPrefix(u8, arg, "@")) |resp_file_path| {
1033 // This is a "compiler response file". We must parse the file and treat its
1034 // contents as command line parameters.
1035 args_iter.resp_file = initArgIteratorResponseFile(arena, resp_file_path) catch |err| {
1036 fatal("unable to read response file '{s}': {s}", .{ resp_file_path, @errorName(err) });
1037 };
1038 } else if (mem.startsWith(u8, arg, "-")) {
1039 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
1040 try fs.File.stdout().writeAll(usage_build_generic);
1041 return cleanExit();
1042 } else if (mem.eql(u8, arg, "--")) {
1043 if (arg_mode == .run) {
1044 // args_iter.i is 1, referring the next arg after "--" in ["--", ...]
1045 // Add +2 to the index so it is relative to all_args
1046 runtime_args_start = args_iter.i + 2;
1047 break :args_loop;
1048 } else {
1049 fatal("unexpected end-of-parameter mark: --", .{});
1050 }
1051 } else if (mem.eql(u8, arg, "--dep")) {
1052 const next_arg = args_iter.nextOrFatal();
1053 const key, const value = mem.cutScalar(u8, next_arg, '=') orelse .{ next_arg, next_arg };
1054 if (mem.eql(u8, key, "std") and !mem.eql(u8, value, "std")) {
1055 fatal("unable to import as '{s}': conflicts with builtin module", .{
1056 key,
1057 });
1058 }
1059 for ([_][]const u8{ "root", "builtin" }) |name| {
1060 if (mem.eql(u8, key, name)) {
1061 fatal("unable to import as '{s}': conflicts with builtin module", .{
1062 key,
1063 });
1064 }
1065 }
1066 try deps.append(arena, .{
1067 .key = key,
1068 .value = value,
1069 });
1070 } else if (mem.cutPrefix(u8, arg, "-M")) |rest| {
1071 const mod_name, const root_src_orig = mem.cutScalar(u8, rest, '=') orelse .{ rest, null };
1072 try handleModArg(
1073 arena,
1074 mod_name,
1075 root_src_orig,
1076 &create_module,
1077 &mod_opts,
1078 &cc_argv,
1079 &target_arch_os_abi,
1080 &target_mcpu,
1081 &deps,
1082 &c_source_files_owner_index,
1083 &rc_source_files_owner_index,
1084 &cssan,
1085 );
1086 } else if (mem.eql(u8, arg, "--error-limit")) {
1087 const next_arg = args_iter.nextOrFatal();
1088 error_limit = std.fmt.parseUnsigned(Zcu.ErrorInt, next_arg, 0) catch |err| {
1089 fatal("unable to parse error limit '{s}': {s}", .{ next_arg, @errorName(err) });
1090 };
1091 } else if (mem.eql(u8, arg, "-cflags")) {
1092 extra_cflags.shrinkRetainingCapacity(0);
1093 while (true) {
1094 const next_arg = args_iter.next() orelse {
1095 fatal("expected -- after -cflags", .{});
1096 };
1097 if (mem.eql(u8, next_arg, "--")) break;
1098 try extra_cflags.append(arena, next_arg);
1099 }
1100 } else if (mem.eql(u8, arg, "-rcincludes")) {
1101 rc_includes = parseRcIncludes(args_iter.nextOrFatal());
1102 } else if (mem.cutPrefix(u8, arg, "-rcincludes=")) |rest| {
1103 rc_includes = parseRcIncludes(rest);
1104 } else if (mem.eql(u8, arg, "-rcflags")) {
1105 extra_rcflags.shrinkRetainingCapacity(0);
1106 while (true) {
1107 const next_arg = args_iter.next() orelse {
1108 fatal("expected -- after -rcflags", .{});
1109 };
1110 if (mem.eql(u8, next_arg, "--")) break;
1111 try extra_rcflags.append(arena, next_arg);
1112 }
1113 } else if (mem.eql(u8, arg, "-fstructured-cfg")) {
1114 mod_opts.structured_cfg = true;
1115 } else if (mem.eql(u8, arg, "-fno-structured-cfg")) {
1116 mod_opts.structured_cfg = false;
1117 } else if (mem.eql(u8, arg, "--color")) {
1118 const next_arg = args_iter.next() orelse {
1119 fatal("expected [auto|on|off] after --color", .{});
1120 };
1121 color = std.meta.stringToEnum(Color, next_arg) orelse {
1122 fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
1123 };
1124 } else if (mem.cutPrefix(u8, arg, "-j")) |str| {
1125 const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| {
1126 fatal("unable to parse jobs count '{s}': {s}", .{
1127 str, @errorName(err),
1128 });
1129 };
1130 if (num < 1) {
1131 fatal("number of jobs must be at least 1\n", .{});
1132 }
1133 n_jobs = num;
1134 } else if (mem.eql(u8, arg, "--subsystem")) {
1135 subsystem = try parseSubsystem(args_iter.nextOrFatal());
1136 } else if (mem.eql(u8, arg, "-O")) {
1137 mod_opts.optimize_mode = parseOptimizeMode(args_iter.nextOrFatal());
1138 } else if (mem.cutPrefix(u8, arg, "-fentry=")) |rest| {
1139 entry = .{ .named = rest };
1140 } else if (mem.eql(u8, arg, "--force_undefined")) {
1141 try force_undefined_symbols.put(arena, args_iter.nextOrFatal(), {});
1142 } else if (mem.eql(u8, arg, "--discard-all")) {
1143 discard_local_symbols = true;
1144 } else if (mem.eql(u8, arg, "--stack")) {
1145 stack_size = parseStackSize(args_iter.nextOrFatal());
1146 } else if (mem.eql(u8, arg, "--image-base")) {
1147 image_base = parseImageBase(args_iter.nextOrFatal());
1148 } else if (mem.eql(u8, arg, "--name")) {
1149 provided_name = args_iter.nextOrFatal();
1150 if (!mem.eql(u8, provided_name.?, fs.path.basename(provided_name.?)))
1151 fatal("invalid package name '{s}': cannot contain folder separators", .{provided_name.?});
1152 } else if (mem.eql(u8, arg, "-rpath")) {
1153 try create_module.rpath_list.append(arena, args_iter.nextOrFatal());
1154 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
1155 try create_module.lib_dir_args.append(arena, args_iter.nextOrFatal());
1156 } else if (mem.eql(u8, arg, "-F")) {
1157 try create_module.framework_dirs.append(arena, args_iter.nextOrFatal());
1158 } else if (mem.eql(u8, arg, "-framework")) {
1159 try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{});
1160 } else if (mem.eql(u8, arg, "-weak_framework")) {
1161 try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .weak = true });
1162 } else if (mem.eql(u8, arg, "-needed_framework")) {
1163 try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .needed = true });
1164 } else if (mem.eql(u8, arg, "-install_name")) {
1165 install_name = args_iter.nextOrFatal();
1166 } else if (mem.cutPrefix(u8, arg, "--compress-debug-sections=")) |param| {
1167 linker_compress_debug_sections = std.meta.stringToEnum(std.zig.CompressDebugSections, param) orelse {
1168 fatal("expected --compress-debug-sections=[none|zlib|zstd], found '{s}'", .{param});
1169 };
1170 } else if (mem.eql(u8, arg, "--compress-debug-sections")) {
1171 linker_compress_debug_sections = .zlib;
1172 } else if (mem.eql(u8, arg, "-pagezero_size")) {
1173 const next_arg = args_iter.nextOrFatal();
1174 pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
1175 fatal("unable to parse pagezero size'{s}': {s}", .{ next_arg, @errorName(err) });
1176 };
1177 } else if (mem.eql(u8, arg, "-search_paths_first")) {
1178 lib_search_strategy = .paths_first;
1179 lib_preferred_mode = .dynamic;
1180 } else if (mem.eql(u8, arg, "-search_paths_first_static")) {
1181 lib_search_strategy = .paths_first;
1182 lib_preferred_mode = .static;
1183 } else if (mem.eql(u8, arg, "-search_dylibs_first")) {
1184 lib_search_strategy = .mode_first;
1185 lib_preferred_mode = .dynamic;
1186 } else if (mem.eql(u8, arg, "-search_static_first")) {
1187 lib_search_strategy = .mode_first;
1188 lib_preferred_mode = .static;
1189 } else if (mem.eql(u8, arg, "-search_dylibs_only")) {
1190 lib_search_strategy = .no_fallback;
1191 lib_preferred_mode = .dynamic;
1192 } else if (mem.eql(u8, arg, "-search_static_only")) {
1193 lib_search_strategy = .no_fallback;
1194 lib_preferred_mode = .static;
1195 } else if (mem.eql(u8, arg, "-headerpad")) {
1196 const next_arg = args_iter.nextOrFatal();
1197 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
1198 fatal("unable to parse headerpad size '{s}': {s}", .{ next_arg, @errorName(err) });
1199 };
1200 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
1201 headerpad_max_install_names = true;
1202 } else if (mem.eql(u8, arg, "-dead_strip")) {
1203 linker_gc_sections = true;
1204 } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
1205 dead_strip_dylibs = true;
1206 } else if (mem.eql(u8, arg, "-ObjC")) {
1207 force_load_objc = true;
1208 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
1209 linker_script = args_iter.nextOrFatal();
1210 } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
1211 version_script = args_iter.nextOrFatal();
1212 } else if (mem.eql(u8, arg, "--undefined-version")) {
1213 linker_allow_undefined_version = true;
1214 } else if (mem.eql(u8, arg, "--no-undefined-version")) {
1215 linker_allow_undefined_version = false;
1216 } else if (mem.eql(u8, arg, "--enable-new-dtags")) {
1217 linker_enable_new_dtags = true;
1218 } else if (mem.eql(u8, arg, "--disable-new-dtags")) {
1219 linker_enable_new_dtags = false;
1220 } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
1221 // We don't know whether this library is part of libc
1222 // or libc++ until we resolve the target, so we append
1223 // to the list for now.
1224 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1225 .name = args_iter.nextOrFatal(),
1226 .query = .{
1227 .needed = false,
1228 .weak = false,
1229 .preferred_mode = lib_preferred_mode,
1230 .search_strategy = lib_search_strategy,
1231 .allow_so_scripts = allow_so_scripts,
1232 },
1233 } });
1234 } else if (mem.eql(u8, arg, "--needed-library") or
1235 mem.eql(u8, arg, "-needed-l") or
1236 mem.eql(u8, arg, "-needed_library"))
1237 {
1238 const next_arg = args_iter.nextOrFatal();
1239 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1240 .name = next_arg,
1241 .query = .{
1242 .needed = true,
1243 .weak = false,
1244 .preferred_mode = lib_preferred_mode,
1245 .search_strategy = lib_search_strategy,
1246 .allow_so_scripts = allow_so_scripts,
1247 },
1248 } });
1249 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
1250 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1251 .name = args_iter.nextOrFatal(),
1252 .query = .{
1253 .needed = false,
1254 .weak = true,
1255 .preferred_mode = lib_preferred_mode,
1256 .search_strategy = lib_search_strategy,
1257 .allow_so_scripts = allow_so_scripts,
1258 },
1259 } });
1260 } else if (mem.eql(u8, arg, "-D")) {
1261 try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });
1262 } else if (mem.eql(u8, arg, "-I")) {
1263 try cssan.addIncludePath(arena, &cc_argv, .I, arg, args_iter.nextOrFatal(), false);
1264 } else if (mem.cutPrefix(u8, arg, "--embed-dir=")) |rest| {
1265 try cssan.addIncludePath(arena, &cc_argv, .embed_dir, arg, rest, true);
1266 } else if (mem.eql(u8, arg, "-isystem")) {
1267 try cssan.addIncludePath(arena, &cc_argv, .isystem, arg, args_iter.nextOrFatal(), false);
1268 } else if (mem.eql(u8, arg, "-iwithsysroot")) {
1269 try cssan.addIncludePath(arena, &cc_argv, .iwithsysroot, arg, args_iter.nextOrFatal(), false);
1270 } else if (mem.eql(u8, arg, "-idirafter")) {
1271 try cssan.addIncludePath(arena, &cc_argv, .idirafter, arg, args_iter.nextOrFatal(), false);
1272 } else if (mem.eql(u8, arg, "-iframework")) {
1273 const path = args_iter.nextOrFatal();
1274 try cssan.addIncludePath(arena, &cc_argv, .iframework, arg, path, false);
1275 try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F
1276 } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) {
1277 const path = args_iter.nextOrFatal();
1278 try cssan.addIncludePath(arena, &cc_argv, .iframeworkwithsysroot, arg, path, false);
1279 try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F
1280 } else if (mem.eql(u8, arg, "--version")) {
1281 const next_arg = args_iter.nextOrFatal();
1282 version = std.SemanticVersion.parse(next_arg) catch |err| {
1283 fatal("unable to parse --version '{s}': {s}", .{ next_arg, @errorName(err) });
1284 };
1285 have_version = true;
1286 } else if (mem.eql(u8, arg, "-target")) {
1287 target_arch_os_abi = args_iter.nextOrFatal();
1288 } else if (mem.eql(u8, arg, "-mcpu")) {
1289 target_mcpu = args_iter.nextOrFatal();
1290 } else if (mem.eql(u8, arg, "-mcmodel")) {
1291 mod_opts.code_model = parseCodeModel(args_iter.nextOrFatal());
1292 } else if (mem.cutPrefix(u8, arg, "-mcmodel=")) |rest| {
1293 mod_opts.code_model = parseCodeModel(rest);
1294 } else if (mem.cutPrefix(u8, arg, "-ofmt=")) |rest| {
1295 create_module.object_format = rest;
1296 } else if (mem.cutPrefix(u8, arg, "-mcpu=")) |rest| {
1297 target_mcpu = rest;
1298 } else if (mem.cutPrefix(u8, arg, "-O")) |rest| {
1299 mod_opts.optimize_mode = parseOptimizeMode(rest);
1300 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
1301 create_module.dynamic_linker = args_iter.nextOrFatal();
1302 } else if (mem.eql(u8, arg, "--no-dynamic-linker")) {
1303 create_module.dynamic_linker = "";
1304 } else if (mem.eql(u8, arg, "--sysroot")) {
1305 const next_arg = args_iter.nextOrFatal();
1306 create_module.sysroot = next_arg;
1307 try cc_argv.appendSlice(arena, &.{ "-isysroot", next_arg });
1308 } else if (mem.eql(u8, arg, "--libc")) {
1309 create_module.libc_paths_file = args_iter.nextOrFatal();
1310 } else if (mem.eql(u8, arg, "--test-filter")) {
1311 try test_filters.append(arena, args_iter.nextOrFatal());
1312 } else if (mem.eql(u8, arg, "--test-runner")) {
1313 test_runner_path = args_iter.nextOrFatal();
1314 } else if (mem.eql(u8, arg, "--test-cmd")) {
1315 try test_exec_args.append(arena, args_iter.nextOrFatal());
1316 } else if (mem.eql(u8, arg, "--cache-dir")) {
1317 override_local_cache_dir = args_iter.nextOrFatal();
1318 } else if (mem.eql(u8, arg, "--global-cache-dir")) {
1319 override_global_cache_dir = args_iter.nextOrFatal();
1320 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
1321 override_lib_dir = args_iter.nextOrFatal();
1322 } else if (mem.eql(u8, arg, "--debug-log")) {
1323 if (!build_options.enable_logging) {
1324 warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
1325 _ = args_iter.nextOrFatal();
1326 } else {
1327 try log_scopes.append(arena, args_iter.nextOrFatal());
1328 }
1329 } else if (mem.eql(u8, arg, "--listen")) {
1330 const next_arg = args_iter.nextOrFatal();
1331 if (mem.eql(u8, next_arg, "-")) {
1332 dev.check(.stdio_listen);
1333 listen = .stdio;
1334 } else {
1335 dev.check(.network_listen);
1336 // example: --listen 127.0.0.1:9000
1337 const host, const port_text = mem.cutScalar(u8, next_arg, ':') orelse .{ next_arg, "14735" };
1338 const port = std.fmt.parseInt(u16, port_text, 10) catch |err|
1339 fatal("invalid port number: '{s}': {s}", .{ port_text, @errorName(err) });
1340 listen = .{ .ip4 = Io.net.Ip4Address.parse(host, port) catch |err|
1341 fatal("invalid host: '{s}': {s}", .{ host, @errorName(err) }) };
1342 }
1343 } else if (mem.eql(u8, arg, "--listen=-")) {
1344 dev.check(.stdio_listen);
1345 listen = .stdio;
1346 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {
1347 if (!build_options.enable_link_snapshots) {
1348 warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{});
1349 } else {
1350 enable_link_snapshots = true;
1351 }
1352 } else if (mem.eql(u8, arg, "--debug-rt")) {
1353 debug_compiler_runtime_libs = true;
1354 } else if (mem.eql(u8, arg, "--debug-incremental")) {
1355 if (build_options.enable_debug_extensions) {
1356 debug_incremental = true;
1357 } else {
1358 warn("Zig was compiled without debug extensions. --debug-incremental has no effect.", .{});
1359 }
1360 } else if (mem.eql(u8, arg, "-fincremental")) {
1361 dev.check(.incremental);
1362 create_module.opts.incremental = true;
1363 } else if (mem.eql(u8, arg, "-fno-incremental")) {
1364 create_module.opts.incremental = false;
1365 } else if (mem.eql(u8, arg, "--entitlements")) {
1366 entitlements = args_iter.nextOrFatal();
1367 } else if (mem.eql(u8, arg, "-fcompiler-rt")) {
1368 want_compiler_rt = true;
1369 } else if (mem.eql(u8, arg, "-fno-compiler-rt")) {
1370 want_compiler_rt = false;
1371 } else if (mem.eql(u8, arg, "-fubsan-rt")) {
1372 want_ubsan_rt = true;
1373 } else if (mem.eql(u8, arg, "-fno-ubsan-rt")) {
1374 want_ubsan_rt = false;
1375 } else if (mem.eql(u8, arg, "-feach-lib-rpath")) {
1376 create_module.each_lib_rpath = true;
1377 } else if (mem.eql(u8, arg, "-fno-each-lib-rpath")) {
1378 create_module.each_lib_rpath = false;
1379 } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
1380 try test_exec_args.append(arena, null);
1381 } else if (mem.eql(u8, arg, "--test-no-exec")) {
1382 test_no_exec = true;
1383 } else if (mem.eql(u8, arg, "--time-report")) {
1384 time_report = true;
1385 } else if (mem.eql(u8, arg, "-fstack-report")) {
1386 stack_report = true;
1387 } else if (mem.eql(u8, arg, "-fPIC")) {
1388 mod_opts.pic = true;
1389 } else if (mem.eql(u8, arg, "-fno-PIC")) {
1390 mod_opts.pic = false;
1391 } else if (mem.eql(u8, arg, "-fPIE")) {
1392 create_module.opts.pie = true;
1393 } else if (mem.eql(u8, arg, "-fno-PIE")) {
1394 create_module.opts.pie = false;
1395 } else if (mem.eql(u8, arg, "-flto")) {
1396 create_module.opts.lto = .full;
1397 } else if (mem.cutPrefix(u8, arg, "-flto=")) |mode| {
1398 if (mem.eql(u8, mode, "full")) {
1399 create_module.opts.lto = .full;
1400 } else if (mem.eql(u8, mode, "thin")) {
1401 create_module.opts.lto = .thin;
1402 } else {
1403 fatal("Invalid -flto mode: '{s}'. Must be 'full'or 'thin'.", .{mode});
1404 }
1405 } else if (mem.eql(u8, arg, "-fno-lto")) {
1406 create_module.opts.lto = .none;
1407 } else if (mem.eql(u8, arg, "-funwind-tables")) {
1408 mod_opts.unwind_tables = .sync;
1409 } else if (mem.eql(u8, arg, "-fasync-unwind-tables")) {
1410 mod_opts.unwind_tables = .async;
1411 } else if (mem.eql(u8, arg, "-fno-unwind-tables")) {
1412 mod_opts.unwind_tables = .none;
1413 } else if (mem.eql(u8, arg, "-fstack-check")) {
1414 mod_opts.stack_check = true;
1415 } else if (mem.eql(u8, arg, "-fno-stack-check")) {
1416 mod_opts.stack_check = false;
1417 } else if (mem.eql(u8, arg, "-fstack-protector")) {
1418 mod_opts.stack_protector = Compilation.default_stack_protector_buffer_size;
1419 } else if (mem.eql(u8, arg, "-fno-stack-protector")) {
1420 mod_opts.stack_protector = 0;
1421 } else if (mem.eql(u8, arg, "-mred-zone")) {
1422 mod_opts.red_zone = true;
1423 } else if (mem.eql(u8, arg, "-mno-red-zone")) {
1424 mod_opts.red_zone = false;
1425 } else if (mem.eql(u8, arg, "-fomit-frame-pointer")) {
1426 mod_opts.omit_frame_pointer = true;
1427 } else if (mem.eql(u8, arg, "-fno-omit-frame-pointer")) {
1428 mod_opts.omit_frame_pointer = false;
1429 } else if (mem.eql(u8, arg, "-fsanitize-c")) {
1430 mod_opts.sanitize_c = .full;
1431 } else if (mem.cutPrefix(u8, arg, "-fsanitize-c=")) |mode| {
1432 if (mem.eql(u8, mode, "trap")) {
1433 mod_opts.sanitize_c = .trap;
1434 } else if (mem.eql(u8, mode, "full")) {
1435 mod_opts.sanitize_c = .full;
1436 } else {
1437 fatal("Invalid -fsanitize-c mode: '{s}'. Must be 'trap' or 'full'.", .{mode});
1438 }
1439 } else if (mem.eql(u8, arg, "-fno-sanitize-c")) {
1440 mod_opts.sanitize_c = .off;
1441 } else if (mem.eql(u8, arg, "-fvalgrind")) {
1442 mod_opts.valgrind = true;
1443 } else if (mem.eql(u8, arg, "-fno-valgrind")) {
1444 mod_opts.valgrind = false;
1445 } else if (mem.eql(u8, arg, "-fsanitize-thread")) {
1446 mod_opts.sanitize_thread = true;
1447 } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) {
1448 mod_opts.sanitize_thread = false;
1449 } else if (mem.eql(u8, arg, "-ffuzz")) {
1450 mod_opts.fuzz = true;
1451 } else if (mem.eql(u8, arg, "-fno-fuzz")) {
1452 mod_opts.fuzz = false;
1453 } else if (mem.eql(u8, arg, "-fllvm")) {
1454 create_module.opts.use_llvm = true;
1455 } else if (mem.eql(u8, arg, "-fno-llvm")) {
1456 create_module.opts.use_llvm = false;
1457 } else if (mem.eql(u8, arg, "-flibllvm")) {
1458 create_module.opts.use_lib_llvm = true;
1459 } else if (mem.eql(u8, arg, "-fno-libllvm")) {
1460 create_module.opts.use_lib_llvm = false;
1461 } else if (mem.eql(u8, arg, "-flld")) {
1462 create_module.opts.use_lld = true;
1463 } else if (mem.eql(u8, arg, "-fno-lld")) {
1464 create_module.opts.use_lld = false;
1465 } else if (mem.eql(u8, arg, "-fnew-linker")) {
1466 create_module.opts.use_new_linker = true;
1467 } else if (mem.eql(u8, arg, "-fno-new-linker")) {
1468 create_module.opts.use_new_linker = false;
1469 } else if (mem.eql(u8, arg, "-fclang")) {
1470 create_module.opts.use_clang = true;
1471 } else if (mem.eql(u8, arg, "-fno-clang")) {
1472 create_module.opts.use_clang = false;
1473 } else if (mem.eql(u8, arg, "-fsanitize-coverage-trace-pc-guard")) {
1474 create_module.opts.san_cov_trace_pc_guard = true;
1475 } else if (mem.eql(u8, arg, "-fno-sanitize-coverage-trace-pc-guard")) {
1476 create_module.opts.san_cov_trace_pc_guard = false;
1477 } else if (mem.eql(u8, arg, "-freference-trace")) {
1478 reference_trace = 256;
1479 } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {
1480 reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
1481 fatal("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) });
1482 };
1483 } else if (mem.eql(u8, arg, "-fno-reference-trace")) {
1484 reference_trace = null;
1485 } else if (mem.eql(u8, arg, "-ferror-tracing")) {
1486 mod_opts.error_tracing = true;
1487 } else if (mem.eql(u8, arg, "-fno-error-tracing")) {
1488 mod_opts.error_tracing = false;
1489 } else if (mem.eql(u8, arg, "-rdynamic")) {
1490 create_module.opts.rdynamic = true;
1491 } else if (mem.eql(u8, arg, "-fsoname")) {
1492 soname = .yes_default_value;
1493 } else if (mem.cutPrefix(u8, arg, "-fsoname=")) |rest| {
1494 soname = .{ .yes = rest };
1495 } else if (mem.eql(u8, arg, "-fno-soname")) {
1496 soname = .no;
1497 } else if (mem.eql(u8, arg, "-femit-bin")) {
1498 emit_bin = .yes_default_path;
1499 } else if (mem.cutPrefix(u8, arg, "-femit-bin=")) |rest| {
1500 emit_bin = .{ .yes = rest };
1501 } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
1502 emit_bin = .no;
1503 } else if (mem.eql(u8, arg, "-femit-h")) {
1504 emit_h = .yes_default_path;
1505 } else if (mem.cutPrefix(u8, arg, "-femit-h=")) |rest| {
1506 emit_h = .{ .yes = rest };
1507 } else if (mem.eql(u8, arg, "-fno-emit-h")) {
1508 emit_h = .no;
1509 } else if (mem.eql(u8, arg, "-femit-asm")) {
1510 emit_asm = .yes_default_path;
1511 } else if (mem.cutPrefix(u8, arg, "-femit-asm=")) |rest| {
1512 emit_asm = .{ .yes = rest };
1513 } else if (mem.eql(u8, arg, "-fno-emit-asm")) {
1514 emit_asm = .no;
1515 } else if (mem.eql(u8, arg, "-femit-llvm-ir")) {
1516 emit_llvm_ir = .yes_default_path;
1517 } else if (mem.cutPrefix(u8, arg, "-femit-llvm-ir=")) |rest| {
1518 emit_llvm_ir = .{ .yes = rest };
1519 } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
1520 emit_llvm_ir = .no;
1521 } else if (mem.eql(u8, arg, "-femit-llvm-bc")) {
1522 emit_llvm_bc = .yes_default_path;
1523 } else if (mem.cutPrefix(u8, arg, "-femit-llvm-bc=")) |rest| {
1524 emit_llvm_bc = .{ .yes = rest };
1525 } else if (mem.eql(u8, arg, "-fno-emit-llvm-bc")) {
1526 emit_llvm_bc = .no;
1527 } else if (mem.eql(u8, arg, "-femit-docs")) {
1528 emit_docs = .yes_default_path;
1529 } else if (mem.cutPrefix(u8, arg, "-femit-docs=")) |rest| {
1530 emit_docs = .{ .yes = rest };
1531 } else if (mem.eql(u8, arg, "-fno-emit-docs")) {
1532 emit_docs = .no;
1533 } else if (mem.eql(u8, arg, "-femit-implib")) {
1534 emit_implib = .yes_default_path;
1535 emit_implib_arg_provided = true;
1536 } else if (mem.cutPrefix(u8, arg, "-femit-implib=")) |rest| {
1537 emit_implib = .{ .yes = rest };
1538 emit_implib_arg_provided = true;
1539 } else if (mem.eql(u8, arg, "-fno-emit-implib")) {
1540 emit_implib = .no;
1541 emit_implib_arg_provided = true;
1542 } else if (mem.eql(u8, arg, "-dynamic")) {
1543 create_module.opts.link_mode = .dynamic;
1544 lib_preferred_mode = .dynamic;
1545 lib_search_strategy = .mode_first;
1546 } else if (mem.eql(u8, arg, "-static")) {
1547 create_module.opts.link_mode = .static;
1548 lib_preferred_mode = .static;
1549 lib_search_strategy = .no_fallback;
1550 } else if (mem.eql(u8, arg, "-fdll-export-fns")) {
1551 create_module.opts.dll_export_fns = true;
1552 } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
1553 create_module.opts.dll_export_fns = false;
1554 } else if (mem.eql(u8, arg, "--show-builtin")) {
1555 show_builtin = true;
1556 emit_bin = .no;
1557 } else if (mem.eql(u8, arg, "-fstrip")) {
1558 mod_opts.strip = true;
1559 } else if (mem.eql(u8, arg, "-fno-strip")) {
1560 mod_opts.strip = false;
1561 } else if (mem.eql(u8, arg, "-gdwarf32")) {
1562 create_module.opts.debug_format = .{ .dwarf = .@"32" };
1563 } else if (mem.eql(u8, arg, "-gdwarf64")) {
1564 create_module.opts.debug_format = .{ .dwarf = .@"64" };
1565 } else if (mem.eql(u8, arg, "-fformatted-panics")) {
1566 // Remove this after 0.15.0 is tagged.
1567 warn("-fformatted-panics is deprecated and does nothing", .{});
1568 } else if (mem.eql(u8, arg, "-fno-formatted-panics")) {
1569 // Remove this after 0.15.0 is tagged.
1570 warn("-fno-formatted-panics is deprecated and does nothing", .{});
1571 } else if (mem.eql(u8, arg, "-fsingle-threaded")) {
1572 mod_opts.single_threaded = true;
1573 } else if (mem.eql(u8, arg, "-fno-single-threaded")) {
1574 mod_opts.single_threaded = false;
1575 } else if (mem.eql(u8, arg, "-ffunction-sections")) {
1576 function_sections = true;
1577 } else if (mem.eql(u8, arg, "-fno-function-sections")) {
1578 function_sections = false;
1579 } else if (mem.eql(u8, arg, "-fdata-sections")) {
1580 data_sections = true;
1581 } else if (mem.eql(u8, arg, "-fno-data-sections")) {
1582 data_sections = false;
1583 } else if (mem.eql(u8, arg, "-fbuiltin")) {
1584 mod_opts.no_builtin = false;
1585 } else if (mem.eql(u8, arg, "-fno-builtin")) {
1586 mod_opts.no_builtin = true;
1587 } else if (mem.cutPrefix(u8, arg, "-fopt-bisect-limit=")) |next_arg| {
1588 llvm_opt_bisect_limit = std.fmt.parseInt(c_int, next_arg, 0) catch |err|
1589 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
1590 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
1591 link_eh_frame_hdr = true;
1592 } else if (mem.eql(u8, arg, "--no-eh-frame-hdr")) {
1593 link_eh_frame_hdr = false;
1594 } else if (mem.eql(u8, arg, "--dynamicbase")) {
1595 linker_dynamicbase = true;
1596 } else if (mem.eql(u8, arg, "--no-dynamicbase")) {
1597 linker_dynamicbase = false;
1598 } else if (mem.eql(u8, arg, "--emit-relocs")) {
1599 link_emit_relocs = true;
1600 } else if (mem.eql(u8, arg, "-fallow-shlib-undefined")) {
1601 linker_allow_shlib_undefined = true;
1602 } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) {
1603 linker_allow_shlib_undefined = false;
1604 } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {
1605 allow_so_scripts = true;
1606 } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
1607 allow_so_scripts = false;
1608 } else if (mem.eql(u8, arg, "-z")) {
1609 const z_arg = args_iter.nextOrFatal();
1610 if (mem.eql(u8, z_arg, "nodelete")) {
1611 linker_z_nodelete = true;
1612 } else if (mem.eql(u8, z_arg, "notext")) {
1613 linker_z_notext = true;
1614 } else if (mem.eql(u8, z_arg, "defs")) {
1615 linker_z_defs = true;
1616 } else if (mem.eql(u8, z_arg, "undefs")) {
1617 linker_z_defs = false;
1618 } else if (mem.eql(u8, z_arg, "origin")) {
1619 linker_z_origin = true;
1620 } else if (mem.eql(u8, z_arg, "nocopyreloc")) {
1621 linker_z_nocopyreloc = true;
1622 } else if (mem.eql(u8, z_arg, "now")) {
1623 linker_z_now = true;
1624 } else if (mem.eql(u8, z_arg, "lazy")) {
1625 linker_z_now = false;
1626 } else if (mem.eql(u8, z_arg, "relro")) {
1627 linker_z_relro = true;
1628 } else if (mem.eql(u8, z_arg, "norelro")) {
1629 linker_z_relro = false;
1630 } else if (prefixedIntArg(z_arg, "common-page-size=")) |int| {
1631 linker_z_common_page_size = int;
1632 } else if (prefixedIntArg(z_arg, "max-page-size=")) |int| {
1633 linker_z_max_page_size = int;
1634 } else {
1635 fatal("unsupported linker extension flag: -z {s}", .{z_arg});
1636 }
1637 } else if (mem.eql(u8, arg, "--import-memory")) {
1638 create_module.opts.import_memory = true;
1639 } else if (mem.eql(u8, arg, "-fentry")) {
1640 switch (entry) {
1641 .default, .disabled => entry = .enabled,
1642 .enabled, .named => {},
1643 }
1644 } else if (mem.eql(u8, arg, "-fno-entry")) {
1645 entry = .disabled;
1646 } else if (mem.eql(u8, arg, "--export-memory")) {
1647 create_module.opts.export_memory = true;
1648 } else if (mem.eql(u8, arg, "--import-symbols")) {
1649 linker_import_symbols = true;
1650 } else if (mem.eql(u8, arg, "--import-table")) {
1651 linker_import_table = true;
1652 } else if (mem.eql(u8, arg, "--export-table")) {
1653 linker_export_table = true;
1654 } else if (prefixedIntArg(arg, "--initial-memory=")) |int| {
1655 linker_initial_memory = int;
1656 } else if (prefixedIntArg(arg, "--max-memory=")) |int| {
1657 linker_max_memory = int;
1658 } else if (mem.eql(u8, arg, "--shared-memory")) {
1659 create_module.opts.shared_memory = true;
1660 } else if (prefixedIntArg(arg, "--global-base=")) |int| {
1661 linker_global_base = int;
1662 } else if (mem.cutPrefix(u8, arg, "--export=")) |rest| {
1663 try linker_export_symbol_names.append(arena, rest);
1664 } else if (mem.eql(u8, arg, "-Bsymbolic")) {
1665 linker_bind_global_refs_locally = true;
1666 } else if (mem.eql(u8, arg, "--gc-sections")) {
1667 linker_gc_sections = true;
1668 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
1669 linker_gc_sections = false;
1670 } else if (mem.eql(u8, arg, "--build-id")) {
1671 build_id = .fast;
1672 } else if (mem.cutPrefix(u8, arg, "--build-id=")) |style| {
1673 build_id = std.zig.BuildId.parse(style) catch |err| {
1674 fatal("unable to parse --build-id style '{s}': {s}", .{
1675 style, @errorName(err),
1676 });
1677 };
1678 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
1679 if (build_options.enable_debug_extensions) {
1680 debug_compile_errors = true;
1681 } else {
1682 warn("Zig was compiled without debug extensions. --debug-compile-errors has no effect.", .{});
1683 }
1684 } else if (mem.eql(u8, arg, "--verbose-link")) {
1685 verbose_link = true;
1686 } else if (mem.eql(u8, arg, "--verbose-cc")) {
1687 verbose_cc = true;
1688 } else if (mem.eql(u8, arg, "--verbose-air")) {
1689 verbose_air = true;
1690 } else if (mem.eql(u8, arg, "--verbose-intern-pool")) {
1691 verbose_intern_pool = true;
1692 } else if (mem.eql(u8, arg, "--verbose-generic-instances")) {
1693 verbose_generic_instances = true;
1694 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
1695 verbose_llvm_ir = "-";
1696 } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-ir=")) |rest| {
1697 verbose_llvm_ir = rest;
1698 } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-bc=")) |rest| {
1699 verbose_llvm_bc = rest;
1700 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
1701 verbose_cimport = true;
1702 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
1703 verbose_llvm_cpu_features = true;
1704 } else if (mem.cutPrefix(u8, arg, "-T")) |rest| {
1705 linker_script = rest;
1706 } else if (mem.cutPrefix(u8, arg, "-L")) |rest| {
1707 try create_module.lib_dir_args.append(arena, rest);
1708 } else if (mem.cutPrefix(u8, arg, "-F")) |rest| {
1709 try create_module.framework_dirs.append(arena, rest);
1710 } else if (mem.cutPrefix(u8, arg, "-l")) |name| {
1711 // We don't know whether this library is part of libc
1712 // or libc++ until we resolve the target, so we append
1713 // to the list for now.
1714 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1715 .name = name,
1716 .query = .{
1717 .needed = false,
1718 .weak = false,
1719 .preferred_mode = lib_preferred_mode,
1720 .search_strategy = lib_search_strategy,
1721 .allow_so_scripts = allow_so_scripts,
1722 },
1723 } });
1724 } else if (mem.cutPrefix(u8, arg, "-needed-l")) |name| {
1725 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1726 .name = name,
1727 .query = .{
1728 .needed = true,
1729 .weak = false,
1730 .preferred_mode = lib_preferred_mode,
1731 .search_strategy = lib_search_strategy,
1732 .allow_so_scripts = allow_so_scripts,
1733 },
1734 } });
1735 } else if (mem.cutPrefix(u8, arg, "-weak-l")) |name| {
1736 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1737 .name = name,
1738 .query = .{
1739 .needed = false,
1740 .weak = true,
1741 .preferred_mode = lib_preferred_mode,
1742 .search_strategy = lib_search_strategy,
1743 .allow_so_scripts = allow_so_scripts,
1744 },
1745 } });
1746 } else if (mem.startsWith(u8, arg, "-D")) {
1747 try cc_argv.append(arena, arg);
1748 } else if (mem.cutPrefix(u8, arg, "-I")) |rest| {
1749 try cssan.addIncludePath(arena, &cc_argv, .I, arg, rest, true);
1750 } else if (mem.cutPrefix(u8, arg, "-x")) |rest| {
1751 const lang = if (rest.len == 0) args_iter.nextOrFatal() else rest;
1752 if (mem.eql(u8, lang, "none")) {
1753 file_ext = null;
1754 } else if (Compilation.LangToExt.get(lang)) |got_ext| {
1755 file_ext = got_ext;
1756 } else {
1757 fatal("language not recognized: '{s}'", .{lang});
1758 }
1759 } else if (mem.cutPrefix(u8, arg, "-mexec-model=")) |rest| {
1760 create_module.opts.wasi_exec_model = parseWasiExecModel(rest);
1761 } else if (mem.eql(u8, arg, "-municode")) {
1762 mingw_unicode_entry_point = true;
1763 } else {
1764 fatal("unrecognized parameter: '{s}'", .{arg});
1765 }
1766 } else switch (file_ext orelse Compilation.classifyFileExt(arg)) {
1767 .shared_library, .object, .static_library => {
1768 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1769 .path = Path.initCwd(arg),
1770 .query = .{
1771 .preferred_mode = lib_preferred_mode,
1772 .search_strategy = lib_search_strategy,
1773 .allow_so_scripts = allow_so_scripts,
1774 },
1775 } });
1776 // We do not set `any_dyn_libs` yet because a .so file
1777 // may actually resolve to a GNU ld script which ends
1778 // up being a static library.
1779 },
1780 .res => {
1781 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1782 .path = Path.initCwd(arg),
1783 .query = .{
1784 .preferred_mode = lib_preferred_mode,
1785 .search_strategy = lib_search_strategy,
1786 .allow_so_scripts = allow_so_scripts,
1787 },
1788 } });
1789 contains_res_file = true;
1790 },
1791 .manifest => {
1792 if (manifest_file) |other| {
1793 fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other });
1794 } else manifest_file = arg;
1795 },
1796 .assembly, .assembly_with_cpp, .c, .cpp, .h, .hpp, .hm, .hmm, .ll, .bc, .m, .mm => {
1797 dev.check(.c_compiler);
1798 try create_module.c_source_files.append(arena, .{
1799 // Populated after module creation.
1800 .owner = undefined,
1801 .src_path = arg,
1802 .extra_flags = try arena.dupe([]const u8, extra_cflags.items),
1803 // duped when parsing the args.
1804 .ext = file_ext,
1805 });
1806 },
1807 .rc => {
1808 dev.check(.win32_resource);
1809 try create_module.rc_source_files.append(arena, .{
1810 // Populated after module creation.
1811 .owner = undefined,
1812 .src_path = arg,
1813 .extra_flags = try arena.dupe([]const u8, extra_rcflags.items),
1814 });
1815 },
1816 .zig => {
1817 if (root_src_file) |other| {
1818 fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other });
1819 } else root_src_file = arg;
1820 },
1821 .def, .unknown => {
1822 if (std.ascii.eqlIgnoreCase(".xml", fs.path.extension(arg))) {
1823 warn("embedded manifest files must have the extension '.manifest'", .{});
1824 }
1825 fatal("unrecognized file extension of parameter '{s}'", .{arg});
1826 },
1827 }
1828 }
1829 },
1830 .cc, .cpp => {
1831 dev.check(.cc_command);
1832
1833 emit_h = .no;
1834 soname = .no;
1835 create_module.opts.ensure_libc_on_non_freestanding = true;
1836 create_module.opts.ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
1837 create_module.want_native_include_dirs = true;
1838 // Clang's driver enables this switch unconditionally.
1839 // Disabling the emission of .eh_frame_hdr can unexpectedly break
1840 // some functionality that depend on it, such as C++ exceptions and
1841 // DWARF-based stack traces.
1842 link_eh_frame_hdr = true;
1843 allow_so_scripts = true;
1844
1845 const COutMode = enum {
1846 link,
1847 object,
1848 assembly,
1849 preprocessor,
1850 };
1851 var c_out_mode: ?COutMode = null;
1852 var out_path: ?[]const u8 = null;
1853 var is_shared_lib = false;
1854 var preprocessor_args = std.array_list.Managed([]const u8).init(arena);
1855 var linker_args = std.array_list.Managed([]const u8).init(arena);
1856 var it = ClangArgIterator.init(arena, all_args);
1857 var emit_llvm = false;
1858 var needed = false;
1859 var must_link = false;
1860 var file_ext: ?Compilation.FileExt = null;
1861 while (it.has_next) {
1862 it.next() catch |err| {
1863 fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
1864 };
1865 switch (it.zig_equivalent) {
1866 .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown
1867 .o => {
1868 // We handle -o /dev/null equivalent to -fno-emit-bin because
1869 // otherwise our atomic rename into place will fail. This also
1870 // makes Zig do less work, avoiding pointless file system operations.
1871 if (mem.eql(u8, it.only_arg, "/dev/null")) {
1872 emit_bin = .no;
1873 } else {
1874 out_path = it.only_arg;
1875 }
1876 },
1877 .c, .r => c_out_mode = .object, // -c or -r
1878 .asm_only => c_out_mode = .assembly, // -S
1879 .preprocess_only => c_out_mode = .preprocessor, // -E
1880 .emit_llvm => emit_llvm = true,
1881 .x => {
1882 const lang = mem.sliceTo(it.only_arg, 0);
1883 if (mem.eql(u8, lang, "none")) {
1884 file_ext = null;
1885 } else if (Compilation.LangToExt.get(lang)) |got_ext| {
1886 file_ext = got_ext;
1887 } else {
1888 fatal("language not recognized: '{s}'", .{lang});
1889 }
1890 },
1891 .other => {
1892 try cc_argv.appendSlice(arena, it.other_args);
1893 },
1894 .positional => switch (file_ext orelse Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
1895 .assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .hpp, .hm, .hmm, .m, .mm => {
1896 try create_module.c_source_files.append(arena, .{
1897 // Populated after module creation.
1898 .owner = undefined,
1899 .src_path = it.only_arg,
1900 .ext = file_ext, // duped while parsing the args.
1901 });
1902 },
1903 .unknown, .object, .static_library, .shared_library => {
1904 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1905 .path = Path.initCwd(it.only_arg),
1906 .query = .{
1907 .must_link = must_link,
1908 .needed = needed,
1909 .preferred_mode = lib_preferred_mode,
1910 .search_strategy = lib_search_strategy,
1911 .allow_so_scripts = allow_so_scripts,
1912 },
1913 } });
1914 // We do not set `any_dyn_libs` yet because a .so file
1915 // may actually resolve to a GNU ld script which ends
1916 // up being a static library.
1917 },
1918 .res => {
1919 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1920 .path = Path.initCwd(it.only_arg),
1921 .query = .{
1922 .must_link = must_link,
1923 .needed = needed,
1924 .preferred_mode = lib_preferred_mode,
1925 .search_strategy = lib_search_strategy,
1926 .allow_so_scripts = allow_so_scripts,
1927 },
1928 } });
1929 contains_res_file = true;
1930 },
1931 .manifest => {
1932 if (manifest_file) |other| {
1933 fatal("only one manifest file can be specified, found '{s}' after previously specified manifest '{s}'", .{ it.only_arg, other });
1934 } else manifest_file = it.only_arg;
1935 },
1936 .def => {
1937 linker_module_definition_file = it.only_arg;
1938 },
1939 .rc => {
1940 try create_module.rc_source_files.append(arena, .{
1941 // Populated after module creation.
1942 .owner = undefined,
1943 .src_path = it.only_arg,
1944 });
1945 },
1946 .zig => {
1947 if (root_src_file) |other| {
1948 fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other });
1949 } else root_src_file = it.only_arg;
1950 },
1951 },
1952 .l => {
1953 // -l
1954 // We don't know whether this library is part of libc or libc++ until
1955 // we resolve the target, so we simply append to the list for now.
1956 if (mem.startsWith(u8, it.only_arg, ":")) {
1957 // -l :path/to/filename is used when callers need
1958 // more control over what's in the resulting
1959 // binary: no extra rpaths and DSO filename exactly
1960 // as provided. CGo compilation depends on this.
1961 try create_module.cli_link_inputs.append(arena, .{ .dso_exact = .{
1962 .name = it.only_arg,
1963 } });
1964 } else {
1965 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1966 .name = it.only_arg,
1967 .query = .{
1968 .must_link = must_link,
1969 .needed = needed,
1970 .weak = false,
1971 .preferred_mode = lib_preferred_mode,
1972 .search_strategy = lib_search_strategy,
1973 .allow_so_scripts = allow_so_scripts,
1974 },
1975 } });
1976 }
1977 },
1978 .ignore => {},
1979 .driver_punt => {
1980 // Never mind what we're doing, just pass the args directly. For example --help.
1981 return process.exit(try clangMain(arena, all_args));
1982 },
1983 .pic => mod_opts.pic = true,
1984 .no_pic => mod_opts.pic = false,
1985 .pie => create_module.opts.pie = true,
1986 .no_pie => create_module.opts.pie = false,
1987 .lto => {
1988 if (mem.eql(u8, it.only_arg, "flto") or
1989 mem.eql(u8, it.only_arg, "auto") or
1990 mem.eql(u8, it.only_arg, "full") or
1991 mem.eql(u8, it.only_arg, "jobserver"))
1992 {
1993 create_module.opts.lto = .full;
1994 } else if (mem.eql(u8, it.only_arg, "thin")) {
1995 create_module.opts.lto = .thin;
1996 } else {
1997 fatal("Invalid -flto mode: '{s}'. Must be 'auto', 'full', 'thin', or 'jobserver'.", .{it.only_arg});
1998 }
1999 },
2000 .no_lto => create_module.opts.lto = .none,
2001 .red_zone => mod_opts.red_zone = true,
2002 .no_red_zone => mod_opts.red_zone = false,
2003 .omit_frame_pointer => mod_opts.omit_frame_pointer = true,
2004 .no_omit_frame_pointer => mod_opts.omit_frame_pointer = false,
2005 .function_sections => function_sections = true,
2006 .no_function_sections => function_sections = false,
2007 .data_sections => data_sections = true,
2008 .no_data_sections => data_sections = false,
2009 .builtin => mod_opts.no_builtin = false,
2010 .no_builtin => mod_opts.no_builtin = true,
2011 .color_diagnostics => color = .on,
2012 .no_color_diagnostics => color = .off,
2013 .stack_check => mod_opts.stack_check = true,
2014 .no_stack_check => mod_opts.stack_check = false,
2015 .stack_protector => {
2016 if (mod_opts.stack_protector == null) {
2017 mod_opts.stack_protector = Compilation.default_stack_protector_buffer_size;
2018 }
2019 },
2020 .no_stack_protector => mod_opts.stack_protector = 0,
2021 // The way these unwind table options are processed in GCC and Clang is crazy
2022 // convoluted, and we also don't know the target triple here, so this is all
2023 // best-effort.
2024 .unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) {
2025 .none => {
2026 mod_opts.unwind_tables = .sync;
2027 },
2028 .sync, .async => {},
2029 } else {
2030 mod_opts.unwind_tables = .sync;
2031 },
2032 .no_unwind_tables => mod_opts.unwind_tables = .none,
2033 .asynchronous_unwind_tables => mod_opts.unwind_tables = .async,
2034 .no_asynchronous_unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) {
2035 .none, .sync => {},
2036 .async => {
2037 mod_opts.unwind_tables = .sync;
2038 },
2039 } else {
2040 mod_opts.unwind_tables = .sync;
2041 },
2042 .nostdlib => {
2043 create_module.opts.ensure_libc_on_non_freestanding = false;
2044 create_module.opts.ensure_libcpp_on_non_freestanding = false;
2045 },
2046 .nostdlib_cpp => create_module.opts.ensure_libcpp_on_non_freestanding = false,
2047 .shared => {
2048 create_module.opts.link_mode = .dynamic;
2049 is_shared_lib = true;
2050 },
2051 .rdynamic => create_module.opts.rdynamic = true,
2052 .wp => {
2053 var split_it = mem.splitScalar(u8, it.only_arg, ',');
2054 while (split_it.next()) |preprocessor_arg| {
2055 if (preprocessor_arg.len >= 3 and
2056 preprocessor_arg[0] == '-' and
2057 preprocessor_arg[2] != '-')
2058 {
2059 if (mem.indexOfScalar(u8, preprocessor_arg, '=')) |equals_pos| {
2060 const key = preprocessor_arg[0..equals_pos];
2061 const value = preprocessor_arg[equals_pos + 1 ..];
2062 try preprocessor_args.append(key);
2063 try preprocessor_args.append(value);
2064 continue;
2065 }
2066 }
2067 try preprocessor_args.append(preprocessor_arg);
2068 }
2069 },
2070 .wl => {
2071 var split_it = mem.splitScalar(u8, it.only_arg, ',');
2072 while (split_it.next()) |linker_arg| {
2073 // Handle nested-joined args like `-Wl,-rpath=foo`.
2074 // Must be prefixed with 1 or 2 dashes.
2075 if (linker_arg.len >= 3 and
2076 linker_arg[0] == '-' and
2077 linker_arg[2] != '-')
2078 {
2079 if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| {
2080 const key = linker_arg[0..equals_pos];
2081 const value = linker_arg[equals_pos + 1 ..];
2082 if (mem.eql(u8, key, "--build-id")) {
2083 build_id = std.zig.BuildId.parse(value) catch |err| {
2084 fatal("unable to parse --build-id style '{s}': {s}", .{
2085 value, @errorName(err),
2086 });
2087 };
2088 continue;
2089 } else if (mem.eql(u8, key, "--sort-common")) {
2090 // this ignores --sort=common=<anything>; ignoring plain --sort-common
2091 // is done below.
2092 continue;
2093 }
2094 try linker_args.append(key);
2095 try linker_args.append(value);
2096 continue;
2097 }
2098 }
2099 if (mem.eql(u8, linker_arg, "--build-id")) {
2100 build_id = .fast;
2101 } else if (mem.eql(u8, linker_arg, "--as-needed")) {
2102 needed = false;
2103 } else if (mem.eql(u8, linker_arg, "--no-as-needed")) {
2104 needed = true;
2105 } else if (mem.eql(u8, linker_arg, "-no-pie")) {
2106 create_module.opts.pie = false;
2107 } else if (mem.eql(u8, linker_arg, "--sort-common")) {
2108 // from ld.lld(1): --sort-common is ignored for GNU compatibility,
2109 // this ignores plain --sort-common
2110 } else if (mem.eql(u8, linker_arg, "--whole-archive") or
2111 mem.eql(u8, linker_arg, "-whole-archive"))
2112 {
2113 must_link = true;
2114 } else if (mem.eql(u8, linker_arg, "--no-whole-archive") or
2115 mem.eql(u8, linker_arg, "-no-whole-archive"))
2116 {
2117 must_link = false;
2118 } else if (mem.eql(u8, linker_arg, "-Bdynamic") or
2119 mem.eql(u8, linker_arg, "-dy") or
2120 mem.eql(u8, linker_arg, "-call_shared"))
2121 {
2122 lib_search_strategy = .no_fallback;
2123 lib_preferred_mode = .dynamic;
2124 } else if (mem.eql(u8, linker_arg, "-Bstatic") or
2125 mem.eql(u8, linker_arg, "-dn") or
2126 mem.eql(u8, linker_arg, "-non_shared") or
2127 mem.eql(u8, linker_arg, "-static"))
2128 {
2129 lib_search_strategy = .no_fallback;
2130 lib_preferred_mode = .static;
2131 } else if (mem.eql(u8, linker_arg, "-search_paths_first")) {
2132 lib_search_strategy = .paths_first;
2133 lib_preferred_mode = .dynamic;
2134 } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {
2135 lib_search_strategy = .mode_first;
2136 lib_preferred_mode = .dynamic;
2137 } else {
2138 try linker_args.append(linker_arg);
2139 }
2140 }
2141 },
2142 .san_cov_trace_pc_guard => create_module.opts.san_cov_trace_pc_guard = true,
2143 .san_cov => {
2144 var split_it = mem.splitScalar(u8, it.only_arg, ',');
2145 while (split_it.next()) |san_arg| {
2146 if (std.mem.eql(u8, san_arg, "trace-pc-guard")) {
2147 create_module.opts.san_cov_trace_pc_guard = true;
2148 }
2149 }
2150 try cc_argv.appendSlice(arena, it.other_args);
2151 },
2152 .no_san_cov => {
2153 var split_it = mem.splitScalar(u8, it.only_arg, ',');
2154 while (split_it.next()) |san_arg| {
2155 if (std.mem.eql(u8, san_arg, "trace-pc-guard")) {
2156 create_module.opts.san_cov_trace_pc_guard = false;
2157 }
2158 }
2159 try cc_argv.appendSlice(arena, it.other_args);
2160 },
2161 .optimize => {
2162 // Alright, what release mode do they want?
2163 const level = if (it.only_arg.len >= 1 and it.only_arg[0] == 'O') it.only_arg[1..] else it.only_arg;
2164 if (mem.eql(u8, level, "s") or
2165 mem.eql(u8, level, "z"))
2166 {
2167 mod_opts.optimize_mode = .ReleaseSmall;
2168 } else if (mem.eql(u8, level, "1") or
2169 mem.eql(u8, level, "2") or
2170 mem.eql(u8, level, "3") or
2171 mem.eql(u8, level, "4") or
2172 mem.eql(u8, level, "fast"))
2173 {
2174 mod_opts.optimize_mode = .ReleaseFast;
2175 } else if (mem.eql(u8, level, "g") or
2176 mem.eql(u8, level, "0"))
2177 {
2178 mod_opts.optimize_mode = .Debug;
2179 } else {
2180 try cc_argv.appendSlice(arena, it.other_args);
2181 }
2182 },
2183 .debug => {
2184 mod_opts.strip = false;
2185 if (mem.eql(u8, it.only_arg, "g")) {
2186 // We handled with strip = false above.
2187 } else if (mem.eql(u8, it.only_arg, "g1") or
2188 mem.eql(u8, it.only_arg, "gline-tables-only"))
2189 {
2190 // We handled with strip = false above. but we also want reduced debug info.
2191 try cc_argv.append(arena, "-gline-tables-only");
2192 } else {
2193 try cc_argv.appendSlice(arena, it.other_args);
2194 }
2195 },
2196 .gdwarf32 => {
2197 mod_opts.strip = false;
2198 create_module.opts.debug_format = .{ .dwarf = .@"32" };
2199 },
2200 .gdwarf64 => {
2201 mod_opts.strip = false;
2202 create_module.opts.debug_format = .{ .dwarf = .@"64" };
2203 },
2204 .sanitize, .no_sanitize => |t| {
2205 const enable = t == .sanitize;
2206 var san_it = std.mem.splitScalar(u8, it.only_arg, ',');
2207 var recognized_any = false;
2208 while (san_it.next()) |sub_arg| {
2209 if (mem.eql(u8, sub_arg, "undefined")) {
2210 mod_opts.sanitize_c = if (enable) .full else .off;
2211 recognized_any = true;
2212 } else if (mem.eql(u8, sub_arg, "thread")) {
2213 mod_opts.sanitize_thread = enable;
2214 recognized_any = true;
2215 } else if (mem.eql(u8, sub_arg, "fuzzer") or mem.eql(u8, sub_arg, "fuzzer-no-link")) {
2216 mod_opts.fuzz = enable;
2217 recognized_any = true;
2218 }
2219 }
2220 if (!recognized_any) {
2221 try cc_argv.appendSlice(arena, it.other_args);
2222 }
2223 },
2224 .sanitize_trap, .no_sanitize_trap => |t| {
2225 const enable = t == .sanitize_trap;
2226 var san_it = std.mem.splitScalar(u8, it.only_arg, ',');
2227 var recognized_any = false;
2228 while (san_it.next()) |sub_arg| {
2229 // This logic doesn't match Clang 1:1, but it's probably good enough, and avoids
2230 // significantly complicating the resolution of the options.
2231 if (mem.eql(u8, sub_arg, "undefined")) {
2232 if (mod_opts.sanitize_c) |sc| switch (sc) {
2233 .off => if (enable) {
2234 mod_opts.sanitize_c = .trap;
2235 },
2236 .trap => if (!enable) {
2237 mod_opts.sanitize_c = .full;
2238 },
2239 .full => if (enable) {
2240 mod_opts.sanitize_c = .trap;
2241 },
2242 } else {
2243 if (enable) {
2244 mod_opts.sanitize_c = .trap;
2245 } else {
2246 // This means we were passed `-fno-sanitize-trap=undefined` and nothing else. In
2247 // this case, ideally, we should use whatever value `sanitize_c` resolves to by
2248 // default, except change `trap` to `full`. However, we don't yet know what
2249 // `sanitize_c` will resolve to! So we either have to pick `off` or `full`.
2250 //
2251 // `full` has the potential to be problematic if `optimize_mode` turns out to
2252 // be `ReleaseFast`/`ReleaseSmall` because the user will get a slower and larger
2253 // binary than expected. On the other hand, if `optimize_mode` turns out to be
2254 // `Debug`/`ReleaseSafe`, `off` would mean UBSan would unexpectedly be disabled.
2255 //
2256 // `off` seems very slightly less bad, so let's go with that.
2257 mod_opts.sanitize_c = .off;
2258 }
2259 }
2260 recognized_any = true;
2261 }
2262 }
2263 if (!recognized_any) {
2264 try cc_argv.appendSlice(arena, it.other_args);
2265 }
2266 },
2267 .linker_script => linker_script = it.only_arg,
2268 .verbose => {
2269 verbose_link = true;
2270 // Have Clang print more infos, some tools such as CMake
2271 // parse this to discover any implicit include and
2272 // library dir to look-up into.
2273 try cc_argv.append(arena, "-v");
2274 },
2275 .dry_run => {
2276 // This flag means "dry run". Clang will not actually output anything
2277 // to the file system.
2278 verbose_link = true;
2279 disable_c_depfile = true;
2280 try cc_argv.append(arena, "-###");
2281 },
2282 .for_linker => try linker_args.append(it.only_arg),
2283 .linker_input_z => {
2284 try linker_args.append("-z");
2285 try linker_args.append(it.only_arg);
2286 },
2287 .lib_dir => try create_module.lib_dir_args.append(arena, it.only_arg),
2288 .mcpu => target_mcpu = it.only_arg,
2289 .m => try create_module.llvm_m_args.append(arena, it.only_arg),
2290 .dep_file => {
2291 disable_c_depfile = true;
2292 try cc_argv.appendSlice(arena, it.other_args);
2293 },
2294 .dep_file_to_stdout => { // -M, -MM
2295 // "Like -MD, but also implies -E and writes to stdout by default"
2296 // "Like -MMD, but also implies -E and writes to stdout by default"
2297 c_out_mode = .preprocessor;
2298 disable_c_depfile = true;
2299 try cc_argv.appendSlice(arena, it.other_args);
2300 },
2301 .framework_dir => try create_module.framework_dirs.append(arena, it.only_arg),
2302 .framework => try create_module.frameworks.put(arena, it.only_arg, .{}),
2303 .nostdlibinc => create_module.want_native_include_dirs = false,
2304 .strip => mod_opts.strip = true,
2305 .exec_model => {
2306 create_module.opts.wasi_exec_model = parseWasiExecModel(it.only_arg);
2307 },
2308 .sysroot => {
2309 create_module.sysroot = it.only_arg;
2310 },
2311 .entry => {
2312 entry = .{ .named = it.only_arg };
2313 },
2314 .force_undefined_symbol => {
2315 try force_undefined_symbols.put(arena, it.only_arg, {});
2316 },
2317 .force_load_objc => force_load_objc = true,
2318 .mingw_unicode_entry_point => mingw_unicode_entry_point = true,
2319 .weak_library => try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2320 .name = it.only_arg,
2321 .query = .{
2322 .needed = false,
2323 .weak = true,
2324 .preferred_mode = lib_preferred_mode,
2325 .search_strategy = lib_search_strategy,
2326 .allow_so_scripts = allow_so_scripts,
2327 },
2328 } }),
2329 .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),
2330 .headerpad_max_install_names => headerpad_max_install_names = true,
2331 .compress_debug_sections => {
2332 if (it.only_arg.len == 0) {
2333 linker_compress_debug_sections = .zlib;
2334 } else {
2335 linker_compress_debug_sections = std.meta.stringToEnum(std.zig.CompressDebugSections, it.only_arg) orelse {
2336 fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{it.only_arg});
2337 };
2338 }
2339 },
2340 .install_name => {
2341 install_name = it.only_arg;
2342 },
2343 .undefined => {
2344 if (mem.eql(u8, "dynamic_lookup", it.only_arg)) {
2345 linker_allow_shlib_undefined = true;
2346 } else if (mem.eql(u8, "error", it.only_arg)) {
2347 linker_allow_shlib_undefined = false;
2348 } else {
2349 fatal("unsupported -undefined option '{s}'", .{it.only_arg});
2350 }
2351 },
2352 .rtlib => {
2353 // Unlike Clang, we support `none` for explicitly omitting compiler-rt.
2354 if (mem.eql(u8, "none", it.only_arg)) {
2355 want_compiler_rt = false;
2356 } else if (mem.eql(u8, "compiler-rt", it.only_arg) or
2357 mem.eql(u8, "libgcc", it.only_arg))
2358 {
2359 want_compiler_rt = true;
2360 } else {
2361 // Note that we don't support `platform`.
2362 fatal("unsupported -rtlib option '{s}'", .{it.only_arg});
2363 }
2364 },
2365 .static => {
2366 create_module.opts.link_mode = .static;
2367 lib_preferred_mode = .static;
2368 lib_search_strategy = .no_fallback;
2369 },
2370 .dynamic => {
2371 create_module.opts.link_mode = .dynamic;
2372 lib_preferred_mode = .dynamic;
2373 lib_search_strategy = .mode_first;
2374 },
2375 }
2376 }
2377 // Parse linker args.
2378 var linker_args_it = ArgsIterator{
2379 .args = linker_args.items,
2380 };
2381 while (linker_args_it.next()) |arg| {
2382 if (mem.eql(u8, arg, "-soname") or
2383 mem.eql(u8, arg, "--soname"))
2384 {
2385 const name = linker_args_it.nextOrFatal();
2386 soname = .{ .yes = name };
2387 // Use it as --name.
2388 // Example: libsoundio.so.2
2389 var prefix: usize = 0;
2390 if (mem.startsWith(u8, name, "lib")) {
2391 prefix = 3;
2392 }
2393 var end: usize = name.len;
2394 if (mem.endsWith(u8, name, ".so")) {
2395 end -= 3;
2396 } else {
2397 var found_digit = false;
2398 while (end > 0 and std.ascii.isDigit(name[end - 1])) {
2399 found_digit = true;
2400 end -= 1;
2401 }
2402 if (found_digit and end > 0 and name[end - 1] == '.') {
2403 end -= 1;
2404 } else {
2405 end = name.len;
2406 }
2407 if (mem.endsWith(u8, name[prefix..end], ".so")) {
2408 end -= 3;
2409 }
2410 }
2411 provided_name = name[prefix..end];
2412 } else if (mem.eql(u8, arg, "-rpath") or mem.eql(u8, arg, "--rpath") or mem.eql(u8, arg, "-R")) {
2413 try create_module.rpath_list.append(arena, linker_args_it.nextOrFatal());
2414 } else if (mem.eql(u8, arg, "--subsystem")) {
2415 subsystem = try parseSubsystem(linker_args_it.nextOrFatal());
2416 } else if (mem.eql(u8, arg, "-I") or
2417 mem.eql(u8, arg, "--dynamic-linker") or
2418 mem.eql(u8, arg, "-dynamic-linker"))
2419 {
2420 create_module.dynamic_linker = linker_args_it.nextOrFatal();
2421 } else if (mem.eql(u8, arg, "-I") or
2422 mem.eql(u8, arg, "--no-dynamic-linker") or
2423 mem.eql(u8, arg, "-no-dynamic-linker"))
2424 {
2425 create_module.dynamic_linker = "";
2426 } else if (mem.eql(u8, arg, "-E") or
2427 mem.eql(u8, arg, "--export-dynamic") or
2428 mem.eql(u8, arg, "-export-dynamic"))
2429 {
2430 create_module.opts.rdynamic = true;
2431 } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
2432 version_script = linker_args_it.nextOrFatal();
2433 } else if (mem.eql(u8, arg, "--undefined-version")) {
2434 linker_allow_undefined_version = true;
2435 } else if (mem.eql(u8, arg, "--no-undefined-version")) {
2436 linker_allow_undefined_version = false;
2437 } else if (mem.eql(u8, arg, "--enable-new-dtags")) {
2438 linker_enable_new_dtags = true;
2439 } else if (mem.eql(u8, arg, "--disable-new-dtags")) {
2440 linker_enable_new_dtags = false;
2441 } else if (mem.eql(u8, arg, "-O")) {
2442 linker_optimization = linker_args_it.nextOrFatal();
2443 } else if (mem.cutPrefix(u8, arg, "-O")) |rest| {
2444 linker_optimization = rest;
2445 } else if (mem.eql(u8, arg, "-pagezero_size")) {
2446 const next_arg = linker_args_it.nextOrFatal();
2447 pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
2448 fatal("unable to parse pagezero size '{s}': {s}", .{ next_arg, @errorName(err) });
2449 };
2450 } else if (mem.eql(u8, arg, "-headerpad")) {
2451 const next_arg = linker_args_it.nextOrFatal();
2452 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
2453 fatal("unable to parse headerpad size '{s}': {s}", .{ next_arg, @errorName(err) });
2454 };
2455 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
2456 headerpad_max_install_names = true;
2457 } else if (mem.eql(u8, arg, "-dead_strip")) {
2458 linker_gc_sections = true;
2459 } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
2460 dead_strip_dylibs = true;
2461 } else if (mem.eql(u8, arg, "-ObjC")) {
2462 force_load_objc = true;
2463 } else if (mem.eql(u8, arg, "--no-undefined")) {
2464 linker_z_defs = true;
2465 } else if (mem.eql(u8, arg, "--gc-sections")) {
2466 linker_gc_sections = true;
2467 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
2468 linker_gc_sections = false;
2469 } else if (mem.eql(u8, arg, "--print-gc-sections")) {
2470 linker_print_gc_sections = true;
2471 } else if (mem.eql(u8, arg, "--print-icf-sections")) {
2472 linker_print_icf_sections = true;
2473 } else if (mem.eql(u8, arg, "--print-map")) {
2474 linker_print_map = true;
2475 } else if (mem.eql(u8, arg, "--sort-section")) {
2476 const arg1 = linker_args_it.nextOrFatal();
2477 linker_sort_section = std.meta.stringToEnum(link.File.Lld.Elf.SortSection, arg1) orelse {
2478 fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1});
2479 };
2480 } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
2481 mem.eql(u8, arg, "-allow-shlib-undefined"))
2482 {
2483 linker_allow_shlib_undefined = true;
2484 } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
2485 mem.eql(u8, arg, "-no-allow-shlib-undefined"))
2486 {
2487 linker_allow_shlib_undefined = false;
2488 } else if (mem.eql(u8, arg, "-Bsymbolic")) {
2489 linker_bind_global_refs_locally = true;
2490 } else if (mem.eql(u8, arg, "--import-memory")) {
2491 create_module.opts.import_memory = true;
2492 } else if (mem.eql(u8, arg, "--export-memory")) {
2493 create_module.opts.export_memory = true;
2494 } else if (mem.eql(u8, arg, "--import-symbols")) {
2495 linker_import_symbols = true;
2496 } else if (mem.eql(u8, arg, "--import-table")) {
2497 linker_import_table = true;
2498 } else if (mem.eql(u8, arg, "--export-table")) {
2499 linker_export_table = true;
2500 } else if (mem.eql(u8, arg, "--no-entry")) {
2501 entry = .disabled;
2502 } else if (mem.eql(u8, arg, "--initial-memory")) {
2503 const next_arg = linker_args_it.nextOrFatal();
2504 linker_initial_memory = std.fmt.parseUnsigned(u32, next_arg, 10) catch |err| {
2505 fatal("unable to parse initial memory size '{s}': {s}", .{ next_arg, @errorName(err) });
2506 };
2507 } else if (mem.eql(u8, arg, "--max-memory")) {
2508 const next_arg = linker_args_it.nextOrFatal();
2509 linker_max_memory = std.fmt.parseUnsigned(u32, next_arg, 10) catch |err| {
2510 fatal("unable to parse max memory size '{s}': {s}", .{ next_arg, @errorName(err) });
2511 };
2512 } else if (mem.eql(u8, arg, "--shared-memory")) {
2513 create_module.opts.shared_memory = true;
2514 } else if (mem.eql(u8, arg, "--global-base")) {
2515 const next_arg = linker_args_it.nextOrFatal();
2516 linker_global_base = std.fmt.parseUnsigned(u32, next_arg, 10) catch |err| {
2517 fatal("unable to parse global base '{s}': {s}", .{ next_arg, @errorName(err) });
2518 };
2519 } else if (mem.eql(u8, arg, "--export")) {
2520 try linker_export_symbol_names.append(arena, linker_args_it.nextOrFatal());
2521 } else if (mem.eql(u8, arg, "--compress-debug-sections")) {
2522 const arg1 = linker_args_it.nextOrFatal();
2523 linker_compress_debug_sections = std.meta.stringToEnum(std.zig.CompressDebugSections, arg1) orelse {
2524 fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{arg1});
2525 };
2526 } else if (mem.cutPrefix(u8, arg, "-z")) |z_rest| {
2527 const z_arg = if (z_rest.len == 0) linker_args_it.nextOrFatal() else z_rest;
2528 if (mem.eql(u8, z_arg, "nodelete")) {
2529 linker_z_nodelete = true;
2530 } else if (mem.eql(u8, z_arg, "notext")) {
2531 linker_z_notext = true;
2532 } else if (mem.eql(u8, z_arg, "defs")) {
2533 linker_z_defs = true;
2534 } else if (mem.eql(u8, z_arg, "undefs")) {
2535 linker_z_defs = false;
2536 } else if (mem.eql(u8, z_arg, "origin")) {
2537 linker_z_origin = true;
2538 } else if (mem.eql(u8, z_arg, "nocopyreloc")) {
2539 linker_z_nocopyreloc = true;
2540 } else if (mem.eql(u8, z_arg, "noexecstack")) {
2541 // noexecstack is the default when linking with LLD
2542 } else if (mem.eql(u8, z_arg, "now")) {
2543 linker_z_now = true;
2544 } else if (mem.eql(u8, z_arg, "lazy")) {
2545 linker_z_now = false;
2546 } else if (mem.eql(u8, z_arg, "relro")) {
2547 linker_z_relro = true;
2548 } else if (mem.eql(u8, z_arg, "norelro")) {
2549 linker_z_relro = false;
2550 } else if (mem.cutPrefix(u8, z_arg, "stack-size=")) |rest| {
2551 stack_size = parseStackSize(rest);
2552 } else if (prefixedIntArg(z_arg, "common-page-size=")) |int| {
2553 linker_z_common_page_size = int;
2554 } else if (prefixedIntArg(z_arg, "max-page-size=")) |int| {
2555 linker_z_max_page_size = int;
2556 } else {
2557 fatal("unsupported linker extension flag: -z {s}", .{z_arg});
2558 }
2559 } else if (mem.eql(u8, arg, "--major-image-version")) {
2560 const major = linker_args_it.nextOrFatal();
2561 version.major = std.fmt.parseUnsigned(u32, major, 10) catch |err| {
2562 fatal("unable to parse major image version '{s}': {s}", .{ major, @errorName(err) });
2563 };
2564 have_version = true;
2565 } else if (mem.eql(u8, arg, "--minor-image-version")) {
2566 const minor = linker_args_it.nextOrFatal();
2567 version.minor = std.fmt.parseUnsigned(u32, minor, 10) catch |err| {
2568 fatal("unable to parse minor image version '{s}': {s}", .{ minor, @errorName(err) });
2569 };
2570 have_version = true;
2571 } else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) {
2572 entry = .{ .named = linker_args_it.nextOrFatal() };
2573 } else if (mem.eql(u8, arg, "-u")) {
2574 try force_undefined_symbols.put(arena, linker_args_it.nextOrFatal(), {});
2575 } else if (mem.eql(u8, arg, "-x") or mem.eql(u8, arg, "--discard-all")) {
2576 discard_local_symbols = true;
2577 } else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) {
2578 stack_size = parseStackSize(linker_args_it.nextOrFatal());
2579 } else if (mem.eql(u8, arg, "--image-base")) {
2580 image_base = parseImageBase(linker_args_it.nextOrFatal());
2581 } else if (mem.eql(u8, arg, "--enable-auto-image-base") or
2582 mem.eql(u8, arg, "--disable-auto-image-base"))
2583 {
2584 // `--enable-auto-image-base` is a flag that binutils added in ~2000 for MinGW.
2585 // It does a hash of the file and uses that as part of the image base value.
2586 // Presumably the idea was to avoid DLLs needing to be relocated when loaded.
2587 // This is practically irrelevant today as all PEs produced since Windows Vista
2588 // have ASLR enabled by default anyway, and Windows 10+ has Mandatory ASLR which
2589 // doesn't even care what the PE file wants and relocates it anyway.
2590 //
2591 // Unfortunately, Libtool hardcodes usage of this archaic flag when targeting
2592 // MinGW, so to make `zig cc` for that use case work, accept and ignore the
2593 // flag, and warn the user that it has no effect.
2594 warn("auto-image-base options are unimplemented and ignored", .{});
2595 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
2596 linker_script = linker_args_it.nextOrFatal();
2597 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
2598 link_eh_frame_hdr = true;
2599 } else if (mem.eql(u8, arg, "--no-eh-frame-hdr")) {
2600 link_eh_frame_hdr = false;
2601 } else if (mem.eql(u8, arg, "--tsaware")) {
2602 linker_tsaware = true;
2603 } else if (mem.eql(u8, arg, "--nxcompat")) {
2604 linker_nxcompat = true;
2605 } else if (mem.eql(u8, arg, "--dynamicbase")) {
2606 linker_dynamicbase = true;
2607 } else if (mem.eql(u8, arg, "--no-dynamicbase")) {
2608 linker_dynamicbase = false;
2609 } else if (mem.eql(u8, arg, "--high-entropy-va")) {
2610 // This option does not do anything.
2611 } else if (mem.eql(u8, arg, "--export-all-symbols")) {
2612 create_module.opts.rdynamic = true;
2613 } else if (mem.eql(u8, arg, "--color-diagnostics") or
2614 mem.eql(u8, arg, "--color-diagnostics=always"))
2615 {
2616 color = .on;
2617 } else if (mem.eql(u8, arg, "--no-color-diagnostics") or
2618 mem.eql(u8, arg, "--color-diagnostics=never"))
2619 {
2620 color = .off;
2621 } else if (mem.eql(u8, arg, "-s") or mem.eql(u8, arg, "--strip-all") or
2622 mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--strip-debug"))
2623 {
2624 // -s, --strip-all Strip all symbols
2625 // -S, --strip-debug Strip debugging symbols
2626 mod_opts.strip = true;
2627 } else if (mem.eql(u8, arg, "--start-group") or
2628 mem.eql(u8, arg, "--end-group"))
2629 {
2630 // We don't need to care about these because these args are
2631 // for resolving circular dependencies but our linker takes
2632 // care of this without explicit args.
2633 } else if (mem.eql(u8, arg, "--major-os-version") or
2634 mem.eql(u8, arg, "--minor-os-version"))
2635 {
2636 // This option does not do anything.
2637 _ = linker_args_it.nextOrFatal();
2638 } else if (mem.eql(u8, arg, "--major-subsystem-version")) {
2639 const major = linker_args_it.nextOrFatal();
2640 major_subsystem_version = std.fmt.parseUnsigned(u16, major, 10) catch |err| {
2641 fatal("unable to parse major subsystem version '{s}': {s}", .{
2642 major, @errorName(err),
2643 });
2644 };
2645 } else if (mem.eql(u8, arg, "--minor-subsystem-version")) {
2646 const minor = linker_args_it.nextOrFatal();
2647 minor_subsystem_version = std.fmt.parseUnsigned(u16, minor, 10) catch |err| {
2648 fatal("unable to parse minor subsystem version '{s}': {s}", .{
2649 minor, @errorName(err),
2650 });
2651 };
2652 } else if (mem.eql(u8, arg, "-framework")) {
2653 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{});
2654 } else if (mem.eql(u8, arg, "-weak_framework")) {
2655 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .weak = true });
2656 } else if (mem.eql(u8, arg, "-needed_framework")) {
2657 try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });
2658 } else if (mem.eql(u8, arg, "-needed_library")) {
2659 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2660 .name = linker_args_it.nextOrFatal(),
2661 .query = .{
2662 .weak = false,
2663 .needed = true,
2664 .preferred_mode = lib_preferred_mode,
2665 .search_strategy = lib_search_strategy,
2666 .allow_so_scripts = allow_so_scripts,
2667 },
2668 } });
2669 } else if (mem.cutPrefix(u8, arg, "-weak-l")) |rest| {
2670 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2671 .name = rest,
2672 .query = .{
2673 .weak = true,
2674 .needed = false,
2675 .preferred_mode = lib_preferred_mode,
2676 .search_strategy = lib_search_strategy,
2677 .allow_so_scripts = allow_so_scripts,
2678 },
2679 } });
2680 } else if (mem.eql(u8, arg, "-weak_library")) {
2681 try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2682 .name = linker_args_it.nextOrFatal(),
2683 .query = .{
2684 .weak = true,
2685 .needed = false,
2686 .preferred_mode = lib_preferred_mode,
2687 .search_strategy = lib_search_strategy,
2688 .allow_so_scripts = allow_so_scripts,
2689 },
2690 } });
2691 } else if (mem.eql(u8, arg, "-compatibility_version")) {
2692 const compat_version = linker_args_it.nextOrFatal();
2693 compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {
2694 fatal("unable to parse -compatibility_version '{s}': {s}", .{ compat_version, @errorName(err) });
2695 };
2696 } else if (mem.eql(u8, arg, "-current_version")) {
2697 const curr_version = linker_args_it.nextOrFatal();
2698 version = std.SemanticVersion.parse(curr_version) catch |err| {
2699 fatal("unable to parse -current_version '{s}': {s}", .{ curr_version, @errorName(err) });
2700 };
2701 have_version = true;
2702 } else if (mem.eql(u8, arg, "--out-implib") or
2703 mem.eql(u8, arg, "-implib"))
2704 {
2705 emit_implib = .{ .yes = linker_args_it.nextOrFatal() };
2706 emit_implib_arg_provided = true;
2707 } else if (mem.eql(u8, arg, "-Brepro") or mem.eql(u8, arg, "/Brepro")) {
2708 linker_repro = true;
2709 } else if (mem.eql(u8, arg, "-undefined")) {
2710 const lookup_type = linker_args_it.nextOrFatal();
2711 if (mem.eql(u8, "dynamic_lookup", lookup_type)) {
2712 linker_allow_shlib_undefined = true;
2713 } else if (mem.eql(u8, "error", lookup_type)) {
2714 linker_allow_shlib_undefined = false;
2715 } else {
2716 fatal("unsupported -undefined option '{s}'", .{lookup_type});
2717 }
2718 } else if (mem.eql(u8, arg, "-install_name")) {
2719 install_name = linker_args_it.nextOrFatal();
2720 } else if (mem.eql(u8, arg, "-force_load")) {
2721 try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
2722 .path = Path.initCwd(linker_args_it.nextOrFatal()),
2723 .query = .{
2724 .must_link = true,
2725 .preferred_mode = .static,
2726 .search_strategy = .no_fallback,
2727 },
2728 } });
2729 } else if (mem.eql(u8, arg, "-hash-style") or
2730 mem.eql(u8, arg, "--hash-style"))
2731 {
2732 const next_arg = linker_args_it.nextOrFatal();
2733 hash_style = std.meta.stringToEnum(link.File.Lld.Elf.HashStyle, next_arg) orelse {
2734 fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{
2735 next_arg,
2736 });
2737 };
2738 } else if (mem.eql(u8, arg, "-wrap")) {
2739 const next_arg = linker_args_it.nextOrFatal();
2740 try symbol_wrap_set.put(arena, next_arg, {});
2741 } else if (mem.startsWith(u8, arg, "/subsystem:")) {
2742 var split_it = mem.splitBackwardsScalar(u8, arg, ':');
2743 subsystem = try parseSubsystem(split_it.first());
2744 } else if (mem.startsWith(u8, arg, "/implib:")) {
2745 var split_it = mem.splitBackwardsScalar(u8, arg, ':');
2746 emit_implib = .{ .yes = split_it.first() };
2747 emit_implib_arg_provided = true;
2748 } else if (mem.startsWith(u8, arg, "/pdb:")) {
2749 var split_it = mem.splitBackwardsScalar(u8, arg, ':');
2750 pdb_out_path = split_it.first();
2751 } else if (mem.startsWith(u8, arg, "/version:")) {
2752 var split_it = mem.splitBackwardsScalar(u8, arg, ':');
2753 const version_arg = split_it.first();
2754 version = std.SemanticVersion.parse(version_arg) catch |err| {
2755 fatal("unable to parse /version '{s}': {s}", .{ arg, @errorName(err) });
2756 };
2757 have_version = true;
2758 } else if (mem.eql(u8, arg, "-V")) {
2759 warn("ignoring request for supported emulations: unimplemented", .{});
2760 } else if (mem.eql(u8, arg, "-v")) {
2761 try fs.File.stdout().writeAll("zig ld " ++ build_options.version ++ "\n");
2762 } else if (mem.eql(u8, arg, "--version")) {
2763 try fs.File.stdout().writeAll("zig ld " ++ build_options.version ++ "\n");
2764 process.exit(0);
2765 } else {
2766 fatal("unsupported linker arg: {s}", .{arg});
2767 }
2768 }
2769
2770 // Parse preprocessor args.
2771 var preprocessor_args_it = ArgsIterator{
2772 .args = preprocessor_args.items,
2773 };
2774 while (preprocessor_args_it.next()) |arg| {
2775 if (mem.eql(u8, arg, "-MD") or mem.eql(u8, arg, "-MMD") or mem.eql(u8, arg, "-MT")) {
2776 disable_c_depfile = true;
2777 const cc_arg = try std.fmt.allocPrint(arena, "-Wp,{s},{s}", .{ arg, preprocessor_args_it.nextOrFatal() });
2778 try cc_argv.append(arena, cc_arg);
2779 } else {
2780 fatal("unsupported preprocessor arg: {s}", .{arg});
2781 }
2782 }
2783
2784 if (mod_opts.sanitize_c) |wsc| {
2785 if (wsc != .off and mod_opts.optimize_mode == .ReleaseFast) {
2786 mod_opts.optimize_mode = .ReleaseSafe;
2787 }
2788 }
2789
2790 // precompiled header syntax: "zig cc -x c-header test.h -o test.pch"
2791 const emit_pch = ((file_ext == .h or file_ext == .hpp or file_ext == .hm or file_ext == .hmm) and c_out_mode == null);
2792 if (emit_pch)
2793 c_out_mode = .preprocessor;
2794
2795 switch (c_out_mode orelse .link) {
2796 .link => {
2797 create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe;
2798 if (emit_bin != .no) {
2799 emit_bin = if (out_path) |p| .{ .yes = p } else .yes_a_out;
2800 }
2801 if (emit_llvm) {
2802 fatal("-emit-llvm cannot be used when linking", .{});
2803 }
2804 },
2805 .object => {
2806 create_module.opts.output_mode = .Obj;
2807 if (emit_llvm) {
2808 emit_bin = .no;
2809 if (out_path) |p| {
2810 emit_llvm_bc = .{ .yes = p };
2811 } else {
2812 emit_llvm_bc = .yes_default_path;
2813 }
2814 } else {
2815 if (out_path) |p| {
2816 emit_bin = .{ .yes = p };
2817 } else {
2818 emit_bin = .yes_default_path;
2819 }
2820 }
2821 },
2822 .assembly => {
2823 create_module.opts.output_mode = .Obj;
2824 emit_bin = .no;
2825 if (emit_llvm) {
2826 if (out_path) |p| {
2827 emit_llvm_ir = .{ .yes = p };
2828 } else {
2829 emit_llvm_ir = .yes_default_path;
2830 }
2831 } else {
2832 if (out_path) |p| {
2833 emit_asm = .{ .yes = p };
2834 } else {
2835 emit_asm = .yes_default_path;
2836 }
2837 }
2838 },
2839 .preprocessor => {
2840 create_module.opts.output_mode = .Obj;
2841 // An error message is generated when there is more than 1 C source file.
2842 if (create_module.c_source_files.items.len != 1) {
2843 // For example `zig cc` and no args should print the "no input files" message.
2844 return process.exit(try clangMain(arena, all_args));
2845 }
2846 if (emit_pch) {
2847 emit_bin = if (out_path) |p| .{ .yes = p } else .yes_default_path;
2848 clang_preprocessor_mode = .pch;
2849 } else {
2850 // If the output path is "-" (stdout), then we need to emit the preprocessed output to stdout
2851 // like "clang -E main.c -o -" does.
2852 if (out_path != null and !mem.eql(u8, out_path.?, "-")) {
2853 emit_bin = .{ .yes = out_path.? };
2854 clang_preprocessor_mode = .yes;
2855 } else {
2856 emit_bin = .no;
2857 clang_preprocessor_mode = .stdout;
2858 }
2859 }
2860 },
2861 }
2862 if (create_module.c_source_files.items.len == 0 and
2863 !anyObjectLinkInputs(create_module.cli_link_inputs.items) and
2864 root_src_file == null)
2865 {
2866 // For example `zig cc` and no args should print the "no input files" message.
2867 // There could be other reasons to punt to clang, for example, --help.
2868 return process.exit(try clangMain(arena, all_args));
2869 }
2870 },
2871 }
2872
2873 if (arg_mode == .zig_test_obj and !test_no_exec and listen == .none) {
2874 fatal("test-obj requires --test-no-exec", .{});
2875 }
2876
2877 if (time_report and listen == .none) {
2878 fatal("--time-report requires --listen", .{});
2879 }
2880
2881 if (arg_mode == .translate_c and create_module.c_source_files.items.len != 1) {
2882 fatal("translate-c expects exactly 1 source file (found {d})", .{create_module.c_source_files.items.len});
2883 }
2884
2885 if (show_builtin and root_src_file == null) {
2886 // Without this, there will be no main module created and no zig
2887 // compilation unit, and therefore also no builtin.zig contents
2888 // created.
2889 root_src_file = "builtin.zig";
2890 }
2891
2892 implicit_root_mod: {
2893 const src_path = b: {
2894 if (root_src_file) |src_path| {
2895 if (create_module.modules.count() != 0) {
2896 fatal("main module provided both by '-M{s}={s}{c}{s}' and by positional argument '{s}'", .{
2897 create_module.modules.keys()[0],
2898 create_module.modules.values()[0].root_path,
2899 fs.path.sep,
2900 create_module.modules.values()[0].root_src_path,
2901 src_path,
2902 });
2903 }
2904 create_module.opts.have_zcu = true;
2905 break :b src_path;
2906 }
2907
2908 if (create_module.modules.count() != 0)
2909 break :implicit_root_mod;
2910
2911 if (create_module.c_source_files.items.len >= 1)
2912 break :b create_module.c_source_files.items[0].src_path;
2913
2914 for (create_module.cli_link_inputs.items) |unresolved_link_input| switch (unresolved_link_input) {
2915 // Intentionally includes dynamic libraries provided by file path.
2916 .path_query => |pq| break :b pq.path.sub_path,
2917 else => continue,
2918 };
2919
2920 if (emit_bin == .yes)
2921 break :b emit_bin.yes;
2922
2923 if (create_module.rc_source_files.items.len >= 1)
2924 break :b create_module.rc_source_files.items[0].src_path;
2925
2926 if (arg_mode == .run)
2927 fatal("`zig run` expects at least one positional argument", .{});
2928
2929 fatal("expected a positional argument, -femit-bin=[path], --show-builtin, or --name [name]", .{});
2930
2931 break :implicit_root_mod;
2932 };
2933
2934 // See duplicate logic: ModCreationGlobalFlags
2935 if (mod_opts.single_threaded == false)
2936 create_module.opts.any_non_single_threaded = true;
2937 if (mod_opts.sanitize_thread == true)
2938 create_module.opts.any_sanitize_thread = true;
2939 if (mod_opts.sanitize_c) |sc| switch (sc) {
2940 .off => {},
2941 .trap => if (create_module.opts.any_sanitize_c == .off) {
2942 create_module.opts.any_sanitize_c = .trap;
2943 },
2944 .full => create_module.opts.any_sanitize_c = .full,
2945 };
2946 if (mod_opts.fuzz == true)
2947 create_module.opts.any_fuzz = true;
2948 if (mod_opts.unwind_tables) |uwt| switch (uwt) {
2949 .none => {},
2950 .sync, .async => create_module.opts.any_unwind_tables = true,
2951 };
2952 if (mod_opts.strip == false)
2953 create_module.opts.any_non_stripped = true;
2954 if (mod_opts.error_tracing == true)
2955 create_module.opts.any_error_tracing = true;
2956
2957 const name = switch (arg_mode) {
2958 .zig_test => "test",
2959 .build, .cc, .cpp, .translate_c, .zig_test_obj, .run => fs.path.stem(fs.path.basename(src_path)),
2960 };
2961
2962 try create_module.modules.put(arena, name, .{
2963 .root_path = fs.path.dirname(src_path) orelse ".",
2964 .root_src_path = fs.path.basename(src_path),
2965 .cc_argv = try cc_argv.toOwnedSlice(arena),
2966 .inherited = mod_opts,
2967 .target_arch_os_abi = target_arch_os_abi,
2968 .target_mcpu = target_mcpu,
2969 .deps = try deps.toOwnedSlice(arena),
2970 .resolved = null,
2971 .c_source_files_start = c_source_files_owner_index,
2972 .c_source_files_end = create_module.c_source_files.items.len,
2973 .rc_source_files_start = rc_source_files_owner_index,
2974 .rc_source_files_end = create_module.rc_source_files.items.len,
2975 });
2976 cssan.reset();
2977 mod_opts = .{};
2978 target_arch_os_abi = null;
2979 target_mcpu = null;
2980 c_source_files_owner_index = create_module.c_source_files.items.len;
2981 rc_source_files_owner_index = create_module.rc_source_files.items.len;
2982 }
2983
2984 if (!create_module.opts.have_zcu and create_module.opts.is_test) {
2985 fatal("`zig test` expects a zig source file argument", .{});
2986 }
2987
2988 if (c_source_files_owner_index != create_module.c_source_files.items.len) {
2989 fatal("C source file '{s}' has no parent module", .{
2990 create_module.c_source_files.items[c_source_files_owner_index].src_path,
2991 });
2992 }
2993
2994 if (rc_source_files_owner_index != create_module.rc_source_files.items.len) {
2995 fatal("resource file '{s}' has no parent module", .{
2996 create_module.rc_source_files.items[rc_source_files_owner_index].src_path,
2997 });
2998 }
2999
3000 const self_exe_path = switch (native_os) {
3001 .wasi => {},
3002 else => fs.selfExePathAlloc(arena) catch |err| {
3003 fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
3004 },
3005 };
3006
3007 // This `init` calls `fatal` on error.
3008 var dirs: Compilation.Directories = .init(
3009 arena,
3010 override_lib_dir,
3011 override_global_cache_dir,
3012 s: {
3013 if (override_local_cache_dir) |p| break :s .{ .override = p };
3014 break :s switch (arg_mode) {
3015 .run => .global,
3016 else => .search,
3017 };
3018 },
3019 if (native_os == .wasi) wasi_preopens,
3020 self_exe_path,
3021 );
3022 defer dirs.deinit();
3023
3024 if (linker_optimization) |o| {
3025 warn("ignoring deprecated linker optimization setting '{s}'", .{o});
3026 }
3027
3028 create_module.dirs = dirs;
3029 create_module.opts.emit_llvm_ir = emit_llvm_ir != .no;
3030 create_module.opts.emit_llvm_bc = emit_llvm_bc != .no;
3031 create_module.opts.emit_bin = emit_bin != .no;
3032 create_module.opts.any_c_source_files = create_module.c_source_files.items.len != 0;
3033
3034 const main_mod = try createModule(gpa, arena, io, &create_module, 0, null, color);
3035 for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
3036 if (cli_mod.resolved == null)
3037 fatal("module '{s}' declared but not used", .{key});
3038 }
3039
3040 // When you're testing std, the main module is std, and we need to avoid duplicating the module.
3041 const main_mod_is_std = main_mod.root.root == .zig_lib and
3042 mem.eql(u8, main_mod.root.sub_path, "std") and
3043 mem.eql(u8, main_mod.root_src_path, "std.zig");
3044
3045 const std_mod = m: {
3046 if (main_mod_is_std) break :m main_mod;
3047 if (create_module.modules.get("std")) |cli_mod| break :m cli_mod.resolved.?;
3048 break :m null;
3049 };
3050
3051 const root_mod = switch (arg_mode) {
3052 .zig_test, .zig_test_obj => root_mod: {
3053 const test_mod = if (test_runner_path) |test_runner| test_mod: {
3054 const test_mod = try Package.Module.create(arena, .{
3055 .paths = .{
3056 .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(test_runner) orelse "."}),
3057 .root_src_path = fs.path.basename(test_runner),
3058 },
3059 .fully_qualified_name = "root",
3060 .cc_argv = &.{},
3061 .inherited = .{},
3062 .global = create_module.resolved_options,
3063 .parent = main_mod,
3064 });
3065 test_mod.deps = try main_mod.deps.clone(arena);
3066 break :test_mod test_mod;
3067 } else try Package.Module.create(arena, .{
3068 .paths = .{
3069 .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"),
3070 .root_src_path = "test_runner.zig",
3071 },
3072 .fully_qualified_name = "root",
3073 .cc_argv = &.{},
3074 .inherited = .{},
3075 .global = create_module.resolved_options,
3076 .parent = main_mod,
3077 });
3078
3079 break :root_mod test_mod;
3080 },
3081 else => main_mod,
3082 };
3083
3084 const target = &main_mod.resolved_target.result;
3085
3086 if (target.cpu.arch == .arc or target.cpu.arch.isNvptx()) {
3087 if (emit_bin != .no and create_module.resolved_options.use_llvm) {
3088 fatal("cannot emit {s} binary with the LLVM backend; only '-femit-asm' is supported", .{
3089 @tagName(target.cpu.arch),
3090 });
3091 }
3092 }
3093
3094 if (target.os.tag == .windows and major_subsystem_version == null and minor_subsystem_version == null) {
3095 major_subsystem_version, minor_subsystem_version = switch (target.os.version_range.windows.min) {
3096 .nt4 => .{ 4, 0 },
3097 .win2k => .{ 5, 0 },
3098 .xp => if (target.cpu.arch == .x86_64) .{ 5, 2 } else .{ 5, 1 },
3099 .ws2003 => .{ 5, 2 },
3100 else => .{ null, null },
3101 };
3102 }
3103
3104 if (target.ofmt != .coff) {
3105 if (manifest_file != null) {
3106 fatal("manifest file is not allowed unless the target object format is coff (Windows/UEFI)", .{});
3107 }
3108 if (create_module.rc_source_files.items.len != 0) {
3109 fatal("rc files are not allowed unless the target object format is coff (Windows/UEFI)", .{});
3110 }
3111 if (contains_res_file) {
3112 fatal("res files are not allowed unless the target object format is coff (Windows/UEFI)", .{});
3113 }
3114 }
3115
3116 var resolved_frameworks = std.array_list.Managed(Compilation.Framework).init(arena);
3117
3118 if (create_module.frameworks.keys().len > 0) {
3119 var test_path = std.array_list.Managed(u8).init(gpa);
3120 defer test_path.deinit();
3121
3122 var checked_paths = std.array_list.Managed(u8).init(gpa);
3123 defer checked_paths.deinit();
3124
3125 var failed_frameworks = std.array_list.Managed(struct {
3126 name: []const u8,
3127 checked_paths: []const u8,
3128 }).init(arena);
3129
3130 framework: for (create_module.frameworks.keys(), create_module.frameworks.values()) |framework_name, info| {
3131 checked_paths.clearRetainingCapacity();
3132
3133 for (create_module.framework_dirs.items) |framework_dir_path| {
3134 if (try accessFrameworkPath(
3135 &test_path,
3136 &checked_paths,
3137 framework_dir_path,
3138 framework_name,
3139 )) {
3140 const path = Path.initCwd(try arena.dupe(u8, test_path.items));
3141 try resolved_frameworks.append(.{
3142 .needed = info.needed,
3143 .weak = info.weak,
3144 .path = path,
3145 });
3146 continue :framework;
3147 }
3148 }
3149
3150 try failed_frameworks.append(.{
3151 .name = framework_name,
3152 .checked_paths = try arena.dupe(u8, checked_paths.items),
3153 });
3154 }
3155
3156 if (failed_frameworks.items.len > 0) {
3157 for (failed_frameworks.items) |f| {
3158 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
3159 std.log.err("unable to find framework '{s}'. searched paths: {s}", .{
3160 f.name, searched_paths,
3161 });
3162 }
3163 process.exit(1);
3164 }
3165 }
3166 // After this point, resolved_frameworks is used instead of frameworks.
3167
3168 if (create_module.resolved_options.output_mode == .Obj and target.ofmt == .coff) {
3169 const total_obj_count = create_module.c_source_files.items.len +
3170 @intFromBool(root_src_file != null) +
3171 create_module.rc_source_files.items.len +
3172 link.countObjectInputs(create_module.link_inputs.items);
3173 if (total_obj_count > 1) {
3174 fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)});
3175 }
3176 }
3177
3178 var cleanup_emit_bin_dir: ?fs.Dir = null;
3179 defer if (cleanup_emit_bin_dir) |*dir| dir.close();
3180
3181 // For `zig run` and `zig test`, we don't want to put the binary in the cwd by default. So, if
3182 // the binary is requested with no explicit path (as is the default), we emit to the cache.
3183 const output_to_cache: ?Emit.OutputToCacheReason = switch (listen) {
3184 .stdio, .ip4 => .listen,
3185 .none => if (arg_mode == .run and emit_bin == .yes_default_path)
3186 .@"zig run"
3187 else if (arg_mode == .zig_test and emit_bin == .yes_default_path)
3188 .@"zig test"
3189 else
3190 null,
3191 };
3192 const optional_version = if (have_version) version else null;
3193
3194 const root_name = if (provided_name) |n| n else main_mod.fully_qualified_name;
3195
3196 const resolved_soname: ?[]const u8 = switch (soname) {
3197 .yes => |explicit| explicit,
3198 .no => null,
3199 .yes_default_value => if (create_module.resolved_options.output_mode == .Lib and
3200 create_module.resolved_options.link_mode == .dynamic and target.ofmt == .elf)
3201 if (have_version)
3202 try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major })
3203 else
3204 try std.fmt.allocPrint(arena, "lib{s}.so", .{root_name})
3205 else
3206 null,
3207 };
3208
3209 const emit_bin_resolved: Compilation.CreateOptions.Emit = switch (emit_bin) {
3210 .no => .no,
3211 .yes_default_path => emit: {
3212 if (output_to_cache != null) break :emit .yes_cache;
3213 const name = switch (clang_preprocessor_mode) {
3214 .pch => try std.fmt.allocPrint(arena, "{s}.pch", .{root_name}),
3215 else => try std.zig.binNameAlloc(arena, .{
3216 .root_name = root_name,
3217 .target = target,
3218 .output_mode = create_module.resolved_options.output_mode,
3219 .link_mode = create_module.resolved_options.link_mode,
3220 .version = optional_version,
3221 }),
3222 };
3223 break :emit .{ .yes_path = name };
3224 },
3225 .yes => |path| if (output_to_cache != null) {
3226 assert(output_to_cache == .listen); // there was an explicit bin path
3227 fatal("--listen incompatible with explicit output path '{s}'", .{path});
3228 } else emit: {
3229 // If there's a dirname, check that dir exists. This will give a more descriptive error than `Compilation` otherwise would.
3230 if (fs.path.dirname(path)) |dir_path| {
3231 var dir = fs.cwd().openDir(dir_path, .{}) catch |err| {
3232 fatal("unable to open output directory '{s}': {s}", .{ dir_path, @errorName(err) });
3233 };
3234 dir.close();
3235 }
3236 break :emit .{ .yes_path = path };
3237 },
3238 .yes_a_out => emit: {
3239 assert(output_to_cache == null);
3240 break :emit .{ .yes_path = switch (target.ofmt) {
3241 .coff => "a.exe",
3242 else => "a.out",
3243 } };
3244 },
3245 };
3246
3247 const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name});
3248 const emit_h_resolved = emit_h.resolve(default_h_basename, output_to_cache);
3249
3250 const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name});
3251 const emit_asm_resolved = emit_asm.resolve(default_asm_basename, output_to_cache);
3252
3253 const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name});
3254 const emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename, output_to_cache);
3255
3256 const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name});
3257 const emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename, output_to_cache);
3258
3259 const emit_docs_resolved = emit_docs.resolve("docs", output_to_cache);
3260
3261 const is_exe_or_dyn_lib = switch (create_module.resolved_options.output_mode) {
3262 .Obj => false,
3263 .Lib => create_module.resolved_options.link_mode == .dynamic,
3264 .Exe => true,
3265 };
3266 // Note that cmake when targeting Windows will try to execute
3267 // zig cc to make an executable and output an implib too.
3268 const implib_eligible = is_exe_or_dyn_lib and
3269 emit_bin_resolved != .no and target.os.tag == .windows;
3270 if (!implib_eligible) {
3271 if (!emit_implib_arg_provided) {
3272 emit_implib = .no;
3273 } else if (emit_implib != .no) {
3274 fatal("the argument -femit-implib is allowed only when building a Windows DLL", .{});
3275 }
3276 }
3277 const default_implib_basename = try std.fmt.allocPrint(arena, "{s}.lib", .{root_name});
3278 const emit_implib_resolved: Compilation.CreateOptions.Emit = switch (emit_implib) {
3279 .no => .no,
3280 .yes => emit_implib.resolve(default_implib_basename, output_to_cache),
3281 .yes_default_path => emit: {
3282 if (output_to_cache != null) break :emit .yes_cache;
3283 const p = try fs.path.join(arena, &.{
3284 fs.path.dirname(emit_bin_resolved.yes_path) orelse ".",
3285 default_implib_basename,
3286 });
3287 break :emit .{ .yes_path = p };
3288 },
3289 };
3290
3291 var thread_pool: ThreadPool = undefined;
3292 try thread_pool.init(.{
3293 .allocator = gpa,
3294 .n_jobs = @min(@max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
3295 .track_ids = true,
3296 .stack_size = thread_stack_size,
3297 });
3298 defer thread_pool.deinit();
3299
3300 for (create_module.c_source_files.items) |*src| {
3301 dev.check(.c_compiler);
3302 if (!mem.eql(u8, src.src_path, "-")) continue;
3303
3304 const ext = src.ext orelse
3305 fatal("-E or -x is required when reading from a non-regular file", .{});
3306
3307 // "-" is stdin. Dump it to a real file.
3308 const sep = fs.path.sep_str;
3309 const dump_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{
3310 std.crypto.random.int(u64), ext.canonicalName(target),
3311 });
3312 try dirs.local_cache.handle.makePath("tmp");
3313
3314 // Note that in one of the happy paths, execve() is used to switch to
3315 // clang in which case any cleanup logic that exists for this temporary
3316 // file will not run and this temp file will be leaked. The filename
3317 // will be a hash of its contents — so multiple invocations of
3318 // `zig cc -` will result in the same temp file name.
3319 var f = try dirs.local_cache.handle.createFile(dump_path, .{});
3320 defer f.close();
3321
3322 // Re-using the hasher from Cache, since the functional requirements
3323 // for the hashing algorithm here and in the cache are the same.
3324 // We are providing our own cache key, because this file has nothing
3325 // to do with the cache manifest.
3326 var file_writer = f.writer(&.{});
3327 var buffer: [1000]u8 = undefined;
3328 var hasher = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef"), &buffer);
3329 var stdin_reader = fs.File.stdin().readerStreaming(io, &.{});
3330 _ = hasher.writer.sendFileAll(&stdin_reader, .unlimited) catch |err| switch (err) {
3331 error.WriteFailed => fatal("failed to write {s}: {t}", .{ dump_path, file_writer.err.? }),
3332 else => fatal("failed to pipe stdin to {s}: {t}", .{ dump_path, err }),
3333 };
3334 try hasher.writer.flush();
3335
3336 const bin_digest: Cache.BinDigest = hasher.hasher.finalResult();
3337
3338 const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{
3339 &bin_digest, ext.canonicalName(target),
3340 });
3341 try dirs.local_cache.handle.rename(dump_path, sub_path);
3342
3343 // Convert `sub_path` to be relative to current working directory.
3344 src.src_path = try dirs.local_cache.join(arena, &.{sub_path});
3345 }
3346
3347 if (build_options.have_llvm and emit_asm_resolved != .no) {
3348 // LLVM has no way to set this non-globally.
3349 const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" };
3350 @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv);
3351 }
3352
3353 const clang_passthrough_mode = switch (arg_mode) {
3354 .cc, .cpp, .translate_c => true,
3355 else => false,
3356 };
3357
3358 const incremental = create_module.resolved_options.incremental;
3359 if (debug_incremental and !incremental) {
3360 fatal("--debug-incremental requires -fincremental", .{});
3361 }
3362
3363 if (incremental and create_module.resolved_options.use_llvm) {
3364 warn("-fincremental is currently unsupported by the LLVM backend; crashes or miscompilations are likely", .{});
3365 }
3366
3367 const cache_mode: Compilation.CacheMode = b: {
3368 // Once incremental compilation is the default, we'll want some smarter logic here,
3369 // considering things like the backend in use and whether there's a ZCU.
3370 if (output_to_cache == null) break :b .none;
3371 if (incremental) break :b .incremental;
3372 break :b .whole;
3373 };
3374
3375 process.raiseFileDescriptorLimit();
3376
3377 var file_system_inputs: std.ArrayList(u8) = .empty;
3378 defer file_system_inputs.deinit(gpa);
3379
3380 // Deduplicate rpath entries
3381 var rpath_dedup = std.StringArrayHashMapUnmanaged(void){};
3382 for (create_module.rpath_list.items) |rpath| {
3383 try rpath_dedup.put(arena, rpath, {});
3384 }
3385 create_module.rpath_list.clearRetainingCapacity();
3386 try create_module.rpath_list.appendSlice(arena, rpath_dedup.keys());
3387
3388 var create_diag: Compilation.CreateDiagnostic = undefined;
3389 const comp = Compilation.create(gpa, arena, io, &create_diag, .{
3390 .dirs = dirs,
3391 .thread_pool = &thread_pool,
3392 .self_exe_path = switch (native_os) {
3393 .wasi => null,
3394 else => self_exe_path,
3395 },
3396 .config = create_module.resolved_options,
3397 .root_name = root_name,
3398 .sysroot = create_module.sysroot,
3399 .main_mod = main_mod,
3400 .root_mod = root_mod,
3401 .std_mod = std_mod,
3402 .emit_bin = emit_bin_resolved,
3403 .emit_h = emit_h_resolved,
3404 .emit_asm = emit_asm_resolved,
3405 .emit_llvm_ir = emit_llvm_ir_resolved,
3406 .emit_llvm_bc = emit_llvm_bc_resolved,
3407 .emit_docs = emit_docs_resolved,
3408 .emit_implib = emit_implib_resolved,
3409 .lib_directories = create_module.lib_directories.items,
3410 .rpath_list = create_module.rpath_list.items,
3411 .symbol_wrap_set = symbol_wrap_set,
3412 .c_source_files = create_module.c_source_files.items,
3413 .rc_source_files = create_module.rc_source_files.items,
3414 .manifest_file = manifest_file,
3415 .rc_includes = rc_includes,
3416 .mingw_unicode_entry_point = mingw_unicode_entry_point,
3417 .link_inputs = create_module.link_inputs.items,
3418 .framework_dirs = create_module.framework_dirs.items,
3419 .frameworks = resolved_frameworks.items,
3420 .windows_lib_names = create_module.windows_libs.keys(),
3421 .want_compiler_rt = want_compiler_rt,
3422 .want_ubsan_rt = want_ubsan_rt,
3423 .hash_style = hash_style,
3424 .linker_script = linker_script,
3425 .version_script = version_script,
3426 .linker_allow_undefined_version = linker_allow_undefined_version,
3427 .linker_enable_new_dtags = linker_enable_new_dtags,
3428 .disable_c_depfile = disable_c_depfile,
3429 .soname = resolved_soname,
3430 .linker_sort_section = linker_sort_section,
3431 .linker_gc_sections = linker_gc_sections,
3432 .linker_repro = linker_repro,
3433 .linker_allow_shlib_undefined = linker_allow_shlib_undefined,
3434 .linker_bind_global_refs_locally = linker_bind_global_refs_locally,
3435 .linker_import_symbols = linker_import_symbols,
3436 .linker_import_table = linker_import_table,
3437 .linker_export_table = linker_export_table,
3438 .linker_initial_memory = linker_initial_memory,
3439 .linker_max_memory = linker_max_memory,
3440 .linker_print_gc_sections = linker_print_gc_sections,
3441 .linker_print_icf_sections = linker_print_icf_sections,
3442 .linker_print_map = linker_print_map,
3443 .llvm_opt_bisect_limit = llvm_opt_bisect_limit,
3444 .linker_global_base = linker_global_base,
3445 .linker_export_symbol_names = linker_export_symbol_names.items,
3446 .linker_z_nocopyreloc = linker_z_nocopyreloc,
3447 .linker_z_nodelete = linker_z_nodelete,
3448 .linker_z_notext = linker_z_notext,
3449 .linker_z_defs = linker_z_defs,
3450 .linker_z_origin = linker_z_origin,
3451 .linker_z_now = linker_z_now,
3452 .linker_z_relro = linker_z_relro,
3453 .linker_z_common_page_size = linker_z_common_page_size,
3454 .linker_z_max_page_size = linker_z_max_page_size,
3455 .linker_tsaware = linker_tsaware,
3456 .linker_nxcompat = linker_nxcompat,
3457 .linker_dynamicbase = linker_dynamicbase,
3458 .linker_compress_debug_sections = linker_compress_debug_sections,
3459 .linker_module_definition_file = linker_module_definition_file,
3460 .major_subsystem_version = major_subsystem_version,
3461 .minor_subsystem_version = minor_subsystem_version,
3462 .link_eh_frame_hdr = link_eh_frame_hdr,
3463 .link_emit_relocs = link_emit_relocs,
3464 .entry = entry,
3465 .force_undefined_symbols = force_undefined_symbols,
3466 .stack_size = stack_size,
3467 .image_base = image_base,
3468 .function_sections = function_sections,
3469 .data_sections = data_sections,
3470 .clang_passthrough_mode = clang_passthrough_mode,
3471 .clang_preprocessor_mode = clang_preprocessor_mode,
3472 .version = optional_version,
3473 .compatibility_version = compatibility_version,
3474 .libc_installation = if (create_module.libc_installation) |*lci| lci else null,
3475 .verbose_cc = verbose_cc,
3476 .verbose_link = verbose_link,
3477 .verbose_air = verbose_air,
3478 .verbose_intern_pool = verbose_intern_pool,
3479 .verbose_generic_instances = verbose_generic_instances,
3480 .verbose_llvm_ir = verbose_llvm_ir,
3481 .verbose_llvm_bc = verbose_llvm_bc,
3482 .verbose_cimport = verbose_cimport,
3483 .verbose_llvm_cpu_features = verbose_llvm_cpu_features,
3484 .time_report = time_report,
3485 .stack_report = stack_report,
3486 .build_id = build_id,
3487 .test_filters = test_filters.items,
3488 .test_runner_path = test_runner_path,
3489 .cache_mode = cache_mode,
3490 .subsystem = subsystem,
3491 .debug_compile_errors = debug_compile_errors,
3492 .debug_incremental = debug_incremental,
3493 .enable_link_snapshots = enable_link_snapshots,
3494 .install_name = install_name,
3495 .entitlements = entitlements,
3496 .pagezero_size = pagezero_size,
3497 .headerpad_size = headerpad_size,
3498 .headerpad_max_install_names = headerpad_max_install_names,
3499 .dead_strip_dylibs = dead_strip_dylibs,
3500 .force_load_objc = force_load_objc,
3501 .discard_local_symbols = discard_local_symbols,
3502 .reference_trace = reference_trace,
3503 .pdb_out_path = pdb_out_path,
3504 .error_limit = error_limit,
3505 .native_system_include_paths = create_module.native_system_include_paths,
3506 // Any leftover C compilation args (such as -I) apply globally rather
3507 // than to any particular module. This feature can greatly reduce CLI
3508 // noise when --search-prefix and -M are combined.
3509 .global_cc_argv = try cc_argv.toOwnedSlice(arena),
3510 .file_system_inputs = &file_system_inputs,
3511 .debug_compiler_runtime_libs = debug_compiler_runtime_libs,
3512 }) catch |err| switch (err) {
3513 error.CreateFail => switch (create_diag) {
3514 .cross_libc_unavailable => {
3515 // We can emit a more informative error for this.
3516 const triple_name = try target.zigTriple(arena);
3517 std.log.err("unable to provide libc for target '{s}'", .{triple_name});
3518
3519 for (std.zig.target.available_libcs) |t| {
3520 if (t.arch == target.cpu.arch and t.os == target.os.tag) {
3521 // If there's a `glibc_min`, there's also an `os_ver`.
3522 if (t.glibc_min) |glibc_min| {
3523 std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}.{d}.{d}", .{
3524 @tagName(t.arch),
3525 @tagName(t.os),
3526 t.os_ver.?,
3527 @tagName(t.abi),
3528 glibc_min.major,
3529 glibc_min.minor,
3530 });
3531 } else if (t.os_ver) |os_ver| {
3532 std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}", .{
3533 @tagName(t.arch),
3534 @tagName(t.os),
3535 os_ver,
3536 @tagName(t.abi),
3537 });
3538 } else {
3539 std.log.info("zig can provide libc for related target {s}-{s}-{s}", .{
3540 @tagName(t.arch),
3541 @tagName(t.os),
3542 @tagName(t.abi),
3543 });
3544 }
3545 }
3546 }
3547 process.exit(1);
3548 },
3549 else => fatal("{f}", .{create_diag}),
3550 },
3551 else => fatal("failed to create compilation: {s}", .{@errorName(err)}),
3552 };
3553 var comp_destroyed = false;
3554 defer if (!comp_destroyed) comp.destroy();
3555
3556 if (show_builtin) {
3557 const builtin_opts = comp.root_mod.getBuiltinOptions(comp.config);
3558 const source = try builtin_opts.generate(arena);
3559 return fs.File.stdout().writeAll(source);
3560 }
3561 switch (listen) {
3562 .none => {},
3563 .stdio => {
3564 var stdin_reader = fs.File.stdin().reader(io, &stdin_buffer);
3565 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
3566 try serve(
3567 comp,
3568 &stdin_reader.interface,
3569 &stdout_writer.interface,
3570 test_exec_args.items,
3571 self_exe_path,
3572 arg_mode,
3573 all_args,
3574 runtime_args_start,
3575 );
3576 return cleanExit();
3577 },
3578 .ip4 => |ip4_addr| {
3579 const addr: Io.net.IpAddress = .{ .ip4 = ip4_addr };
3580
3581 var server = try addr.listen(io, .{
3582 .reuse_address = true,
3583 });
3584 defer server.deinit(io);
3585
3586 var stream = try server.accept(io);
3587 defer stream.close(io);
3588
3589 var input = stream.reader(io, &stdin_buffer);
3590 var output = stream.writer(io, &stdout_buffer);
3591
3592 try serve(
3593 comp,
3594 &input.interface,
3595 &output.interface,
3596 test_exec_args.items,
3597 self_exe_path,
3598 arg_mode,
3599 all_args,
3600 runtime_args_start,
3601 );
3602 return cleanExit();
3603 },
3604 }
3605
3606 {
3607 const root_prog_node = std.Progress.start(.{
3608 .disable_printing = (color == .off),
3609 });
3610 defer root_prog_node.end();
3611
3612 if (arg_mode == .translate_c) {
3613 return cmdTranslateC(comp, arena, null, null, root_prog_node);
3614 }
3615
3616 updateModule(comp, color, root_prog_node) catch |err| switch (err) {
3617 error.CompileErrorsReported => {
3618 assert(listen == .none);
3619 saveState(comp, incremental);
3620 process.exit(1);
3621 },
3622 else => |e| return e,
3623 };
3624 }
3625 try comp.makeBinFileExecutable();
3626 saveState(comp, incremental);
3627
3628 if (switch (arg_mode) {
3629 .run => true,
3630 .zig_test => !test_no_exec,
3631 else => false,
3632 }) {
3633 dev.checkAny(&.{ .run_command, .test_command });
3634
3635 if (test_exec_args.items.len == 0 and target.ofmt == .c and emit_bin_resolved != .no) {
3636 // Default to using `zig run` to execute the produced .c code from `zig test`.
3637 try test_exec_args.appendSlice(arena, &.{ self_exe_path, "run" });
3638 if (dirs.zig_lib.path) |p| {
3639 try test_exec_args.appendSlice(arena, &.{ "-I", p });
3640 }
3641
3642 if (create_module.resolved_options.link_libc) {
3643 try test_exec_args.append(arena, "-lc");
3644 } else if (target.os.tag == .windows) {
3645 try test_exec_args.appendSlice(arena, &.{
3646 "--subsystem", "console",
3647 "-lkernel32", "-lntdll",
3648 });
3649 }
3650
3651 const first_cli_mod = create_module.modules.values()[0];
3652 if (first_cli_mod.target_arch_os_abi) |triple| {
3653 try test_exec_args.appendSlice(arena, &.{ "-target", triple });
3654 }
3655 if (first_cli_mod.target_mcpu) |mcpu| {
3656 try test_exec_args.append(arena, try std.fmt.allocPrint(arena, "-mcpu={s}", .{mcpu}));
3657 }
3658 if (create_module.dynamic_linker) |dl| {
3659 if (dl.len > 0) {
3660 try test_exec_args.appendSlice(arena, &.{ "--dynamic-linker", dl });
3661 } else {
3662 try test_exec_args.append(arena, "--no-dynamic-linker");
3663 }
3664 }
3665 try test_exec_args.append(arena, null); // placeholder for the path of the emitted C source file
3666 }
3667
3668 try runOrTest(
3669 comp,
3670 gpa,
3671 arena,
3672 io,
3673 test_exec_args.items,
3674 self_exe_path,
3675 arg_mode,
3676 target,
3677 &comp_destroyed,
3678 all_args,
3679 runtime_args_start,
3680 create_module.resolved_options.link_libc,
3681 );
3682 }
3683
3684 // Skip resource deallocation in release builds; let the OS do it.
3685 return cleanExit();
3686}
3687
3688const CreateModule = struct {
3689 dirs: Compilation.Directories,
3690 modules: std.StringArrayHashMapUnmanaged(CliModule),
3691 opts: Compilation.Config.Options,
3692 dynamic_linker: ?[]const u8,
3693 object_format: ?[]const u8,
3694 /// undefined until createModule() for the root module is called.
3695 resolved_options: Compilation.Config,
3696
3697 /// This one is used while collecting CLI options. The set of libs is used
3698 /// directly after computing the target and used to compute link_libc,
3699 /// link_libcpp, and then the libraries are filtered into
3700 /// `unresolved_link_inputs` and `windows_libs`.
3701 cli_link_inputs: std.ArrayList(link.UnresolvedInput),
3702 windows_libs: std.StringArrayHashMapUnmanaged(void),
3703 /// The local variable `unresolved_link_inputs` is fed into library
3704 /// resolution, mutating the input array, and producing this data as
3705 /// output. Allocated with gpa.
3706 link_inputs: std.ArrayList(link.Input),
3707
3708 c_source_files: std.ArrayList(Compilation.CSourceFile),
3709 rc_source_files: std.ArrayList(Compilation.RcSourceFile),
3710
3711 /// e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
3712 /// This array is populated by zig cc frontend and then has to be converted to zig-style
3713 /// CPU features.
3714 llvm_m_args: std.ArrayList([]const u8),
3715 sysroot: ?[]const u8,
3716 lib_directories: std.ArrayList(Directory),
3717 lib_dir_args: std.ArrayList([]const u8),
3718 libc_installation: ?LibCInstallation,
3719 want_native_include_dirs: bool,
3720 frameworks: std.StringArrayHashMapUnmanaged(Framework),
3721 native_system_include_paths: []const []const u8,
3722 framework_dirs: std.ArrayList([]const u8),
3723 rpath_list: std.ArrayList([]const u8),
3724 each_lib_rpath: ?bool,
3725 libc_paths_file: ?[]const u8,
3726};
3727
3728fn createModule(
3729 gpa: Allocator,
3730 arena: Allocator,
3731 io: Io,
3732 create_module: *CreateModule,
3733 index: usize,
3734 parent: ?*Package.Module,
3735 color: std.zig.Color,
3736) Allocator.Error!*Package.Module {
3737 const cli_mod = &create_module.modules.values()[index];
3738 if (cli_mod.resolved) |m| return m;
3739
3740 const name = create_module.modules.keys()[index];
3741
3742 cli_mod.inherited.resolved_target = t: {
3743 // If the target is not overridden, use the parent's target. Of course,
3744 // if this is the root module then we need to proceed to resolve the
3745 // target.
3746 if (cli_mod.target_arch_os_abi == null and cli_mod.target_mcpu == null) {
3747 if (parent) |p| break :t p.resolved_target;
3748 }
3749
3750 var target_parse_options: std.Target.Query.ParseOptions = .{
3751 .arch_os_abi = cli_mod.target_arch_os_abi orelse "native",
3752 .cpu_features = cli_mod.target_mcpu,
3753 .dynamic_linker = create_module.dynamic_linker,
3754 .object_format = create_module.object_format,
3755 };
3756
3757 // Before passing the mcpu string in for parsing, we convert any -m flags that were
3758 // passed in via zig cc to zig-style.
3759 if (create_module.llvm_m_args.items.len != 0) {
3760 // If this returns null, we let it fall through to the case below which will
3761 // run the full parse function and do proper error handling.
3762 if (std.Target.Query.parseCpuArch(target_parse_options)) |cpu_arch| {
3763 var llvm_to_zig_name = std.StringHashMap([]const u8).init(gpa);
3764 defer llvm_to_zig_name.deinit();
3765
3766 for (cpu_arch.allFeaturesList()) |feature| {
3767 const llvm_name = feature.llvm_name orelse continue;
3768 try llvm_to_zig_name.put(llvm_name, feature.name);
3769 }
3770
3771 var mcpu_buffer = std.array_list.Managed(u8).init(gpa);
3772 defer mcpu_buffer.deinit();
3773
3774 try mcpu_buffer.appendSlice(cli_mod.target_mcpu orelse "baseline");
3775
3776 for (create_module.llvm_m_args.items) |llvm_m_arg| {
3777 if (mem.cutPrefix(u8, llvm_m_arg, "mno-")) |llvm_name| {
3778 const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
3779 fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
3780 @tagName(cpu_arch), llvm_name,
3781 });
3782 };
3783 try mcpu_buffer.append('-');
3784 try mcpu_buffer.appendSlice(zig_name);
3785 } else if (mem.cutPrefix(u8, llvm_m_arg, "m")) |llvm_name| {
3786 const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
3787 fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
3788 @tagName(cpu_arch), llvm_name,
3789 });
3790 };
3791 try mcpu_buffer.append('+');
3792 try mcpu_buffer.appendSlice(zig_name);
3793 } else {
3794 unreachable;
3795 }
3796 }
3797
3798 const adjusted_target_mcpu = try arena.dupe(u8, mcpu_buffer.items);
3799 std.log.debug("adjusted target_mcpu: {s}", .{adjusted_target_mcpu});
3800 target_parse_options.cpu_features = adjusted_target_mcpu;
3801 }
3802 }
3803
3804 const target_query = std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);
3805 const target = std.zig.resolveTargetQueryOrFatal(io, target_query);
3806 break :t .{
3807 .result = target,
3808 .is_native_os = target_query.isNativeOs(),
3809 .is_native_abi = target_query.isNativeAbi(),
3810 .is_explicit_dynamic_linker = target_query.dynamic_linker != null,
3811 };
3812 };
3813
3814 if (parent == null) {
3815 // This block is for initializing the fields of
3816 // `Compilation.Config.Options` that require knowledge of the
3817 // target (which was just now resolved for the root module above).
3818 const resolved_target = &cli_mod.inherited.resolved_target.?;
3819 create_module.opts.resolved_target = resolved_target.*;
3820 create_module.opts.root_optimize_mode = cli_mod.inherited.optimize_mode;
3821 create_module.opts.root_strip = cli_mod.inherited.strip;
3822 create_module.opts.root_error_tracing = cli_mod.inherited.error_tracing;
3823 const target = &resolved_target.result;
3824
3825 // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
3826 // We need to know whether the set of system libraries contains anything besides these
3827 // to decide whether to trigger native path detection logic.
3828 // Preserves linker input order.
3829 var unresolved_link_inputs: std.ArrayList(link.UnresolvedInput) = .empty;
3830 defer unresolved_link_inputs.deinit(gpa);
3831 try unresolved_link_inputs.ensureUnusedCapacity(gpa, create_module.cli_link_inputs.items.len);
3832 var any_name_queries_remaining = false;
3833 for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {
3834 .name_query => |nq| {
3835 const lib_name = nq.name;
3836
3837 if (std.zig.target.isLibCLibName(target, lib_name)) {
3838 create_module.opts.link_libc = true;
3839 continue;
3840 }
3841 if (std.zig.target.isLibCxxLibName(target, lib_name)) {
3842 create_module.opts.link_libcpp = true;
3843 continue;
3844 }
3845
3846 switch (target_util.classifyCompilerRtLibName(lib_name)) {
3847 .none => {},
3848 .only_libunwind, .both => {
3849 create_module.opts.link_libunwind = true;
3850 continue;
3851 },
3852 .only_compiler_rt => continue,
3853 }
3854
3855 if (target.isMinGW()) {
3856 const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| {
3857 fatal("failed to check zig installation for DLL import libs: {s}", .{
3858 @errorName(err),
3859 });
3860 };
3861 if (exists) {
3862 try create_module.windows_libs.put(arena, lib_name, {});
3863 continue;
3864 }
3865 }
3866
3867 if (fs.path.isAbsolute(lib_name)) {
3868 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
3869 }
3870
3871 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
3872 any_name_queries_remaining = true;
3873 },
3874 else => {
3875 unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
3876 },
3877 }; // After this point, unresolved_link_inputs is used instead of cli_link_inputs.
3878
3879 if (any_name_queries_remaining) create_module.want_native_include_dirs = true;
3880
3881 // Resolve the library path arguments with respect to sysroot.
3882 try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);
3883 if (create_module.sysroot) |root| {
3884 for (create_module.lib_dir_args.items) |lib_dir_arg| {
3885 if (fs.path.isAbsolute(lib_dir_arg)) {
3886 const stripped_dir = lib_dir_arg[fs.path.parsePath(lib_dir_arg).root.len..];
3887 const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
3888 addLibDirectoryWarn(&create_module.lib_directories, full_path);
3889 } else {
3890 addLibDirectoryWarn(&create_module.lib_directories, lib_dir_arg);
3891 }
3892 }
3893 } else {
3894 for (create_module.lib_dir_args.items) |lib_dir_arg| {
3895 addLibDirectoryWarn(&create_module.lib_directories, lib_dir_arg);
3896 }
3897 }
3898 create_module.lib_dir_args = undefined; // From here we use lib_directories instead.
3899
3900 if (resolved_target.is_native_os and target.os.tag.isDarwin()) {
3901 // If we want to link against frameworks, we need system headers.
3902 if (create_module.frameworks.count() > 0)
3903 create_module.want_native_include_dirs = true;
3904 }
3905
3906 if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {
3907 try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len);
3908 for (create_module.lib_directories.items) |lib_directory| {
3909 create_module.rpath_list.appendAssumeCapacity(lib_directory.path.?);
3910 }
3911 }
3912
3913 // Trigger native system library path detection if necessary.
3914 if (create_module.sysroot == null and
3915 resolved_target.is_native_os and resolved_target.is_native_abi and
3916 create_module.want_native_include_dirs)
3917 {
3918 var paths = std.zig.system.NativePaths.detect(arena, target) catch |err| {
3919 fatal("unable to detect native system paths: {s}", .{@errorName(err)});
3920 };
3921 for (paths.warnings.items) |warning| {
3922 warn("{s}", .{warning});
3923 }
3924
3925 create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);
3926
3927 try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items);
3928 try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);
3929
3930 try create_module.lib_directories.ensureUnusedCapacity(arena, paths.lib_dirs.items.len);
3931 for (paths.lib_dirs.items) |path| addLibDirectoryWarn2(&create_module.lib_directories, path, true);
3932 }
3933
3934 if (create_module.libc_paths_file) |paths_file| {
3935 create_module.libc_installation = LibCInstallation.parse(arena, paths_file, target) catch |err| {
3936 fatal("unable to parse libc paths file at path {s}: {s}", .{
3937 paths_file, @errorName(err),
3938 });
3939 };
3940 }
3941
3942 if (target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and
3943 any_name_queries_remaining)
3944 {
3945 if (create_module.libc_installation == null) {
3946 create_module.libc_installation = LibCInstallation.findNative(.{
3947 .allocator = arena,
3948 .verbose = true,
3949 .target = target,
3950 }) catch |err| {
3951 fatal("unable to find native libc installation: {s}", .{@errorName(err)});
3952 };
3953 }
3954 try create_module.lib_directories.ensureUnusedCapacity(arena, 2);
3955 addLibDirectoryWarn(&create_module.lib_directories, create_module.libc_installation.?.msvc_lib_dir.?);
3956 addLibDirectoryWarn(&create_module.lib_directories, create_module.libc_installation.?.kernel32_lib_dir.?);
3957 }
3958
3959 // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.
3960 link.resolveInputs(
3961 gpa,
3962 arena,
3963 target,
3964 &unresolved_link_inputs,
3965 &create_module.link_inputs,
3966 create_module.lib_directories.items,
3967 color,
3968 ) catch |err| fatal("failed to resolve link inputs: {s}", .{@errorName(err)});
3969
3970 if (!create_module.opts.any_dyn_libs) for (create_module.link_inputs.items) |item| switch (item) {
3971 .dso, .dso_exact => {
3972 create_module.opts.any_dyn_libs = true;
3973 break;
3974 },
3975 else => {},
3976 };
3977
3978 create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) {
3979 error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}),
3980 error.SharedMemoryIsWasmOnly => fatal("only WebAssembly CPU targets support shared memory", .{}),
3981 error.ObjectFilesCannotShareMemory => fatal("object files cannot share memory", .{}),
3982 error.ObjectFilesCannotSpecifyDynamicLinker => fatal("object files cannot specify --dynamic-linker", .{}),
3983 error.SharedMemoryRequiresAtomicsAndBulkMemory => fatal("shared memory requires atomics and bulk_memory CPU features", .{}),
3984 error.ThreadsRequireSharedMemory => fatal("threads require shared memory", .{}),
3985 error.EmittingLlvmModuleRequiresLlvmBackend => fatal("emitting an LLVM module requires using the LLVM backend", .{}),
3986 error.LlvmLacksTargetSupport => fatal("LLVM lacks support for the specified target", .{}),
3987 error.ZigLacksTargetSupport => fatal("compiler backend unavailable for the specified target", .{}),
3988 error.EmittingBinaryRequiresLlvmLibrary => fatal("producing machine code via LLVM requires using the LLVM library", .{}),
3989 error.LldIncompatibleObjectFormat => fatal("using LLD to link {s} files is unsupported", .{@tagName(target.ofmt)}),
3990 error.LldCannotIncrementallyLink => fatal("self-hosted backends do not support linking with LLD", .{}),
3991 error.LldCannotSpecifyDynamicLinkerForSharedLibraries => fatal("LLD does not support --dynamic-linker on shared libraries", .{}),
3992 error.LtoRequiresLld => fatal("LTO requires using LLD", .{}),
3993 error.SanitizeThreadRequiresLibCpp => fatal("thread sanitization is (for now) implemented in C++, so it requires linking libc++", .{}),
3994 error.LibCRequiresLibUnwind => fatal("libc of the specified target requires linking libunwind", .{}),
3995 error.LibCppRequiresLibUnwind => fatal("libc++ requires linking libunwind", .{}),
3996 error.OsRequiresLibC => fatal("the target OS requires using libc as the stable syscall interface", .{}),
3997 error.LibCppRequiresLibC => fatal("libc++ requires linking libc", .{}),
3998 error.LibUnwindRequiresLibC => fatal("libunwind requires linking libc", .{}),
3999 error.TargetCannotDynamicLink => fatal("dynamic linking unavailable on the specified target", .{}),
4000 error.TargetCannotStaticLinkExecutables => fatal("static linking of executables unavailable on the specified target", .{}),
4001 error.LibCRequiresDynamicLinking => fatal("libc of the specified target requires dynamic linking", .{}),
4002 error.SharedLibrariesRequireDynamicLinking => fatal("using shared libraries requires dynamic linking", .{}),
4003 error.DynamicLinkingWithLldRequiresSharedLibraries => fatal("dynamic linking with lld requires at least one shared library", .{}),
4004 error.ExportMemoryAndDynamicIncompatible => fatal("exporting memory is incompatible with dynamic linking", .{}),
4005 error.DynamicLibraryPrecludesPie => fatal("dynamic libraries cannot be position independent executables", .{}),
4006 error.TargetRequiresPie => fatal("the specified target requires position independent executables", .{}),
4007 error.SanitizeThreadRequiresPie => fatal("thread sanitization requires position independent executables", .{}),
4008 error.BackendLacksErrorTracing => fatal("the selected backend has not yet implemented error return tracing", .{}),
4009 error.LlvmLibraryUnavailable => fatal("zig was compiled without LLVM libraries", .{}),
4010 error.LldUnavailable => fatal("zig was compiled without LLD libraries", .{}),
4011 error.ClangUnavailable => fatal("zig was compiled without Clang libraries", .{}),
4012 error.DllExportFnsRequiresWindows => fatal("only Windows OS targets support DLLs", .{}),
4013 error.NewLinkerIncompatibleObjectFormat => fatal("using the new linker to link {s} files is unsupported", .{@tagName(target.ofmt)}),
4014 error.NewLinkerIncompatibleWithLld => fatal("using the new linker is incompatible with using lld", .{}),
4015 };
4016 }
4017
4018 const root: Compilation.Path = try .fromUnresolved(arena, create_module.dirs, &.{cli_mod.root_path});
4019
4020 const mod = Package.Module.create(arena, .{
4021 .paths = .{
4022 .root = root,
4023 .root_src_path = cli_mod.root_src_path,
4024 },
4025 .fully_qualified_name = name,
4026
4027 .cc_argv = cli_mod.cc_argv,
4028 .inherited = cli_mod.inherited,
4029 .global = create_module.resolved_options,
4030 .parent = parent,
4031 }) catch |err| switch (err) {
4032 error.ValgrindUnsupportedOnTarget => fatal("unable to create module '{s}': valgrind does not support the selected target CPU architecture", .{name}),
4033 error.TargetRequiresSingleThreaded => fatal("unable to create module '{s}': the selected target does not support multithreading", .{name}),
4034 error.BackendRequiresSingleThreaded => fatal("unable to create module '{s}': the selected machine code backend is limited to single-threaded applications", .{name}),
4035 error.TargetRequiresPic => fatal("unable to create module '{s}': the selected target requires position independent code", .{name}),
4036 error.PieRequiresPic => fatal("unable to create module '{s}': making a Position Independent Executable requires enabling Position Independent Code", .{name}),
4037 error.DynamicLinkingRequiresPic => fatal("unable to create module '{s}': dynamic linking requires enabling Position Independent Code", .{name}),
4038 error.TargetHasNoRedZone => fatal("unable to create module '{s}': the selected target does not have a red zone", .{name}),
4039 error.StackCheckUnsupportedByTarget => fatal("unable to create module '{s}': the selected target does not support stack checking", .{name}),
4040 error.StackProtectorUnsupportedByTarget => fatal("unable to create module '{s}': the selected target does not support stack protection", .{name}),
4041 error.StackProtectorUnavailableWithoutLibC => fatal("unable to create module '{s}': enabling stack protection requires libc", .{name}),
4042 error.OutOfMemory => return error.OutOfMemory,
4043 };
4044 cli_mod.resolved = mod;
4045
4046 for (create_module.c_source_files.items[cli_mod.c_source_files_start..cli_mod.c_source_files_end]) |*item| item.owner = mod;
4047
4048 for (create_module.rc_source_files.items[cli_mod.rc_source_files_start..cli_mod.rc_source_files_end]) |*item| item.owner = mod;
4049
4050 for (cli_mod.deps) |dep| {
4051 const dep_index = create_module.modules.getIndex(dep.value) orelse
4052 fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });
4053 const dep_mod = try createModule(gpa, arena, io, create_module, dep_index, mod, color);
4054 try mod.deps.put(arena, dep.key, dep_mod);
4055 }
4056
4057 return mod;
4058}
4059
4060fn saveState(comp: *Compilation, incremental: bool) void {
4061 if (incremental) {
4062 comp.saveState() catch |err| {
4063 warn("unable to save incremental compilation state: {s}", .{@errorName(err)});
4064 };
4065 }
4066}
4067
4068fn serve(
4069 comp: *Compilation,
4070 in: *Io.Reader,
4071 out: *Io.Writer,
4072 test_exec_args: []const ?[]const u8,
4073 self_exe_path: ?[]const u8,
4074 arg_mode: ArgMode,
4075 all_args: []const []const u8,
4076 runtime_args_start: ?usize,
4077) !void {
4078 const gpa = comp.gpa;
4079
4080 var server = try Server.init(.{
4081 .in = in,
4082 .out = out,
4083 .zig_version = build_options.version,
4084 });
4085
4086 var child_pid: ?std.process.Child.Id = null;
4087
4088 const main_progress_node = std.Progress.start(.{});
4089 const file_system_inputs = comp.file_system_inputs.?;
4090
4091 const IncrementalDebugServer = if (build_options.enable_debug_extensions and !builtin.single_threaded)
4092 @import("IncrementalDebugServer.zig")
4093 else
4094 void;
4095
4096 var ids: IncrementalDebugServer = if (comp.debugIncremental()) ids: {
4097 break :ids .init(comp.zcu orelse @panic("--debug-incremental requires a ZCU"));
4098 } else undefined;
4099 defer if (comp.debugIncremental()) ids.deinit();
4100
4101 if (comp.debugIncremental()) ids.spawn();
4102
4103 while (true) {
4104 const hdr = try server.receiveMessage();
4105
4106 // Lock the debug server while handling the message.
4107 if (comp.debugIncremental()) ids.mutex.lock();
4108 defer if (comp.debugIncremental()) ids.mutex.unlock();
4109
4110 switch (hdr.tag) {
4111 .exit => return cleanExit(),
4112 .update => {
4113 tracy.frameMark();
4114 file_system_inputs.clearRetainingCapacity();
4115
4116 if (arg_mode == .translate_c) {
4117 var arena_instance = std.heap.ArenaAllocator.init(gpa);
4118 defer arena_instance.deinit();
4119 const arena = arena_instance.allocator();
4120 var output: Compilation.CImportResult = undefined;
4121 try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node);
4122 defer output.deinit(gpa);
4123
4124 if (file_system_inputs.items.len != 0) {
4125 try server.serveStringMessage(.file_system_inputs, file_system_inputs.items);
4126 }
4127
4128 if (output.errors.errorMessageCount() != 0) {
4129 try server.serveErrorBundle(output.errors);
4130 } else {
4131 try server.serveEmitDigest(&output.digest, .{
4132 .flags = .{ .cache_hit = output.cache_hit },
4133 });
4134 }
4135
4136 continue;
4137 }
4138
4139 if (comp.config.output_mode == .Exe) {
4140 try comp.makeBinFileWritable();
4141 }
4142
4143 try comp.update(main_progress_node);
4144
4145 try comp.makeBinFileExecutable();
4146 try serveUpdateResults(&server, comp);
4147 },
4148 .run => {
4149 if (child_pid != null) {
4150 @panic("TODO block until the child exits");
4151 }
4152 @panic("TODO call runOrTest");
4153 //try runOrTest(
4154 // comp,
4155 // gpa,
4156 // arena,
4157 // io,
4158 // test_exec_args,
4159 // self_exe_path.?,
4160 // arg_mode,
4161 // target,
4162 // true,
4163 // &comp_destroyed,
4164 // all_args,
4165 // runtime_args_start,
4166 // link_libc,
4167 //);
4168 },
4169 .hot_update => {
4170 tracy.frameMark();
4171 file_system_inputs.clearRetainingCapacity();
4172 if (child_pid) |pid| {
4173 try comp.hotCodeSwap(main_progress_node, pid);
4174 try serveUpdateResults(&server, comp);
4175 } else {
4176 if (comp.config.output_mode == .Exe) {
4177 try comp.makeBinFileWritable();
4178 }
4179 try comp.update(main_progress_node);
4180 try comp.makeBinFileExecutable();
4181 try serveUpdateResults(&server, comp);
4182
4183 child_pid = try runOrTestHotSwap(
4184 comp,
4185 gpa,
4186 test_exec_args,
4187 self_exe_path.?,
4188 arg_mode,
4189 all_args,
4190 runtime_args_start,
4191 );
4192 }
4193 },
4194 else => {
4195 fatal("unrecognized message from client: 0x{x}", .{@intFromEnum(hdr.tag)});
4196 },
4197 }
4198 }
4199}
4200
4201fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
4202 const gpa = comp.gpa;
4203
4204 var error_bundle = try comp.getAllErrorsAlloc();
4205 defer error_bundle.deinit(gpa);
4206
4207 if (comp.file_system_inputs) |file_system_inputs| {
4208 if (file_system_inputs.items.len == 0) {
4209 assert(error_bundle.errorMessageCount() > 0);
4210 } else {
4211 try s.serveStringMessage(.file_system_inputs, file_system_inputs.items);
4212 }
4213 }
4214
4215 if (comp.time_report) |*tr| {
4216 var decls_len: u32 = 0;
4217
4218 var file_name_bytes: std.ArrayList(u8) = .empty;
4219 defer file_name_bytes.deinit(gpa);
4220 var files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, void) = .empty;
4221 defer files.deinit(gpa);
4222 var decl_data: std.ArrayList(u8) = .empty;
4223 defer decl_data.deinit(gpa);
4224
4225 // Each decl needs at least 34 bytes:
4226 // * 2 for 1-byte name plus null terminator
4227 // * 4 for `file`
4228 // * 4 for `sema_count`
4229 // * 8 for `sema_ns`
4230 // * 8 for `codegen_ns`
4231 // * 8 for `link_ns`
4232 // Most, if not all, decls in `tr.decl_sema_ns` are valid, so we have a good size estimate.
4233 try decl_data.ensureUnusedCapacity(gpa, tr.decl_sema_info.count() * 34);
4234
4235 for (tr.decl_sema_info.keys(), tr.decl_sema_info.values()) |tracked_inst, sema_info| {
4236 const resolved = tracked_inst.resolveFull(&comp.zcu.?.intern_pool) orelse continue;
4237 const file = comp.zcu.?.fileByIndex(resolved.file);
4238 const zir = file.zir orelse continue;
4239 const decl_name = zir.nullTerminatedString(zir.getDeclaration(resolved.inst).name);
4240
4241 const gop = try files.getOrPut(gpa, resolved.file);
4242 if (!gop.found_existing) try file_name_bytes.print(gpa, "{f}\x00", .{file.path.fmt(comp)});
4243
4244 const codegen_ns = tr.decl_codegen_ns.get(tracked_inst) orelse 0;
4245 const link_ns = tr.decl_link_ns.get(tracked_inst) orelse 0;
4246
4247 decls_len += 1;
4248
4249 try decl_data.ensureUnusedCapacity(gpa, 33 + decl_name.len);
4250 decl_data.appendSliceAssumeCapacity(decl_name);
4251 decl_data.appendAssumeCapacity(0);
4252
4253 const out_file = decl_data.addManyAsArrayAssumeCapacity(4);
4254 const out_sema_count = decl_data.addManyAsArrayAssumeCapacity(4);
4255 const out_sema_ns = decl_data.addManyAsArrayAssumeCapacity(8);
4256 const out_codegen_ns = decl_data.addManyAsArrayAssumeCapacity(8);
4257 const out_link_ns = decl_data.addManyAsArrayAssumeCapacity(8);
4258 std.mem.writeInt(u32, out_file, @intCast(gop.index), .little);
4259 std.mem.writeInt(u32, out_sema_count, sema_info.count, .little);
4260 std.mem.writeInt(u64, out_sema_ns, sema_info.ns, .little);
4261 std.mem.writeInt(u64, out_codegen_ns, codegen_ns, .little);
4262 std.mem.writeInt(u64, out_link_ns, link_ns, .little);
4263 }
4264
4265 const header: std.zig.Server.Message.TimeReport = .{
4266 .stats = tr.stats,
4267 .llvm_pass_timings_len = @intCast(tr.llvm_pass_timings.len),
4268 .files_len = @intCast(files.count()),
4269 .decls_len = decls_len,
4270 .flags = .{
4271 .use_llvm = comp.zcu != null and comp.zcu.?.llvm_object != null,
4272 },
4273 };
4274
4275 var slices: [4][]const u8 = .{
4276 @ptrCast(&header),
4277 tr.llvm_pass_timings,
4278 file_name_bytes.items,
4279 decl_data.items,
4280 };
4281 try s.serveMessageHeader(.{
4282 .tag = .time_report,
4283 .bytes_len = len: {
4284 var len: u32 = 0;
4285 for (slices) |slice| len += @intCast(slice.len);
4286 break :len len;
4287 },
4288 });
4289 try s.out.writeVecAll(&slices);
4290 try s.out.flush();
4291 }
4292
4293 if (error_bundle.errorMessageCount() > 0) {
4294 try s.serveErrorBundle(error_bundle);
4295 return;
4296 }
4297
4298 if (comp.digest) |digest| {
4299 try s.serveEmitDigest(&digest, .{
4300 .flags = .{ .cache_hit = comp.last_update_was_cache_hit },
4301 });
4302 }
4303
4304 // Serve empty error bundle to indicate the update is done.
4305 try s.serveErrorBundle(std.zig.ErrorBundle.empty);
4306}
4307
4308fn runOrTest(
4309 comp: *Compilation,
4310 gpa: Allocator,
4311 arena: Allocator,
4312 io: Io,
4313 test_exec_args: []const ?[]const u8,
4314 self_exe_path: []const u8,
4315 arg_mode: ArgMode,
4316 target: *const std.Target,
4317 comp_destroyed: *bool,
4318 all_args: []const []const u8,
4319 runtime_args_start: ?usize,
4320 link_libc: bool,
4321) !void {
4322 const raw_emit_bin = comp.emit_bin orelse return;
4323 const exe_path = switch (comp.cache_use) {
4324 .none => p: {
4325 if (fs.path.isAbsolute(raw_emit_bin)) break :p raw_emit_bin;
4326 // Use `fs.path.join` to make a file in the cwd is still executed properly.
4327 break :p try fs.path.join(arena, &.{
4328 ".",
4329 raw_emit_bin,
4330 });
4331 },
4332 .whole, .incremental => try comp.dirs.local_cache.join(arena, &.{
4333 "o",
4334 &Cache.binToHex(comp.digest.?),
4335 raw_emit_bin,
4336 }),
4337 };
4338
4339 var argv = std.array_list.Managed([]const u8).init(gpa);
4340 defer argv.deinit();
4341
4342 if (test_exec_args.len == 0) {
4343 try argv.append(exe_path);
4344 if (arg_mode == .zig_test) {
4345 try argv.append(
4346 try std.fmt.allocPrint(arena, "--seed=0x{x}", .{std.crypto.random.int(u32)}),
4347 );
4348 }
4349 } else {
4350 for (test_exec_args) |arg| {
4351 try argv.append(arg orelse exe_path);
4352 }
4353 }
4354 if (runtime_args_start) |i| {
4355 try argv.appendSlice(all_args[i..]);
4356 }
4357 var env_map = try process.getEnvMap(arena);
4358 try env_map.put("ZIG_EXE", self_exe_path);
4359
4360 // We do not execve for tests because if the test fails we want to print
4361 // the error message and invocation below.
4362 if (process.can_execv and arg_mode == .run) {
4363 // execv releases the locks; no need to destroy the Compilation here.
4364 std.debug.lockStdErr();
4365 const err = process.execve(gpa, argv.items, &env_map);
4366 std.debug.unlockStdErr();
4367 try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
4368 const cmd = try std.mem.join(arena, " ", argv.items);
4369 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
4370 } else if (process.can_spawn) {
4371 var child = std.process.Child.init(argv.items, gpa);
4372 child.env_map = &env_map;
4373 child.stdin_behavior = .Inherit;
4374 child.stdout_behavior = .Inherit;
4375 child.stderr_behavior = .Inherit;
4376
4377 // Here we release all the locks associated with the Compilation so
4378 // that whatever this child process wants to do won't deadlock.
4379 comp.destroy();
4380 comp_destroyed.* = true;
4381
4382 const term_result = t: {
4383 std.debug.lockStdErr();
4384 defer std.debug.unlockStdErr();
4385 break :t child.spawnAndWait();
4386 };
4387 const term = term_result catch |err| {
4388 try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
4389 const cmd = try std.mem.join(arena, " ", argv.items);
4390 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
4391 };
4392 switch (arg_mode) {
4393 .run, .build => {
4394 switch (term) {
4395 .Exited => |code| {
4396 if (code == 0) {
4397 return cleanExit();
4398 } else {
4399 process.exit(code);
4400 }
4401 },
4402 else => {
4403 process.exit(1);
4404 },
4405 }
4406 },
4407 .zig_test => {
4408 switch (term) {
4409 .Exited => |code| {
4410 if (code == 0) {
4411 return cleanExit();
4412 } else {
4413 const cmd = try std.mem.join(arena, " ", argv.items);
4414 fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
4415 }
4416 },
4417 else => {
4418 const cmd = try std.mem.join(arena, " ", argv.items);
4419 fatal("the following test command crashed:\n{s}", .{cmd});
4420 },
4421 }
4422 },
4423 else => unreachable,
4424 }
4425 } else {
4426 const cmd = try std.mem.join(arena, " ", argv.items);
4427 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd });
4428 }
4429}
4430
4431fn runOrTestHotSwap(
4432 comp: *Compilation,
4433 gpa: Allocator,
4434 test_exec_args: []const ?[]const u8,
4435 self_exe_path: []const u8,
4436 arg_mode: ArgMode,
4437 all_args: []const []const u8,
4438 runtime_args_start: ?usize,
4439) !std.process.Child.Id {
4440 const lf = comp.bin_file.?;
4441
4442 const exe_path = switch (builtin.target.os.tag) {
4443 // On Windows it seems impossible to perform an atomic rename of a file that is currently
4444 // running in a process. Therefore, we do the opposite. We create a copy of the file in
4445 // tmp zig-cache and use it to spawn the child process. This way we are free to update
4446 // the binary with each requested hot update.
4447 .windows => blk: {
4448 try lf.emit.root_dir.handle.copyFile(lf.emit.sub_path, comp.dirs.local_cache.handle, lf.emit.sub_path, .{});
4449 break :blk try fs.path.join(gpa, &.{ comp.dirs.local_cache.path orelse ".", lf.emit.sub_path });
4450 },
4451
4452 // A naive `directory.join` here will indeed get the correct path to the binary,
4453 // however, in the case of cwd, we actually want `./foo` so that the path can be executed.
4454 else => try fs.path.join(gpa, &.{
4455 lf.emit.root_dir.path orelse ".", lf.emit.sub_path,
4456 }),
4457 };
4458 defer gpa.free(exe_path);
4459
4460 var argv = std.array_list.Managed([]const u8).init(gpa);
4461 defer argv.deinit();
4462
4463 if (test_exec_args.len == 0) {
4464 // when testing pass the zig_exe_path to argv
4465 if (arg_mode == .zig_test)
4466 try argv.appendSlice(&[_][]const u8{
4467 exe_path, self_exe_path,
4468 })
4469 // when running just pass the current exe
4470 else
4471 try argv.appendSlice(&[_][]const u8{
4472 exe_path,
4473 });
4474 } else {
4475 for (test_exec_args) |arg| {
4476 if (arg) |a| {
4477 try argv.append(a);
4478 } else {
4479 try argv.appendSlice(&[_][]const u8{
4480 exe_path, self_exe_path,
4481 });
4482 }
4483 }
4484 }
4485 if (runtime_args_start) |i| {
4486 try argv.appendSlice(all_args[i..]);
4487 }
4488
4489 switch (builtin.target.os.tag) {
4490 .macos => {
4491 const PosixSpawn = @import("DarwinPosixSpawn.zig");
4492
4493 var attr = try PosixSpawn.Attr.init();
4494 defer attr.deinit();
4495
4496 // ASLR is probably a good default for better debugging experience/programming
4497 // with hot-code updates in mind. However, we can also make it work with ASLR on.
4498 try attr.set(.{
4499 .SETSIGDEF = true,
4500 .SETSIGMASK = true,
4501 .DISABLE_ASLR = true,
4502 });
4503
4504 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
4505 defer arena_allocator.deinit();
4506 const arena = arena_allocator.allocator();
4507
4508 const argv_buf = try arena.allocSentinel(?[*:0]u8, argv.items.len, null);
4509 for (argv.items, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
4510
4511 const pid = try PosixSpawn.spawn(argv.items[0], null, attr, argv_buf, std.c.environ);
4512 return pid;
4513 },
4514 else => {
4515 var child = std.process.Child.init(argv.items, gpa);
4516
4517 child.stdin_behavior = .Inherit;
4518 child.stdout_behavior = .Inherit;
4519 child.stderr_behavior = .Inherit;
4520
4521 try child.spawn();
4522
4523 return child.id;
4524 },
4525 }
4526}
4527
4528const UpdateModuleError = Compilation.UpdateError || error{
4529 /// The update caused compile errors. The error bundle has already been
4530 /// reported to the user by being rendered to stderr.
4531 CompileErrorsReported,
4532};
4533fn updateModule(comp: *Compilation, color: Color, prog_node: std.Progress.Node) UpdateModuleError!void {
4534 try comp.update(prog_node);
4535
4536 var errors = try comp.getAllErrorsAlloc();
4537 defer errors.deinit(comp.gpa);
4538
4539 if (errors.errorMessageCount() > 0) {
4540 errors.renderToStdErr(.{}, color);
4541 return error.CompileErrorsReported;
4542 }
4543}
4544
4545fn cmdTranslateC(
4546 comp: *Compilation,
4547 arena: Allocator,
4548 fancy_output: ?*Compilation.CImportResult,
4549 file_system_inputs: ?*std.ArrayList(u8),
4550 prog_node: std.Progress.Node,
4551) !void {
4552 dev.check(.translate_c_command);
4553
4554 const io = comp.io;
4555
4556 assert(comp.c_source_files.len == 1);
4557 const c_source_file = comp.c_source_files[0];
4558
4559 const translated_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.root_name});
4560
4561 var man: Cache.Manifest = comp.obtainCObjectCacheManifest(comp.root_mod);
4562 man.want_shared_lock = false;
4563 defer man.deinit();
4564
4565 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
4566 man.hash.add(comp.config.c_frontend);
4567 Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| {
4568 fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
4569 };
4570
4571 const result: Compilation.CImportResult = if (try man.hit()) .{
4572 .digest = man.finalBin(),
4573 .cache_hit = true,
4574 .errors = std.zig.ErrorBundle.empty,
4575 } else result: {
4576 const result = try comp.translateC(
4577 arena,
4578 &man,
4579 Compilation.classifyFileExt(c_source_file.src_path),
4580 .{ .path = c_source_file.src_path },
4581 translated_basename,
4582 comp.root_mod,
4583 prog_node,
4584 );
4585
4586 if (result.errors.errorMessageCount() != 0) {
4587 if (fancy_output) |p| {
4588 if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
4589 p.* = result;
4590 return;
4591 } else {
4592 const color: Color = .auto;
4593 result.errors.renderToStdErr(.{}, color);
4594 process.exit(1);
4595 }
4596 }
4597
4598 man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err});
4599 break :result result;
4600 };
4601
4602 if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
4603 if (fancy_output) |p| {
4604 p.* = result;
4605 } else {
4606 const hex_digest = Cache.binToHex(result.digest);
4607 const out_zig_path = try fs.path.join(arena, &.{ "o", &hex_digest, translated_basename });
4608 const zig_file = comp.dirs.local_cache.handle.openFile(out_zig_path, .{}) catch |err| {
4609 const path = comp.dirs.local_cache.path orelse ".";
4610 fatal("unable to open cached translated zig file '{s}{s}{s}': {s}", .{
4611 path,
4612 fs.path.sep_str,
4613 out_zig_path,
4614 @errorName(err),
4615 });
4616 };
4617 defer zig_file.close();
4618 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
4619 var file_reader = zig_file.reader(io, &.{});
4620 _ = try stdout_writer.interface.sendFileAll(&file_reader, .unlimited);
4621 try stdout_writer.interface.flush();
4622 return cleanExit();
4623 }
4624}
4625
4626pub fn translateC(
4627 gpa: Allocator,
4628 arena: Allocator,
4629 io: Io,
4630 argv: []const []const u8,
4631 prog_node: std.Progress.Node,
4632 capture: ?*[]u8,
4633) !void {
4634 try jitCmd(gpa, arena, io, argv, .{
4635 .cmd_name = "translate-c",
4636 .root_src_path = "translate-c/main.zig",
4637 .depend_on_aro = true,
4638 .progress_node = prog_node,
4639 .capture = capture,
4640 });
4641}
4642
4643const usage_init =
4644 \\Usage: zig init
4645 \\
4646 \\ Initializes a `zig build` project in the current working
4647 \\ directory.
4648 \\
4649 \\Options:
4650 \\ -m, --minimal Use minimal init template
4651 \\ -h, --help Print this help and exit
4652 \\
4653 \\
4654;
4655
4656fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4657 dev.check(.init_command);
4658
4659 var template: enum { example, minimal } = .example;
4660 {
4661 var i: usize = 0;
4662 while (i < args.len) : (i += 1) {
4663 const arg = args[i];
4664 if (mem.startsWith(u8, arg, "-")) {
4665 if (mem.eql(u8, arg, "-m") or mem.eql(u8, arg, "--minimal")) {
4666 template = .minimal;
4667 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
4668 try fs.File.stdout().writeAll(usage_init);
4669 return cleanExit();
4670 } else {
4671 fatal("unrecognized parameter: '{s}'", .{arg});
4672 }
4673 } else {
4674 fatal("unexpected extra parameter: '{s}'", .{arg});
4675 }
4676 }
4677 }
4678
4679 const cwd_path = try introspect.getResolvedCwd(arena);
4680 const cwd_basename = fs.path.basename(cwd_path);
4681 const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename);
4682
4683 const fingerprint: Package.Fingerprint = .generate(sanitized_root_name);
4684
4685 switch (template) {
4686 .example => {
4687 var templates = findTemplates(gpa, arena);
4688 defer templates.deinit();
4689
4690 const s = fs.path.sep_str;
4691 const template_paths = [_][]const u8{
4692 Package.build_zig_basename,
4693 Package.Manifest.basename,
4694 "src" ++ s ++ "main.zig",
4695 "src" ++ s ++ "root.zig",
4696 };
4697 var ok_count: usize = 0;
4698
4699 for (template_paths) |template_path| {
4700 if (templates.write(arena, fs.cwd(), sanitized_root_name, template_path, fingerprint)) |_| {
4701 std.log.info("created {s}", .{template_path});
4702 ok_count += 1;
4703 } else |err| switch (err) {
4704 error.PathAlreadyExists => std.log.info("preserving already existing file: {s}", .{
4705 template_path,
4706 }),
4707 else => std.log.err("unable to write {s}: {s}\n", .{ template_path, @errorName(err) }),
4708 }
4709 }
4710
4711 if (ok_count == template_paths.len) {
4712 std.log.info("see `zig build --help` for a menu of options", .{});
4713 }
4714 return cleanExit();
4715 },
4716 .minimal => {
4717 writeSimpleTemplateFile(Package.Manifest.basename,
4718 \\.{{
4719 \\ .name = .{s},
4720 \\ .version = "0.0.1",
4721 \\ .minimum_zig_version = "{s}",
4722 \\ .paths = .{{""}},
4723 \\ .fingerprint = 0x{x},
4724 \\}}
4725 \\
4726 , .{
4727 sanitized_root_name,
4728 build_options.version,
4729 fingerprint.int(),
4730 }) catch |err| switch (err) {
4731 else => fatal("failed to create '{s}': {s}", .{ Package.Manifest.basename, @errorName(err) }),
4732 error.PathAlreadyExists => fatal("refusing to overwrite '{s}'", .{Package.Manifest.basename}),
4733 };
4734 writeSimpleTemplateFile(Package.build_zig_basename,
4735 \\const std = @import("std");
4736 \\
4737 \\pub fn build(b: *std.Build) void {{
4738 \\ _ = b; // stub
4739 \\}}
4740 \\
4741 , .{}) catch |err| switch (err) {
4742 else => fatal("failed to create '{s}': {s}", .{ Package.build_zig_basename, @errorName(err) }),
4743 // `build.zig` already existing is okay: the user has just used `zig init` to set up
4744 // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal.
4745 error.PathAlreadyExists => {
4746 std.log.info("successfully populated '{s}', preserving existing '{s}'", .{ Package.Manifest.basename, Package.build_zig_basename });
4747 return cleanExit();
4748 },
4749 };
4750 std.log.info("successfully populated '{s}' and '{s}'", .{ Package.Manifest.basename, Package.build_zig_basename });
4751 return cleanExit();
4752 },
4753 }
4754}
4755
4756fn sanitizeExampleName(arena: Allocator, bytes: []const u8) error{OutOfMemory}![]const u8 {
4757 var result: std.ArrayList(u8) = .empty;
4758 for (bytes, 0..) |byte, i| switch (byte) {
4759 '0'...'9' => {
4760 if (i == 0) try result.append(arena, '_');
4761 try result.append(arena, byte);
4762 },
4763 '_', 'a'...'z', 'A'...'Z' => try result.append(arena, byte),
4764 '-', '.', ' ' => try result.append(arena, '_'),
4765 else => continue,
4766 };
4767 if (!std.zig.isValidId(result.items)) return "foo";
4768 if (result.items.len > Package.Manifest.max_name_len)
4769 result.shrinkRetainingCapacity(Package.Manifest.max_name_len);
4770
4771 return result.toOwnedSlice(arena);
4772}
4773
4774test sanitizeExampleName {
4775 var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator);
4776 defer arena_instance.deinit();
4777 const arena = arena_instance.allocator();
4778
4779 try std.testing.expectEqualStrings("foo_bar", try sanitizeExampleName(arena, "foo bar+"));
4780 try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, ""));
4781 try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "!"));
4782 try std.testing.expectEqualStrings("a", try sanitizeExampleName(arena, "!a"));
4783 try std.testing.expectEqualStrings("a_b", try sanitizeExampleName(arena, "a.b!"));
4784 try std.testing.expectEqualStrings("_01234", try sanitizeExampleName(arena, "01234"));
4785 try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "error"));
4786 try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "test"));
4787 try std.testing.expectEqualStrings("tests", try sanitizeExampleName(arena, "tests"));
4788 try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project"));
4789}
4790
4791fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void {
4792 dev.check(.build_command);
4793
4794 var build_file: ?[]const u8 = null;
4795 var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
4796 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
4797 var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena);
4798 var override_build_runner: ?[]const u8 = try EnvVar.ZIG_BUILD_RUNNER.get(arena);
4799 var child_argv = std.array_list.Managed([]const u8).init(arena);
4800 var reference_trace: ?u32 = null;
4801 var debug_compile_errors = false;
4802 var verbose_link = (native_os != .wasi or builtin.link_libc) and
4803 EnvVar.ZIG_VERBOSE_LINK.isSet();
4804 var verbose_cc = (native_os != .wasi or builtin.link_libc) and
4805 EnvVar.ZIG_VERBOSE_CC.isSet();
4806 var verbose_air = false;
4807 var verbose_intern_pool = false;
4808 var verbose_generic_instances = false;
4809 var verbose_llvm_ir: ?[]const u8 = null;
4810 var verbose_llvm_bc: ?[]const u8 = null;
4811 var verbose_cimport = false;
4812 var verbose_llvm_cpu_features = false;
4813 var fetch_only = false;
4814 var fetch_mode: Package.Fetch.JobQueue.Mode = .needed;
4815 var system_pkg_dir_path: ?[]const u8 = null;
4816 var debug_target: ?[]const u8 = null;
4817 var debug_libc_paths_file: ?[]const u8 = null;
4818
4819 const argv_index_exe = child_argv.items.len;
4820 _ = try child_argv.addOne();
4821
4822 const self_exe_path = try fs.selfExePathAlloc(arena);
4823 try child_argv.append(self_exe_path);
4824
4825 const argv_index_zig_lib_dir = child_argv.items.len;
4826 _ = try child_argv.addOne();
4827
4828 const argv_index_build_file = child_argv.items.len;
4829 _ = try child_argv.addOne();
4830
4831 const argv_index_cache_dir = child_argv.items.len;
4832 _ = try child_argv.addOne();
4833
4834 const argv_index_global_cache_dir = child_argv.items.len;
4835 _ = try child_argv.addOne();
4836
4837 try child_argv.appendSlice(&.{
4838 "--seed",
4839 try std.fmt.allocPrint(arena, "0x{x}", .{std.crypto.random.int(u32)}),
4840 });
4841 const argv_index_seed = child_argv.items.len - 1;
4842
4843 // This parent process needs a way to obtain results from the configuration
4844 // phase of the child process. In the future, the make phase will be
4845 // executed in a separate process than the configure phase, and we can then
4846 // use stdout from the configuration phase for this purpose.
4847 //
4848 // However, currently, both phases are in the same process, and Run Step
4849 // provides API for making the runned subprocesses inherit stdout and stderr
4850 // which means these streams are not available for passing metadata back
4851 // to the parent.
4852 //
4853 // Until make and configure phases are separated into different processes,
4854 // the strategy is to choose a temporary file name ahead of time, and then
4855 // read this file in the parent to obtain the results, in the case the child
4856 // exits with code 3.
4857 const results_tmp_file_nonce = std.fmt.hex(std.crypto.random.int(u64));
4858 try child_argv.append("-Z" ++ results_tmp_file_nonce);
4859
4860 var color: Color = .auto;
4861 var n_jobs: ?u32 = null;
4862
4863 {
4864 var i: usize = 0;
4865 while (i < args.len) : (i += 1) {
4866 const arg = args[i];
4867 if (mem.startsWith(u8, arg, "-")) {
4868 if (mem.eql(u8, arg, "--build-file")) {
4869 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4870 i += 1;
4871 build_file = args[i];
4872 continue;
4873 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
4874 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4875 i += 1;
4876 override_lib_dir = args[i];
4877 continue;
4878 } else if (mem.eql(u8, arg, "--build-runner")) {
4879 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4880 i += 1;
4881 override_build_runner = args[i];
4882 continue;
4883 } else if (mem.eql(u8, arg, "--cache-dir")) {
4884 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4885 i += 1;
4886 override_local_cache_dir = args[i];
4887 continue;
4888 } else if (mem.eql(u8, arg, "--global-cache-dir")) {
4889 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4890 i += 1;
4891 override_global_cache_dir = args[i];
4892 continue;
4893 } else if (mem.eql(u8, arg, "-freference-trace")) {
4894 reference_trace = 256;
4895 } else if (mem.eql(u8, arg, "--fetch")) {
4896 fetch_only = true;
4897 } else if (mem.cutPrefix(u8, arg, "--fetch=")) |sub_arg| {
4898 fetch_only = true;
4899 fetch_mode = std.meta.stringToEnum(Package.Fetch.JobQueue.Mode, sub_arg) orelse
4900 fatal("expected [needed|all] after '--fetch=', found '{s}'", .{
4901 sub_arg,
4902 });
4903 } else if (mem.eql(u8, arg, "--system")) {
4904 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4905 i += 1;
4906 system_pkg_dir_path = args[i];
4907 try child_argv.append("--system");
4908 continue;
4909 } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {
4910 reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
4911 fatal("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) });
4912 };
4913 } else if (mem.eql(u8, arg, "-fno-reference-trace")) {
4914 reference_trace = null;
4915 } else if (mem.eql(u8, arg, "--debug-log")) {
4916 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4917 try child_argv.appendSlice(args[i .. i + 2]);
4918 i += 1;
4919 if (!build_options.enable_logging) {
4920 warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
4921 } else {
4922 try log_scopes.append(arena, args[i]);
4923 }
4924 continue;
4925 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
4926 if (build_options.enable_debug_extensions) {
4927 debug_compile_errors = true;
4928 } else {
4929 warn("Zig was compiled without debug extensions. --debug-compile-errors has no effect.", .{});
4930 }
4931 } else if (mem.eql(u8, arg, "--debug-target")) {
4932 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4933 i += 1;
4934 if (build_options.enable_debug_extensions) {
4935 debug_target = args[i];
4936 } else {
4937 warn("Zig was compiled without debug extensions. --debug-target has no effect.", .{});
4938 }
4939 } else if (mem.eql(u8, arg, "--debug-libc")) {
4940 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4941 i += 1;
4942 if (build_options.enable_debug_extensions) {
4943 debug_libc_paths_file = args[i];
4944 } else {
4945 warn("Zig was compiled without debug extensions. --debug-libc has no effect.", .{});
4946 }
4947 } else if (mem.eql(u8, arg, "--verbose-link")) {
4948 verbose_link = true;
4949 } else if (mem.eql(u8, arg, "--verbose-cc")) {
4950 verbose_cc = true;
4951 } else if (mem.eql(u8, arg, "--verbose-air")) {
4952 verbose_air = true;
4953 } else if (mem.eql(u8, arg, "--verbose-intern-pool")) {
4954 verbose_intern_pool = true;
4955 } else if (mem.eql(u8, arg, "--verbose-generic-instances")) {
4956 verbose_generic_instances = true;
4957 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
4958 verbose_llvm_ir = "-";
4959 } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-ir=")) |rest| {
4960 verbose_llvm_ir = rest;
4961 } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-bc=")) |rest| {
4962 verbose_llvm_bc = rest;
4963 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
4964 verbose_cimport = true;
4965 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
4966 verbose_llvm_cpu_features = true;
4967 } else if (mem.eql(u8, arg, "--color")) {
4968 if (i + 1 >= args.len) fatal("expected [auto|on|off] after {s}", .{arg});
4969 i += 1;
4970 color = std.meta.stringToEnum(Color, args[i]) orelse {
4971 fatal("expected [auto|on|off] after {s}, found '{s}'", .{ arg, args[i] });
4972 };
4973 try child_argv.appendSlice(&.{ arg, args[i] });
4974 continue;
4975 } else if (mem.cutPrefix(u8, arg, "-j")) |str| {
4976 const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| {
4977 fatal("unable to parse jobs count '{s}': {s}", .{
4978 str, @errorName(err),
4979 });
4980 };
4981 if (num < 1) {
4982 fatal("number of jobs must be at least 1\n", .{});
4983 }
4984 n_jobs = num;
4985 } else if (mem.eql(u8, arg, "--seed")) {
4986 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4987 i += 1;
4988 child_argv.items[argv_index_seed] = args[i];
4989 continue;
4990 } else if (mem.eql(u8, arg, "--")) {
4991 // The rest of the args are supposed to get passed onto
4992 // build runner's `build.args`
4993 try child_argv.appendSlice(args[i..]);
4994 break;
4995 }
4996 }
4997 try child_argv.append(arg);
4998 }
4999 }
5000
5001 const work_around_btrfs_bug = native_os == .linux and
5002 EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
5003 const root_prog_node = std.Progress.start(.{
5004 .disable_printing = (color == .off),
5005 .root_name = "Compile Build Script",
5006 });
5007 defer root_prog_node.end();
5008
5009 // Normally the build runner is compiled for the host target but here is
5010 // some code to help when debugging edits to the build runner so that you
5011 // can make sure it compiles successfully on other targets.
5012 const resolved_target: Package.Module.ResolvedTarget = t: {
5013 if (build_options.enable_debug_extensions) {
5014 if (debug_target) |triple| {
5015 const target_query = try std.Target.Query.parse(.{
5016 .arch_os_abi = triple,
5017 });
5018 break :t .{
5019 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
5020 .is_native_os = false,
5021 .is_native_abi = false,
5022 .is_explicit_dynamic_linker = false,
5023 };
5024 }
5025 }
5026 break :t .{
5027 .result = std.zig.resolveTargetQueryOrFatal(io, .{}),
5028 .is_native_os = true,
5029 .is_native_abi = true,
5030 .is_explicit_dynamic_linker = false,
5031 };
5032 };
5033 // Likewise, `--debug-libc` allows overriding the libc installation.
5034 const libc_installation: ?*const LibCInstallation = lci: {
5035 const paths_file = debug_libc_paths_file orelse break :lci null;
5036 if (!build_options.enable_debug_extensions) unreachable;
5037 const lci = try arena.create(LibCInstallation);
5038 lci.* = try .parse(arena, paths_file, &resolved_target.result);
5039 break :lci lci;
5040 };
5041
5042 process.raiseFileDescriptorLimit();
5043
5044 const cwd_path = try introspect.getResolvedCwd(arena);
5045 const build_root = try findBuildRoot(arena, .{
5046 .cwd_path = cwd_path,
5047 .build_file = build_file,
5048 });
5049
5050 // This `init` calls `fatal` on error.
5051 var dirs: Compilation.Directories = .init(
5052 arena,
5053 override_lib_dir,
5054 override_global_cache_dir,
5055 .{ .override = path: {
5056 if (override_local_cache_dir) |d| break :path d;
5057 break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename});
5058 } },
5059 {},
5060 self_exe_path,
5061 );
5062 defer dirs.deinit();
5063
5064 child_argv.items[argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path;
5065 child_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;
5066 child_argv.items[argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path;
5067 child_argv.items[argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path;
5068
5069 var thread_pool: ThreadPool = undefined;
5070 try thread_pool.init(.{
5071 .allocator = gpa,
5072 .n_jobs = @min(@max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
5073 .track_ids = true,
5074 .stack_size = thread_stack_size,
5075 });
5076 defer thread_pool.deinit();
5077
5078 // Dummy http client that is not actually used when fetch_command is unsupported.
5079 // Prevents bootstrap from depending on a bunch of unnecessary stuff.
5080 var http_client: if (dev.env.supports(.fetch_command)) std.http.Client else struct {
5081 allocator: Allocator,
5082 io: Io,
5083 fn deinit(_: @This()) void {}
5084 } = .{ .allocator = gpa, .io = io };
5085 defer http_client.deinit();
5086
5087 var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{};
5088
5089 // This loop is re-evaluated when the build script exits with an indication that it
5090 // could not continue due to missing lazy dependencies.
5091 while (true) {
5092 // We want to release all the locks before executing the child process, so we make a nice
5093 // big block here to ensure the cleanup gets run when we extract out our argv.
5094 {
5095 const main_mod_paths: Package.Module.CreateOptions.Paths = if (override_build_runner) |runner| .{
5096 .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(runner) orelse "."}),
5097 .root_src_path = fs.path.basename(runner),
5098 } else .{
5099 .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"),
5100 .root_src_path = "build_runner.zig",
5101 };
5102
5103 const config = try Compilation.Config.resolve(.{
5104 .output_mode = .Exe,
5105 .resolved_target = resolved_target,
5106 .have_zcu = true,
5107 .emit_bin = true,
5108 .is_test = false,
5109 });
5110
5111 const root_mod = try Package.Module.create(arena, .{
5112 .paths = main_mod_paths,
5113 .fully_qualified_name = "root",
5114 .cc_argv = &.{},
5115 .inherited = .{
5116 .resolved_target = resolved_target,
5117 },
5118 .global = config,
5119 .parent = null,
5120 });
5121
5122 const build_mod = try Package.Module.create(arena, .{
5123 .paths = .{
5124 .root = try .fromUnresolved(arena, dirs, &.{build_root.directory.path orelse "."}),
5125 .root_src_path = build_root.build_zig_basename,
5126 },
5127 .fully_qualified_name = "root.@build",
5128 .cc_argv = &.{},
5129 .inherited = .{},
5130 .global = config,
5131 .parent = root_mod,
5132 });
5133
5134 var cleanup_build_dir: ?fs.Dir = null;
5135 defer if (cleanup_build_dir) |*dir| dir.close();
5136
5137 if (dev.env.supports(.fetch_command)) {
5138 const fetch_prog_node = root_prog_node.start("Fetch Packages", 0);
5139 defer fetch_prog_node.end();
5140
5141 var job_queue: Package.Fetch.JobQueue = .{
5142 .io = io,
5143 .http_client = &http_client,
5144 .global_cache = dirs.global_cache,
5145 .read_only = false,
5146 .recursive = true,
5147 .debug_hash = false,
5148 .work_around_btrfs_bug = work_around_btrfs_bug,
5149 .unlazy_set = unlazy_set,
5150 .mode = fetch_mode,
5151 };
5152 defer job_queue.deinit();
5153
5154 if (system_pkg_dir_path) |p| {
5155 job_queue.global_cache = .{
5156 .path = p,
5157 .handle = fs.cwd().openDir(p, .{}) catch |err| {
5158 fatal("unable to open system package directory '{s}': {s}", .{
5159 p, @errorName(err),
5160 });
5161 },
5162 };
5163 job_queue.read_only = true;
5164 cleanup_build_dir = job_queue.global_cache.handle;
5165 } else {
5166 try http_client.initDefaultProxies(arena);
5167 }
5168
5169 try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);
5170 try job_queue.table.ensureUnusedCapacity(gpa, 1);
5171
5172 const phantom_package_root: Cache.Path = .{ .root_dir = build_root.directory };
5173
5174 var fetch: Package.Fetch = .{
5175 .arena = std.heap.ArenaAllocator.init(gpa),
5176 .location = .{ .relative_path = phantom_package_root },
5177 .location_tok = 0,
5178 .hash_tok = .none,
5179 .name_tok = 0,
5180 .lazy_status = .eager,
5181 .parent_package_root = phantom_package_root,
5182 .parent_manifest_ast = null,
5183 .prog_node = fetch_prog_node,
5184 .job_queue = &job_queue,
5185 .omit_missing_hash_error = true,
5186 .allow_missing_paths_field = false,
5187 .allow_missing_fingerprint = false,
5188 .allow_name_string = false,
5189 .use_latest_commit = false,
5190
5191 .package_root = undefined,
5192 .error_bundle = undefined,
5193 .manifest = null,
5194 .manifest_ast = undefined,
5195 .computed_hash = undefined,
5196 .has_build_zig = true,
5197 .oom_flag = false,
5198 .latest_commit = null,
5199
5200 .module = build_mod,
5201 };
5202 job_queue.all_fetches.appendAssumeCapacity(&fetch);
5203
5204 job_queue.table.putAssumeCapacityNoClobber(
5205 Package.Fetch.relativePathDigest(phantom_package_root, dirs.global_cache),
5206 &fetch,
5207 );
5208
5209 job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" });
5210 job_queue.group.wait(io);
5211
5212 try job_queue.consolidateErrors();
5213
5214 if (fetch.error_bundle.root_list.items.len > 0) {
5215 var errors = try fetch.error_bundle.toOwnedBundle("");
5216 errors.renderToStdErr(.{}, color);
5217 process.exit(1);
5218 }
5219
5220 if (fetch_only) return cleanExit();
5221
5222 var source_buf = std.array_list.Managed(u8).init(gpa);
5223 defer source_buf.deinit();
5224 try job_queue.createDependenciesSource(&source_buf);
5225 const deps_mod = try createDependenciesModule(
5226 arena,
5227 source_buf.items,
5228 root_mod,
5229 dirs,
5230 config,
5231 );
5232
5233 {
5234 // We need a Module for each package's build.zig.
5235 const hashes = job_queue.table.keys();
5236 const fetches = job_queue.table.values();
5237 try deps_mod.deps.ensureUnusedCapacity(arena, @intCast(hashes.len));
5238 for (hashes, fetches) |*hash, f| {
5239 if (f == &fetch) {
5240 // The first one is a dummy package for the current project.
5241 continue;
5242 }
5243 if (!f.has_build_zig)
5244 continue;
5245 const hash_slice = hash.toSlice();
5246 const mod_root_path = try f.package_root.toString(arena);
5247 const m = try Package.Module.create(arena, .{
5248 .paths = .{
5249 .root = try .fromUnresolved(arena, dirs, &.{mod_root_path}),
5250 .root_src_path = Package.build_zig_basename,
5251 },
5252 .fully_qualified_name = try std.fmt.allocPrint(
5253 arena,
5254 "root.@dependencies.{s}",
5255 .{hash_slice},
5256 ),
5257 .cc_argv = &.{},
5258 .inherited = .{},
5259 .global = config,
5260 .parent = root_mod,
5261 });
5262 const hash_cloned = try arena.dupe(u8, hash_slice);
5263 deps_mod.deps.putAssumeCapacityNoClobber(hash_cloned, m);
5264 f.module = m;
5265 }
5266
5267 // Each build.zig module needs access to each of its
5268 // dependencies' build.zig modules by name.
5269 for (fetches) |f| {
5270 const mod = f.module orelse continue;
5271 const man = f.manifest orelse continue;
5272 const dep_names = man.dependencies.keys();
5273 try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len));
5274 for (dep_names, man.dependencies.values()) |name, dep| {
5275 const dep_digest = Package.Fetch.depDigest(
5276 f.package_root,
5277 dirs.global_cache,
5278 dep,
5279 ) orelse continue;
5280 const dep_mod = job_queue.table.get(dep_digest).?.module orelse continue;
5281 const name_cloned = try arena.dupe(u8, name);
5282 mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod);
5283 }
5284 }
5285 }
5286 } else try createEmptyDependenciesModule(
5287 arena,
5288 root_mod,
5289 dirs,
5290 config,
5291 );
5292
5293 try root_mod.deps.put(arena, "@build", build_mod);
5294
5295 var create_diag: Compilation.CreateDiagnostic = undefined;
5296 const comp = Compilation.create(gpa, arena, io, &create_diag, .{
5297 .libc_installation = libc_installation,
5298 .dirs = dirs,
5299 .root_name = "build",
5300 .config = config,
5301 .root_mod = root_mod,
5302 .main_mod = build_mod,
5303 .emit_bin = .yes_cache,
5304 .self_exe_path = self_exe_path,
5305 .thread_pool = &thread_pool,
5306 .verbose_cc = verbose_cc,
5307 .verbose_link = verbose_link,
5308 .verbose_air = verbose_air,
5309 .verbose_intern_pool = verbose_intern_pool,
5310 .verbose_generic_instances = verbose_generic_instances,
5311 .verbose_llvm_ir = verbose_llvm_ir,
5312 .verbose_llvm_bc = verbose_llvm_bc,
5313 .verbose_cimport = verbose_cimport,
5314 .verbose_llvm_cpu_features = verbose_llvm_cpu_features,
5315 .cache_mode = .whole,
5316 .reference_trace = reference_trace,
5317 .debug_compile_errors = debug_compile_errors,
5318 }) catch |err| switch (err) {
5319 error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}),
5320 else => fatal("failed to create compilation: {s}", .{@errorName(err)}),
5321 };
5322 defer comp.destroy();
5323
5324 updateModule(comp, color, root_prog_node) catch |err| switch (err) {
5325 error.CompileErrorsReported => process.exit(2),
5326 else => |e| return e,
5327 };
5328
5329 // Since incremental compilation isn't done yet, we use cache_mode = whole
5330 // above, and thus the output file is already closed.
5331 //try comp.makeBinFileExecutable();
5332 child_argv.items[argv_index_exe] = try dirs.local_cache.join(arena, &.{
5333 "o",
5334 &Cache.binToHex(comp.digest.?),
5335 comp.emit_bin.?,
5336 });
5337 }
5338
5339 if (process.can_spawn) {
5340 var child = std.process.Child.init(child_argv.items, gpa);
5341 child.stdin_behavior = .Inherit;
5342 child.stdout_behavior = .Inherit;
5343 child.stderr_behavior = .Inherit;
5344
5345 const term = t: {
5346 std.debug.lockStdErr();
5347 defer std.debug.unlockStdErr();
5348 break :t child.spawnAndWait() catch |err| {
5349 fatal("failed to spawn build runner {s}: {s}", .{ child_argv.items[0], @errorName(err) });
5350 };
5351 };
5352
5353 switch (term) {
5354 .Exited => |code| {
5355 if (code == 0) return cleanExit();
5356 // Indicates that the build runner has reported compile errors
5357 // and this parent process does not need to report any further
5358 // diagnostics.
5359 if (code == 2) process.exit(2);
5360
5361 if (code == 3) {
5362 if (!dev.env.supports(.fetch_command)) process.exit(3);
5363 // Indicates the configure phase failed due to missing lazy
5364 // dependencies and stdout contains the hashes of the ones
5365 // that are missing.
5366 const s = fs.path.sep_str;
5367 const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce;
5368 const stdout = dirs.local_cache.handle.readFileAlloc(tmp_sub_path, arena, .limited(50 * 1024 * 1024)) catch |err| {
5369 fatal("unable to read results of configure phase from '{f}{s}': {s}", .{
5370 dirs.local_cache, tmp_sub_path, @errorName(err),
5371 });
5372 };
5373 dirs.local_cache.handle.deleteFile(tmp_sub_path) catch {};
5374
5375 var it = mem.splitScalar(u8, stdout, '\n');
5376 var any_errors = false;
5377 while (it.next()) |hash| {
5378 if (hash.len == 0) continue;
5379 if (hash.len > Package.Hash.max_len) {
5380 std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{
5381 hash.len, hash,
5382 });
5383 any_errors = true;
5384 continue;
5385 }
5386 try unlazy_set.put(arena, .fromSlice(hash), {});
5387 }
5388 if (any_errors) process.exit(3);
5389 if (system_pkg_dir_path) |p| {
5390 // In this mode, the system needs to provide these packages; they
5391 // cannot be fetched by Zig.
5392 for (unlazy_set.keys()) |*hash| {
5393 std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{
5394 p, hash.toSlice(),
5395 });
5396 }
5397 std.log.info("remote package fetching disabled due to --system mode", .{});
5398 std.log.info("dependencies might be avoidable depending on build configuration", .{});
5399 process.exit(3);
5400 }
5401 continue;
5402 }
5403
5404 const cmd = try std.mem.join(arena, " ", child_argv.items);
5405 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
5406 },
5407 else => {
5408 const cmd = try std.mem.join(arena, " ", child_argv.items);
5409 fatal("the following build command crashed:\n{s}", .{cmd});
5410 },
5411 }
5412 } else {
5413 const cmd = try std.mem.join(arena, " ", child_argv.items);
5414 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd });
5415 }
5416 }
5417}
5418
5419const JitCmdOptions = struct {
5420 cmd_name: []const u8,
5421 root_src_path: []const u8,
5422 prepend_zig_lib_dir_path: bool = false,
5423 prepend_global_cache_path: bool = false,
5424 prepend_zig_exe_path: bool = false,
5425 depend_on_aro: bool = false,
5426 capture: ?*[]u8 = null,
5427 /// Send error bundles via std.zig.Server over stdout
5428 server: bool = false,
5429 progress_node: ?std.Progress.Node = null,
5430};
5431
5432fn jitCmd(
5433 gpa: Allocator,
5434 arena: Allocator,
5435 io: Io,
5436 args: []const []const u8,
5437 options: JitCmdOptions,
5438) !void {
5439 dev.check(.jit_command);
5440
5441 const color: Color = .auto;
5442 const root_prog_node = if (options.progress_node) |node| node else std.Progress.start(.{
5443 .disable_printing = (color == .off),
5444 });
5445
5446 const target_query: std.Target.Query = .{};
5447 const resolved_target: Package.Module.ResolvedTarget = .{
5448 .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
5449 .is_native_os = true,
5450 .is_native_abi = true,
5451 .is_explicit_dynamic_linker = false,
5452 };
5453
5454 const self_exe_path = fs.selfExePathAlloc(arena) catch |err| {
5455 fatal("unable to find self exe path: {s}", .{@errorName(err)});
5456 };
5457
5458 const optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet())
5459 .Debug
5460 else
5461 .ReleaseFast;
5462 const strip = optimize_mode != .Debug;
5463 const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
5464 const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
5465
5466 // This `init` calls `fatal` on error.
5467 var dirs: Compilation.Directories = .init(
5468 arena,
5469 override_lib_dir,
5470 override_global_cache_dir,
5471 .global,
5472 if (native_os == .wasi) wasi_preopens,
5473 self_exe_path,
5474 );
5475 defer dirs.deinit();
5476
5477 var thread_pool: ThreadPool = undefined;
5478 try thread_pool.init(.{
5479 .allocator = gpa,
5480 .n_jobs = @min(@max(std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
5481 .track_ids = true,
5482 .stack_size = thread_stack_size,
5483 });
5484 defer thread_pool.deinit();
5485
5486 var child_argv: std.ArrayList([]const u8) = .empty;
5487 try child_argv.ensureUnusedCapacity(arena, args.len + 4);
5488
5489 // We want to release all the locks before executing the child process, so we make a nice
5490 // big block here to ensure the cleanup gets run when we extract out our argv.
5491 {
5492 const main_mod_paths: Package.Module.CreateOptions.Paths = .{
5493 .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"),
5494 .root_src_path = options.root_src_path,
5495 };
5496
5497 const config = try Compilation.Config.resolve(.{
5498 .output_mode = .Exe,
5499 .root_strip = strip,
5500 .root_optimize_mode = optimize_mode,
5501 .resolved_target = resolved_target,
5502 .have_zcu = true,
5503 .emit_bin = true,
5504 .is_test = false,
5505 });
5506
5507 const root_mod = try Package.Module.create(arena, .{
5508 .paths = main_mod_paths,
5509 .fully_qualified_name = "root",
5510 .cc_argv = &.{},
5511 .inherited = .{
5512 .resolved_target = resolved_target,
5513 .optimize_mode = optimize_mode,
5514 .strip = strip,
5515 },
5516 .global = config,
5517 .parent = null,
5518 });
5519
5520 if (options.depend_on_aro) {
5521 const aro_mod = try Package.Module.create(arena, .{
5522 .paths = .{
5523 .root = try .fromRoot(arena, dirs, .zig_lib, "compiler/aro"),
5524 .root_src_path = "aro.zig",
5525 },
5526 .fully_qualified_name = "aro",
5527 .cc_argv = &.{},
5528 .inherited = .{
5529 .resolved_target = resolved_target,
5530 .optimize_mode = optimize_mode,
5531 .strip = strip,
5532 },
5533 .global = config,
5534 .parent = null,
5535 });
5536 try root_mod.deps.put(arena, "aro", aro_mod);
5537 }
5538
5539 var create_diag: Compilation.CreateDiagnostic = undefined;
5540 const comp = Compilation.create(gpa, arena, io, &create_diag, .{
5541 .dirs = dirs,
5542 .root_name = options.cmd_name,
5543 .config = config,
5544 .root_mod = root_mod,
5545 .main_mod = root_mod,
5546 .emit_bin = .yes_cache,
5547 .self_exe_path = self_exe_path,
5548 .thread_pool = &thread_pool,
5549 .cache_mode = .whole,
5550 }) catch |err| switch (err) {
5551 error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}),
5552 else => fatal("failed to create compilation: {s}", .{@errorName(err)}),
5553 };
5554 defer comp.destroy();
5555
5556 if (options.server) {
5557 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
5558 var server: std.zig.Server = .{
5559 .out = &stdout_writer.interface,
5560 .in = undefined, // won't be receiving messages
5561 };
5562
5563 try comp.update(root_prog_node);
5564
5565 var error_bundle = try comp.getAllErrorsAlloc();
5566 defer error_bundle.deinit(comp.gpa);
5567 if (error_bundle.errorMessageCount() > 0) {
5568 try server.serveErrorBundle(error_bundle);
5569 process.exit(2);
5570 }
5571 } else {
5572 updateModule(comp, color, root_prog_node) catch |err| switch (err) {
5573 error.CompileErrorsReported => process.exit(2),
5574 else => |e| return e,
5575 };
5576 }
5577
5578 const exe_path = try dirs.global_cache.join(arena, &.{
5579 "o",
5580 &Cache.binToHex(comp.digest.?),
5581 comp.emit_bin.?,
5582 });
5583 child_argv.appendAssumeCapacity(exe_path);
5584 }
5585
5586 if (options.prepend_zig_lib_dir_path)
5587 child_argv.appendAssumeCapacity(dirs.zig_lib.path.?);
5588 if (options.prepend_zig_exe_path)
5589 child_argv.appendAssumeCapacity(self_exe_path);
5590 if (options.prepend_global_cache_path)
5591 child_argv.appendAssumeCapacity(dirs.global_cache.path.?);
5592
5593 child_argv.appendSliceAssumeCapacity(args);
5594
5595 if (process.can_execv and options.capture == null) {
5596 if (EnvVar.ZIG_DEBUG_CMD.isSet()) {
5597 const cmd = try std.mem.join(arena, " ", child_argv.items);
5598 std.debug.print("{s}\n", .{cmd});
5599 }
5600 const err = process.execv(gpa, child_argv.items);
5601 const cmd = try std.mem.join(arena, " ", child_argv.items);
5602 fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd });
5603 }
5604
5605 if (!process.can_spawn) {
5606 const cmd = try std.mem.join(arena, " ", child_argv.items);
5607 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
5608 @tagName(native_os), cmd,
5609 });
5610 }
5611
5612 var child = std.process.Child.init(child_argv.items, gpa);
5613 child.stdin_behavior = .Inherit;
5614 child.stdout_behavior = if (options.capture == null) .Inherit else .Pipe;
5615 child.stderr_behavior = .Inherit;
5616
5617 try child.spawn();
5618
5619 if (options.capture) |ptr| {
5620 var stdout_reader = child.stdout.?.readerStreaming(io, &.{});
5621 ptr.* = try stdout_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32)));
5622 }
5623
5624 const term = try child.wait();
5625 switch (term) {
5626 .Exited => |code| {
5627 if (code == 0) {
5628 if (options.capture != null) return;
5629 return cleanExit();
5630 }
5631 const cmd = try std.mem.join(arena, " ", child_argv.items);
5632 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
5633 },
5634 else => {
5635 const cmd = try std.mem.join(arena, " ", child_argv.items);
5636 fatal("the following build command crashed:\n{s}", .{cmd});
5637 },
5638 }
5639}
5640
5641const info_zen =
5642 \\
5643 \\ * Communicate intent precisely.
5644 \\ * Edge cases matter.
5645 \\ * Favor reading code over writing code.
5646 \\ * Only one obvious way to do things.
5647 \\ * Runtime crashes are better than bugs.
5648 \\ * Compile errors are better than runtime crashes.
5649 \\ * Incremental improvements.
5650 \\ * Avoid local maximums.
5651 \\ * Reduce the amount one must remember.
5652 \\ * Focus on code rather than style.
5653 \\ * Resource allocation may fail; resource deallocation must succeed.
5654 \\ * Memory is a resource.
5655 \\ * Together we serve the users.
5656 \\
5657 \\
5658;
5659
5660extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
5661
5662extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
5663extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
5664
5665fn argsCopyZ(alloc: Allocator, args: []const []const u8) ![:null]?[*:0]u8 {
5666 var argv = try alloc.allocSentinel(?[*:0]u8, args.len, null);
5667 for (args, 0..) |arg, i| {
5668 argv[i] = try alloc.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
5669 }
5670 return argv;
5671}
5672
5673pub fn clangMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 {
5674 if (!build_options.have_llvm)
5675 fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{});
5676
5677 var arena_instance = std.heap.ArenaAllocator.init(alloc);
5678 defer arena_instance.deinit();
5679 const arena = arena_instance.allocator();
5680
5681 // Convert the args to the null-terminated format Clang expects.
5682 const argv = try argsCopyZ(arena, args);
5683 const exit_code = ZigClang_main(@as(c_int, @intCast(argv.len)), argv.ptr);
5684 return @as(u8, @bitCast(@as(i8, @truncate(exit_code))));
5685}
5686
5687pub fn llvmArMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 {
5688 if (!build_options.have_llvm)
5689 fatal("`zig ar`, `zig dlltool`, `zig ranlib', and `zig lib` unavailable: compiler built without LLVM extensions", .{});
5690
5691 var arena_instance = std.heap.ArenaAllocator.init(alloc);
5692 defer arena_instance.deinit();
5693 const arena = arena_instance.allocator();
5694
5695 // Convert the args to the format llvm-ar expects.
5696 // We intentionally shave off the zig binary at args[0].
5697 const argv = try argsCopyZ(arena, args[1..]);
5698 const exit_code = ZigLlvmAr_main(@as(c_int, @intCast(argv.len)), argv.ptr);
5699 return @as(u8, @bitCast(@as(i8, @truncate(exit_code))));
5700}
5701
5702/// The first argument determines which backend is invoked. The options are:
5703/// * `ld.lld` - ELF
5704/// * `lld-link` - COFF
5705/// * `wasm-ld` - WebAssembly
5706pub fn lldMain(
5707 alloc: Allocator,
5708 args: []const []const u8,
5709 can_exit_early: bool,
5710) error{OutOfMemory}!u8 {
5711 if (!build_options.have_llvm)
5712 fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]});
5713
5714 // Print a warning if lld is called multiple times in the same process,
5715 // since it may misbehave
5716 // https://github.com/ziglang/zig/issues/3825
5717 const CallCounter = struct {
5718 var count: usize = 0;
5719 };
5720 if (CallCounter.count == 1) { // Issue the warning on the first repeat call
5721 warn("invoking LLD for the second time within the same process because the host OS ({s}) does not support spawning child processes. This sometimes activates LLD bugs", .{@tagName(native_os)});
5722 }
5723 CallCounter.count += 1;
5724
5725 var arena_instance = std.heap.ArenaAllocator.init(alloc);
5726 defer arena_instance.deinit();
5727 const arena = arena_instance.allocator();
5728
5729 // Convert the args to the format LLD expects.
5730 // We intentionally shave off the zig binary at args[0].
5731 const argv = try argsCopyZ(arena, args[1..]);
5732 // "If an error occurs, false will be returned."
5733 const ok = rc: {
5734 const llvm = @import("codegen/llvm/bindings.zig");
5735 const argc = @as(c_int, @intCast(argv.len));
5736 if (mem.eql(u8, args[1], "ld.lld")) {
5737 break :rc llvm.LinkELF(argc, argv.ptr, can_exit_early, false);
5738 } else if (mem.eql(u8, args[1], "lld-link")) {
5739 break :rc llvm.LinkCOFF(argc, argv.ptr, can_exit_early, false);
5740 } else if (mem.eql(u8, args[1], "wasm-ld")) {
5741 break :rc llvm.LinkWasm(argc, argv.ptr, can_exit_early, false);
5742 } else {
5743 unreachable;
5744 }
5745 };
5746 return @intFromBool(!ok);
5747}
5748
5749const ArgIteratorResponseFile = process.ArgIteratorGeneral(.{ .comments = true, .single_quotes = true });
5750
5751/// Initialize the arguments from a Response File. "*.rsp"
5752fn initArgIteratorResponseFile(allocator: Allocator, resp_file_path: []const u8) !ArgIteratorResponseFile {
5753 const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit
5754 const cmd_line = try fs.cwd().readFileAlloc(resp_file_path, allocator, .limited(max_bytes));
5755 errdefer allocator.free(cmd_line);
5756
5757 return ArgIteratorResponseFile.initTakeOwnership(allocator, cmd_line);
5758}
5759
5760const clang_args = @import("clang_options.zig").list;
5761
5762pub const ClangArgIterator = struct {
5763 has_next: bool,
5764 zig_equivalent: ZigEquivalent,
5765 only_arg: []const u8,
5766 second_arg: []const u8,
5767 other_args: []const []const u8,
5768 argv: []const []const u8,
5769 next_index: usize,
5770 root_args: ?*Args,
5771 arg_iterator_response_file: ArgIteratorResponseFile,
5772 arena: Allocator,
5773
5774 pub const ZigEquivalent = enum {
5775 target,
5776 o,
5777 c,
5778 r,
5779 m,
5780 x,
5781 other,
5782 positional,
5783 l,
5784 ignore,
5785 driver_punt,
5786 pic,
5787 no_pic,
5788 pie,
5789 no_pie,
5790 lto,
5791 no_lto,
5792 unwind_tables,
5793 no_unwind_tables,
5794 asynchronous_unwind_tables,
5795 no_asynchronous_unwind_tables,
5796 nostdlib,
5797 nostdlib_cpp,
5798 shared,
5799 rdynamic,
5800 wl,
5801 wp,
5802 preprocess_only,
5803 asm_only,
5804 optimize,
5805 debug,
5806 gdwarf32,
5807 gdwarf64,
5808 sanitize,
5809 no_sanitize,
5810 sanitize_trap,
5811 no_sanitize_trap,
5812 linker_script,
5813 dry_run,
5814 verbose,
5815 for_linker,
5816 linker_input_z,
5817 lib_dir,
5818 mcpu,
5819 dep_file,
5820 dep_file_to_stdout,
5821 framework_dir,
5822 framework,
5823 nostdlibinc,
5824 red_zone,
5825 no_red_zone,
5826 omit_frame_pointer,
5827 no_omit_frame_pointer,
5828 function_sections,
5829 no_function_sections,
5830 data_sections,
5831 no_data_sections,
5832 builtin,
5833 no_builtin,
5834 color_diagnostics,
5835 no_color_diagnostics,
5836 stack_check,
5837 no_stack_check,
5838 stack_protector,
5839 no_stack_protector,
5840 strip,
5841 exec_model,
5842 emit_llvm,
5843 sysroot,
5844 entry,
5845 force_undefined_symbol,
5846 weak_library,
5847 weak_framework,
5848 headerpad_max_install_names,
5849 compress_debug_sections,
5850 install_name,
5851 undefined,
5852 force_load_objc,
5853 mingw_unicode_entry_point,
5854 san_cov_trace_pc_guard,
5855 san_cov,
5856 no_san_cov,
5857 rtlib,
5858 static,
5859 dynamic,
5860 };
5861
5862 const Args = struct {
5863 next_index: usize,
5864 argv: []const []const u8,
5865 };
5866
5867 fn init(arena: Allocator, argv: []const []const u8) ClangArgIterator {
5868 return .{
5869 .next_index = 2, // `zig cc foo` this points to `foo`
5870 .has_next = argv.len > 2,
5871 .zig_equivalent = undefined,
5872 .only_arg = undefined,
5873 .second_arg = undefined,
5874 .other_args = undefined,
5875 .argv = argv,
5876 .root_args = null,
5877 .arg_iterator_response_file = undefined,
5878 .arena = arena,
5879 };
5880 }
5881
5882 fn next(self: *ClangArgIterator) !void {
5883 assert(self.has_next);
5884 assert(self.next_index < self.argv.len);
5885 // In this state we know that the parameter we are looking at is a root parameter
5886 // rather than an argument to a parameter.
5887 // We adjust the len below when necessary.
5888 self.other_args = (self.argv.ptr + self.next_index)[0..1];
5889 var arg = self.argv[self.next_index];
5890 self.incrementArgIndex();
5891
5892 if (mem.startsWith(u8, arg, "@")) {
5893 if (self.root_args != null) return error.NestedResponseFile;
5894
5895 // This is a "compiler response file". We must parse the file and treat its
5896 // contents as command line parameters.
5897 const arena = self.arena;
5898 const resp_file_path = arg[1..];
5899
5900 self.arg_iterator_response_file =
5901 initArgIteratorResponseFile(arena, resp_file_path) catch |err| {
5902 fatal("unable to read response file '{s}': {s}", .{ resp_file_path, @errorName(err) });
5903 };
5904 // NOTE: The ArgIteratorResponseFile returns tokens from next() that are slices of an
5905 // internal buffer. This internal buffer is arena allocated, so it is not cleaned up here.
5906
5907 var resp_arg_list = std.array_list.Managed([]const u8).init(arena);
5908 defer resp_arg_list.deinit();
5909 {
5910 while (self.arg_iterator_response_file.next()) |token| {
5911 try resp_arg_list.append(token);
5912 }
5913
5914 const args = try arena.create(Args);
5915 errdefer arena.destroy(args);
5916 args.* = .{
5917 .next_index = self.next_index,
5918 .argv = self.argv,
5919 };
5920 self.root_args = args;
5921 }
5922 const resp_arg_slice = try resp_arg_list.toOwnedSlice();
5923 self.next_index = 0;
5924 self.argv = resp_arg_slice;
5925
5926 if (resp_arg_slice.len == 0) {
5927 self.resolveRespFileArgs();
5928 return;
5929 }
5930
5931 self.has_next = true;
5932 self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary.
5933 arg = self.argv[self.next_index];
5934 self.incrementArgIndex();
5935 }
5936
5937 if (mem.eql(u8, arg, "-") or !mem.startsWith(u8, arg, "-")) {
5938 self.zig_equivalent = .positional;
5939 self.only_arg = arg;
5940 return;
5941 }
5942
5943 find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) {
5944 .flag => {
5945 const prefix_len = clang_arg.matchEql(arg);
5946 if (prefix_len > 0) {
5947 self.zig_equivalent = clang_arg.zig_equivalent;
5948 self.only_arg = arg[prefix_len..];
5949
5950 break :find_clang_arg;
5951 }
5952 },
5953 .joined, .comma_joined => {
5954 // joined example: --target=foo
5955 // comma_joined example: -Wl,-soname,libsoundio.so.2
5956 const prefix_len = clang_arg.matchStartsWith(arg);
5957 if (prefix_len != 0) {
5958 self.zig_equivalent = clang_arg.zig_equivalent;
5959 self.only_arg = arg[prefix_len..]; // This will skip over the "--target=" part.
5960
5961 break :find_clang_arg;
5962 }
5963 },
5964 .joined_or_separate => {
5965 // Examples: `-lfoo`, `-l foo`
5966 const prefix_len = clang_arg.matchStartsWith(arg);
5967 if (prefix_len == arg.len) {
5968 if (self.next_index >= self.argv.len) {
5969 fatal("Expected parameter after '{s}'", .{arg});
5970 }
5971 self.only_arg = self.argv[self.next_index];
5972 self.incrementArgIndex();
5973 self.other_args.len += 1;
5974 self.zig_equivalent = clang_arg.zig_equivalent;
5975
5976 break :find_clang_arg;
5977 } else if (prefix_len != 0) {
5978 self.zig_equivalent = clang_arg.zig_equivalent;
5979 self.only_arg = arg[prefix_len..];
5980
5981 break :find_clang_arg;
5982 }
5983 },
5984 .joined_and_separate => {
5985 // Example: `-Xopenmp-target=riscv64-linux-unknown foo`
5986 const prefix_len = clang_arg.matchStartsWith(arg);
5987 if (prefix_len != 0) {
5988 self.only_arg = arg[prefix_len..];
5989 if (self.next_index >= self.argv.len) {
5990 fatal("Expected parameter after '{s}'", .{arg});
5991 }
5992 self.second_arg = self.argv[self.next_index];
5993 self.incrementArgIndex();
5994 self.other_args.len += 1;
5995 self.zig_equivalent = clang_arg.zig_equivalent;
5996 break :find_clang_arg;
5997 }
5998 },
5999 .separate => if (clang_arg.matchEql(arg) > 0) {
6000 if (self.next_index >= self.argv.len) {
6001 fatal("Expected parameter after '{s}'", .{arg});
6002 }
6003 self.only_arg = self.argv[self.next_index];
6004 self.incrementArgIndex();
6005 self.other_args.len += 1;
6006 self.zig_equivalent = clang_arg.zig_equivalent;
6007 break :find_clang_arg;
6008 },
6009 .remaining_args_joined => {
6010 const prefix_len = clang_arg.matchStartsWith(arg);
6011 if (prefix_len != 0) {
6012 @panic("TODO");
6013 }
6014 },
6015 .multi_arg => |num_args| if (clang_arg.matchEql(arg) > 0) {
6016 // Example `-sectcreate <arg1> <arg2> <arg3>`.
6017 var i: usize = 0;
6018 while (i < num_args) : (i += 1) {
6019 self.incrementArgIndex();
6020 self.other_args.len += 1;
6021 }
6022 self.zig_equivalent = clang_arg.zig_equivalent;
6023 break :find_clang_arg;
6024 },
6025 } else {
6026 fatal("Unknown Clang option: '{s}'", .{arg});
6027 }
6028 }
6029
6030 fn incrementArgIndex(self: *ClangArgIterator) void {
6031 self.next_index += 1;
6032 self.resolveRespFileArgs();
6033 }
6034
6035 fn resolveRespFileArgs(self: *ClangArgIterator) void {
6036 const arena = self.arena;
6037 if (self.next_index >= self.argv.len) {
6038 if (self.root_args) |root_args| {
6039 self.next_index = root_args.next_index;
6040 self.argv = root_args.argv;
6041
6042 arena.destroy(root_args);
6043 self.root_args = null;
6044 }
6045 if (self.next_index >= self.argv.len) {
6046 self.has_next = false;
6047 }
6048 }
6049 }
6050};
6051
6052fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {
6053 return std.meta.stringToEnum(std.builtin.CodeModel, arg) orelse
6054 fatal("unsupported machine code model: '{s}'", .{arg});
6055}
6056
6057const usage_ast_check =
6058 \\Usage: zig ast-check [file]
6059 \\
6060 \\ Given a .zig source file or .zon file, reports any compile errors
6061 \\ that can be ascertained on the basis of the source code alone,
6062 \\ without target information or type checking.
6063 \\
6064 \\ If [file] is omitted, stdin is used.
6065 \\
6066 \\Options:
6067 \\ -h, --help Print this help and exit
6068 \\ --color [auto|off|on] Enable or disable colored error messages
6069 \\ --zon Treat the input file as ZON, regardless of file extension
6070 \\ -t (debug option) Output ZIR in text form to stdout
6071 \\
6072 \\
6073;
6074
6075fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8) !void {
6076 dev.check(.ast_check_command);
6077
6078 const Zir = std.zig.Zir;
6079
6080 var color: Color = .auto;
6081 var want_output_text = false;
6082 var force_zon = false;
6083 var zig_source_path: ?[]const u8 = null;
6084
6085 var i: usize = 0;
6086 while (i < args.len) : (i += 1) {
6087 const arg = args[i];
6088 if (mem.startsWith(u8, arg, "-")) {
6089 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
6090 try fs.File.stdout().writeAll(usage_ast_check);
6091 return cleanExit();
6092 } else if (mem.eql(u8, arg, "-t")) {
6093 want_output_text = true;
6094 } else if (mem.eql(u8, arg, "--zon")) {
6095 force_zon = true;
6096 } else if (mem.eql(u8, arg, "--color")) {
6097 if (i + 1 >= args.len) {
6098 fatal("expected [auto|on|off] after --color", .{});
6099 }
6100 i += 1;
6101 const next_arg = args[i];
6102 color = std.meta.stringToEnum(Color, next_arg) orelse {
6103 fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
6104 };
6105 } else {
6106 fatal("unrecognized parameter: '{s}'", .{arg});
6107 }
6108 } else if (zig_source_path == null) {
6109 zig_source_path = arg;
6110 } else {
6111 fatal("extra positional parameter: '{s}'", .{arg});
6112 }
6113 }
6114
6115 const display_path = zig_source_path orelse "<stdin>";
6116 const source: [:0]const u8 = s: {
6117 var f = if (zig_source_path) |p| file: {
6118 break :file fs.cwd().openFile(p, .{}) catch |err| {
6119 fatal("unable to open file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) });
6120 };
6121 } else fs.File.stdin();
6122 defer if (zig_source_path != null) f.close();
6123 var file_reader: fs.File.Reader = f.reader(io, &stdin_buffer);
6124 break :s std.zig.readSourceFileToEndAlloc(arena, &file_reader) catch |err| {
6125 fatal("unable to load file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) });
6126 };
6127 };
6128
6129 const mode: Ast.Mode = mode: {
6130 if (force_zon) break :mode .zon;
6131 if (zig_source_path) |path| {
6132 if (mem.endsWith(u8, path, ".zon")) {
6133 break :mode .zon;
6134 }
6135 }
6136 break :mode .zig;
6137 };
6138
6139 const tree = try Ast.parse(arena, source, mode);
6140
6141 var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
6142 const stdout_bw = &stdout_writer.interface;
6143 switch (mode) {
6144 .zig => {
6145 const zir = try AstGen.generate(arena, tree);
6146
6147 if (zir.hasCompileErrors()) {
6148 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6149 try wip_errors.init(arena);
6150 try wip_errors.addZirErrorMessages(zir, tree, source, display_path);
6151 var error_bundle = try wip_errors.toOwnedBundle("");
6152 error_bundle.renderToStdErr(.{}, color);
6153 if (zir.loweringFailed()) {
6154 process.exit(1);
6155 }
6156 }
6157
6158 if (!want_output_text) {
6159 if (zir.hasCompileErrors()) {
6160 process.exit(1);
6161 } else {
6162 return cleanExit();
6163 }
6164 }
6165 if (!build_options.enable_debug_extensions) {
6166 fatal("-t option only available in builds of zig with debug extensions", .{});
6167 }
6168
6169 {
6170 const token_bytes = @sizeOf(Ast.TokenList) +
6171 tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));
6172 const tree_bytes = @sizeOf(Ast) + tree.nodes.len *
6173 (@sizeOf(Ast.Node.Tag) +
6174 @sizeOf(Ast.TokenIndex) +
6175 // Here we don't use @sizeOf(Ast.Node.Data) because it would include
6176 // the debug safety tag but we want to measure release size.
6177 8);
6178 const instruction_bytes = zir.instructions.len *
6179 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
6180 // the debug safety tag but we want to measure release size.
6181 (@sizeOf(Zir.Inst.Tag) + 8);
6182 const extra_bytes = zir.extra.len * @sizeOf(u32);
6183 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
6184 zir.string_bytes.len * @sizeOf(u8);
6185 // zig fmt: off
6186 try stdout_bw.print(
6187 \\# Source bytes: {Bi}
6188 \\# Tokens: {} ({Bi})
6189 \\# AST Nodes: {} ({Bi})
6190 \\# Total ZIR bytes: {Bi}
6191 \\# Instructions: {d} ({Bi})
6192 \\# String Table Bytes: {}
6193 \\# Extra Data Items: {d} ({Bi})
6194 \\
6195 , .{
6196 source.len,
6197 tree.tokens.len, token_bytes,
6198 tree.nodes.len, tree_bytes,
6199 total_bytes,
6200 zir.instructions.len, instruction_bytes,
6201 zir.string_bytes.len,
6202 zir.extra.len, extra_bytes,
6203 });
6204 // zig fmt: on
6205 }
6206
6207 try @import("print_zir.zig").renderAsText(arena, tree, zir, stdout_bw);
6208 try stdout_bw.flush();
6209
6210 if (zir.hasCompileErrors()) {
6211 process.exit(1);
6212 } else {
6213 return cleanExit();
6214 }
6215 },
6216 .zon => {
6217 const zoir = try ZonGen.generate(arena, tree, .{});
6218 if (zoir.hasCompileErrors()) {
6219 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6220 try wip_errors.init(arena);
6221 try wip_errors.addZoirErrorMessages(zoir, tree, source, display_path);
6222 var error_bundle = try wip_errors.toOwnedBundle("");
6223 error_bundle.renderToStdErr(.{}, color);
6224 process.exit(1);
6225 }
6226
6227 if (!want_output_text) {
6228 return cleanExit();
6229 }
6230
6231 if (!build_options.enable_debug_extensions) {
6232 fatal("-t option only available in builds of zig with debug extensions", .{});
6233 }
6234
6235 try @import("print_zoir.zig").renderToWriter(zoir, arena, stdout_bw);
6236 try stdout_bw.flush();
6237 return cleanExit();
6238 },
6239 }
6240}
6241
6242fn cmdDetectCpu(io: Io, args: []const []const u8) !void {
6243 dev.check(.detect_cpu_command);
6244
6245 const detect_cpu_usage =
6246 \\Usage: zig detect-cpu [--llvm]
6247 \\
6248 \\ Print the host CPU name and feature set to stdout.
6249 \\
6250 \\Options:
6251 \\ -h, --help Print this help and exit
6252 \\ --llvm Detect using LLVM API
6253 \\
6254 ;
6255
6256 var use_llvm = false;
6257
6258 {
6259 var i: usize = 0;
6260 while (i < args.len) : (i += 1) {
6261 const arg = args[i];
6262 if (mem.startsWith(u8, arg, "-")) {
6263 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
6264 try fs.File.stdout().writeAll(detect_cpu_usage);
6265 return cleanExit();
6266 } else if (mem.eql(u8, arg, "--llvm")) {
6267 use_llvm = true;
6268 } else {
6269 fatal("unrecognized parameter: '{s}'", .{arg});
6270 }
6271 } else {
6272 fatal("unexpected extra parameter: '{s}'", .{arg});
6273 }
6274 }
6275 }
6276
6277 if (use_llvm) {
6278 if (!build_options.have_llvm)
6279 fatal("compiler does not use LLVM; cannot compare CPU features with LLVM", .{});
6280
6281 const llvm = @import("codegen/llvm/bindings.zig");
6282 const name = llvm.GetHostCPUName() orelse fatal("LLVM could not figure out the host cpu name", .{});
6283 const features = llvm.GetHostCPUFeatures() orelse fatal("LLVM could not figure out the host cpu feature set", .{});
6284 const cpu = try detectNativeCpuWithLLVM(builtin.cpu.arch, name, features);
6285 try printCpu(cpu);
6286 } else {
6287 const host_target = std.zig.resolveTargetQueryOrFatal(io, .{});
6288 try printCpu(host_target.cpu);
6289 }
6290}
6291
6292fn detectNativeCpuWithLLVM(
6293 arch: std.Target.Cpu.Arch,
6294 llvm_cpu_name_z: ?[*:0]const u8,
6295 llvm_cpu_features_opt: ?[*:0]const u8,
6296) !std.Target.Cpu {
6297 var result = std.Target.Cpu.baseline(arch, builtin.os);
6298
6299 if (llvm_cpu_name_z) |cpu_name_z| {
6300 const llvm_cpu_name = mem.span(cpu_name_z);
6301
6302 for (arch.allCpuModels()) |model| {
6303 const this_llvm_name = model.llvm_name orelse continue;
6304 if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) {
6305 // Here we use the non-dependencies-populated set,
6306 // so that subtracting features later in this function
6307 // affect the prepopulated set.
6308 result = std.Target.Cpu{
6309 .arch = arch,
6310 .model = model,
6311 .features = model.features,
6312 };
6313 break;
6314 }
6315 }
6316 }
6317
6318 const all_features = arch.allFeaturesList();
6319
6320 if (llvm_cpu_features_opt) |llvm_cpu_features| {
6321 var it = mem.tokenizeScalar(u8, mem.span(llvm_cpu_features), ',');
6322 while (it.next()) |decorated_llvm_feat| {
6323 var op: enum {
6324 add,
6325 sub,
6326 } = undefined;
6327 var llvm_feat: []const u8 = undefined;
6328 if (mem.startsWith(u8, decorated_llvm_feat, "+")) {
6329 op = .add;
6330 llvm_feat = decorated_llvm_feat[1..];
6331 } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) {
6332 op = .sub;
6333 llvm_feat = decorated_llvm_feat[1..];
6334 } else {
6335 return error.InvalidLlvmCpuFeaturesFormat;
6336 }
6337 for (all_features, 0..) |feature, index_usize| {
6338 const this_llvm_name = feature.llvm_name orelse continue;
6339 if (mem.eql(u8, llvm_feat, this_llvm_name)) {
6340 const index: std.Target.Cpu.Feature.Set.Index = @intCast(index_usize);
6341 switch (op) {
6342 .add => result.features.addFeature(index),
6343 .sub => result.features.removeFeature(index),
6344 }
6345 break;
6346 }
6347 }
6348 }
6349 }
6350
6351 result.features.populateDependencies(all_features);
6352 return result;
6353}
6354
6355fn printCpu(cpu: std.Target.Cpu) !void {
6356 var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
6357 const stdout_bw = &stdout_writer.interface;
6358
6359 if (cpu.model.llvm_name) |llvm_name| {
6360 try stdout_bw.print("{s}\n", .{llvm_name});
6361 }
6362
6363 const all_features = cpu.arch.allFeaturesList();
6364 for (all_features, 0..) |feature, index_usize| {
6365 const llvm_name = feature.llvm_name orelse continue;
6366 const index: std.Target.Cpu.Feature.Set.Index = @intCast(index_usize);
6367 const is_enabled = cpu.features.isEnabled(index);
6368 const plus_or_minus = "-+"[@intFromBool(is_enabled)];
6369 try stdout_bw.print("{c}{s}\n", .{ plus_or_minus, llvm_name });
6370 }
6371
6372 try stdout_bw.flush();
6373}
6374
6375fn cmdDumpLlvmInts(
6376 gpa: Allocator,
6377 arena: Allocator,
6378 args: []const []const u8,
6379) !void {
6380 dev.check(.llvm_ints_command);
6381
6382 _ = gpa;
6383
6384 if (!build_options.have_llvm)
6385 fatal("compiler does not use LLVM; cannot dump LLVM integer sizes", .{});
6386
6387 const triple = try arena.dupeZ(u8, args[0]);
6388
6389 const llvm = @import("codegen/llvm/bindings.zig");
6390
6391 for ([_]std.Target.Cpu.Arch{ .aarch64, .x86 }) |arch| {
6392 @import("codegen/llvm.zig").initializeLLVMTarget(arch);
6393 }
6394
6395 const target: *llvm.Target = t: {
6396 var target: *llvm.Target = undefined;
6397 var error_message: [*:0]const u8 = undefined;
6398 if (llvm.Target.getFromTriple(triple, &target, &error_message) != .False) @panic("bad");
6399 break :t target;
6400 };
6401 const tm = llvm.TargetMachine.create(target, triple, null, null, .None, .Default, .Default, false, false, .Default, null, false);
6402 const dl = tm.createTargetDataLayout();
6403 const context = llvm.Context.create();
6404
6405 var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
6406 const stdout_bw = &stdout_writer.interface;
6407 for ([_]u16{ 1, 8, 16, 32, 64, 128, 256 }) |bits| {
6408 const int_type = context.intType(bits);
6409 const alignment = dl.abiAlignmentOfType(int_type);
6410 try stdout_bw.print("LLVMABIAlignmentOfType(i{d}) == {d}\n", .{ bits, alignment });
6411 }
6412 try stdout_bw.flush();
6413
6414 return cleanExit();
6415}
6416
6417/// This is only enabled for debug builds.
6418fn cmdDumpZir(arena: Allocator, io: Io, args: []const []const u8) !void {
6419 dev.check(.dump_zir_command);
6420
6421 const Zir = std.zig.Zir;
6422
6423 const cache_file = args[0];
6424
6425 var f = fs.cwd().openFile(cache_file, .{}) catch |err| {
6426 fatal("unable to open zir cache file for dumping '{s}': {s}", .{ cache_file, @errorName(err) });
6427 };
6428 defer f.close();
6429
6430 const zir = try Zcu.loadZirCache(arena, io, f);
6431 var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
6432 const stdout_bw = &stdout_writer.interface;
6433 {
6434 const instruction_bytes = zir.instructions.len *
6435 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
6436 // the debug safety tag but we want to measure release size.
6437 (@sizeOf(Zir.Inst.Tag) + 8);
6438 const extra_bytes = zir.extra.len * @sizeOf(u32);
6439 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
6440 zir.string_bytes.len * @sizeOf(u8);
6441 // zig fmt: off
6442 try stdout_bw.print(
6443 \\# Total ZIR bytes: {Bi}
6444 \\# Instructions: {d} ({Bi})
6445 \\# String Table Bytes: {Bi}
6446 \\# Extra Data Items: {d} ({Bi})
6447 \\
6448 , .{
6449 total_bytes,
6450 zir.instructions.len, instruction_bytes,
6451 zir.string_bytes.len,
6452 zir.extra.len, extra_bytes,
6453 });
6454 // zig fmt: on
6455 }
6456
6457 try @import("print_zir.zig").renderAsText(arena, null, zir, stdout_bw);
6458 try stdout_bw.flush();
6459}
6460
6461/// This is only enabled for debug builds.
6462fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8) !void {
6463 dev.check(.changelist_command);
6464
6465 const color: Color = .auto;
6466 const Zir = std.zig.Zir;
6467
6468 const old_source_path = args[0];
6469 const new_source_path = args[1];
6470
6471 const old_source = source: {
6472 var f = fs.cwd().openFile(old_source_path, .{}) catch |err|
6473 fatal("unable to open old source file '{s}': {s}", .{ old_source_path, @errorName(err) });
6474 defer f.close();
6475 var file_reader: fs.File.Reader = f.reader(io, &stdin_buffer);
6476 break :source std.zig.readSourceFileToEndAlloc(arena, &file_reader) catch |err|
6477 fatal("unable to read old source file '{s}': {s}", .{ old_source_path, @errorName(err) });
6478 };
6479 const new_source = source: {
6480 var f = fs.cwd().openFile(new_source_path, .{}) catch |err|
6481 fatal("unable to open new source file '{s}': {s}", .{ new_source_path, @errorName(err) });
6482 defer f.close();
6483 var file_reader: fs.File.Reader = f.reader(io, &stdin_buffer);
6484 break :source std.zig.readSourceFileToEndAlloc(arena, &file_reader) catch |err|
6485 fatal("unable to read new source file '{s}': {s}", .{ new_source_path, @errorName(err) });
6486 };
6487
6488 const old_tree = try Ast.parse(arena, old_source, .zig);
6489 const old_zir = try AstGen.generate(arena, old_tree);
6490
6491 if (old_zir.loweringFailed()) {
6492 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6493 try wip_errors.init(arena);
6494 try wip_errors.addZirErrorMessages(old_zir, old_tree, old_source, old_source_path);
6495 var error_bundle = try wip_errors.toOwnedBundle("");
6496 error_bundle.renderToStdErr(.{}, color);
6497 process.exit(1);
6498 }
6499
6500 const new_tree = try Ast.parse(arena, new_source, .zig);
6501 const new_zir = try AstGen.generate(arena, new_tree);
6502
6503 if (new_zir.loweringFailed()) {
6504 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6505 try wip_errors.init(arena);
6506 try wip_errors.addZirErrorMessages(new_zir, new_tree, new_source, new_source_path);
6507 var error_bundle = try wip_errors.toOwnedBundle("");
6508 error_bundle.renderToStdErr(.{}, color);
6509 process.exit(1);
6510 }
6511
6512 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty;
6513 try Zcu.mapOldZirToNew(arena, old_zir, new_zir, &inst_map);
6514
6515 var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
6516 const stdout_bw = &stdout_writer.interface;
6517 {
6518 try stdout_bw.print("Instruction mappings:\n", .{});
6519 var it = inst_map.iterator();
6520 while (it.next()) |entry| {
6521 try stdout_bw.print(" %{d} => %{d}\n", .{
6522 @intFromEnum(entry.key_ptr.*),
6523 @intFromEnum(entry.value_ptr.*),
6524 });
6525 }
6526 }
6527 try stdout_bw.flush();
6528}
6529
6530fn eatIntPrefix(arg: []const u8, base: u8) []const u8 {
6531 if (arg.len > 2 and arg[0] == '0') {
6532 switch (std.ascii.toLower(arg[1])) {
6533 'b' => if (base == 2) return arg[2..],
6534 'o' => if (base == 8) return arg[2..],
6535 'x' => if (base == 16) return arg[2..],
6536 else => {},
6537 }
6538 }
6539 return arg;
6540}
6541
6542fn prefixedIntArg(arg: []const u8, prefix: []const u8) ?u64 {
6543 const number = mem.cutPrefix(u8, arg, prefix) orelse return null;
6544 return std.fmt.parseUnsigned(u64, number, 0) catch |err| fatal("unable to parse '{s}': {t}", .{ arg, err });
6545}
6546
6547fn warnAboutForeignBinaries(
6548 io: Io,
6549 arena: Allocator,
6550 arg_mode: ArgMode,
6551 target: *const std.Target,
6552 link_libc: bool,
6553) !void {
6554 const host_query: std.Target.Query = .{};
6555 const host_target = std.zig.resolveTargetQueryOrFatal(io, host_query);
6556
6557 switch (std.zig.system.getExternalExecutor(&host_target, target, .{ .link_libc = link_libc })) {
6558 .native => return,
6559 .rosetta => {
6560 const host_name = try host_target.zigTriple(arena);
6561 const foreign_name = try target.zigTriple(arena);
6562 warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}). Consider installing Rosetta.", .{
6563 host_name, foreign_name,
6564 });
6565 },
6566 .qemu => |qemu| {
6567 const host_name = try host_target.zigTriple(arena);
6568 const foreign_name = try target.zigTriple(arena);
6569 switch (arg_mode) {
6570 .zig_test => warn(
6571 "the host system ({s}) does not appear to be capable of executing binaries " ++
6572 "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
6573 "to run the tests",
6574 .{ host_name, foreign_name, qemu },
6575 ),
6576 else => warn(
6577 "the host system ({s}) does not appear to be capable of executing binaries " ++
6578 "from the target ({s}). Consider using '{s}' to run the binary",
6579 .{ host_name, foreign_name, qemu },
6580 ),
6581 }
6582 },
6583 .wine => |wine| {
6584 const host_name = try host_target.zigTriple(arena);
6585 const foreign_name = try target.zigTriple(arena);
6586 switch (arg_mode) {
6587 .zig_test => warn(
6588 "the host system ({s}) does not appear to be capable of executing binaries " ++
6589 "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
6590 "to run the tests",
6591 .{ host_name, foreign_name, wine },
6592 ),
6593 else => warn(
6594 "the host system ({s}) does not appear to be capable of executing binaries " ++
6595 "from the target ({s}). Consider using '{s}' to run the binary",
6596 .{ host_name, foreign_name, wine },
6597 ),
6598 }
6599 },
6600 .wasmtime => |wasmtime| {
6601 const host_name = try host_target.zigTriple(arena);
6602 const foreign_name = try target.zigTriple(arena);
6603 switch (arg_mode) {
6604 .zig_test => warn(
6605 "the host system ({s}) does not appear to be capable of executing binaries " ++
6606 "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
6607 "to run the tests",
6608 .{ host_name, foreign_name, wasmtime },
6609 ),
6610 else => warn(
6611 "the host system ({s}) does not appear to be capable of executing binaries " ++
6612 "from the target ({s}). Consider using '{s}' to run the binary",
6613 .{ host_name, foreign_name, wasmtime },
6614 ),
6615 }
6616 },
6617 .darling => |darling| {
6618 const host_name = try host_target.zigTriple(arena);
6619 const foreign_name = try target.zigTriple(arena);
6620 switch (arg_mode) {
6621 .zig_test => warn(
6622 "the host system ({s}) does not appear to be capable of executing binaries " ++
6623 "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
6624 "to run the tests",
6625 .{ host_name, foreign_name, darling },
6626 ),
6627 else => warn(
6628 "the host system ({s}) does not appear to be capable of executing binaries " ++
6629 "from the target ({s}). Consider using '{s}' to run the binary",
6630 .{ host_name, foreign_name, darling },
6631 ),
6632 }
6633 },
6634 .bad_dl => |foreign_dl| {
6635 const host_dl = host_target.dynamic_linker.get() orelse "(none)";
6636 const tip_suffix = switch (arg_mode) {
6637 .zig_test => ", '--test-no-exec', or '--test-cmd'",
6638 else => "",
6639 };
6640 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}", .{
6641 host_dl, foreign_dl, tip_suffix,
6642 });
6643 },
6644 .bad_os_or_cpu => {
6645 const host_name = try host_target.zigTriple(arena);
6646 const foreign_name = try target.zigTriple(arena);
6647 const tip_suffix = switch (arg_mode) {
6648 .zig_test => ". Consider using '--test-no-exec' or '--test-cmd'",
6649 else => "",
6650 };
6651 warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}){s}", .{
6652 host_name, foreign_name, tip_suffix,
6653 });
6654 },
6655 }
6656}
6657
6658fn parseSubsystem(arg: []const u8) !std.zig.Subsystem {
6659 return std.meta.stringToEnum(std.zig.Subsystem, arg) orelse
6660 fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{
6661 arg,
6662 \\ console
6663 \\ windows
6664 \\ posix
6665 \\ native
6666 \\ efi_application
6667 \\ efi_boot_service_driver
6668 \\ efi_rom
6669 \\ efi_runtime_driver
6670 \\
6671 });
6672}
6673
6674/// Model a header searchlist as a group.
6675/// Silently ignore superfluous search dirs.
6676/// Warn when a dir is added to multiple searchlists.
6677const ClangSearchSanitizer = struct {
6678 map: std.StringHashMapUnmanaged(Membership) = .empty,
6679
6680 fn reset(self: *@This()) void {
6681 self.map.clearRetainingCapacity();
6682 }
6683
6684 fn addIncludePath(
6685 self: *@This(),
6686 ally: Allocator,
6687 argv: *std.ArrayList([]const u8),
6688 group: Group,
6689 arg: []const u8,
6690 dir: []const u8,
6691 joined: bool,
6692 ) !void {
6693 const gopr = try self.map.getOrPut(ally, dir);
6694 const m = gopr.value_ptr;
6695 if (!gopr.found_existing) {
6696 // init empty membership
6697 m.* = .{};
6698 }
6699 const wtxt = "add '{s}' to header searchlist '-{s}' conflicts with '-{s}'";
6700 switch (group) {
6701 .I => {
6702 if (m.I) return;
6703 m.I = true;
6704 if (m.isystem) warn(wtxt, .{ dir, "I", "isystem" });
6705 if (m.idirafter) warn(wtxt, .{ dir, "I", "idirafter" });
6706 if (m.iframework) warn(wtxt, .{ dir, "I", "iframework" });
6707 },
6708 .isystem => {
6709 if (m.isystem) return;
6710 m.isystem = true;
6711 if (m.I) warn(wtxt, .{ dir, "isystem", "I" });
6712 if (m.idirafter) warn(wtxt, .{ dir, "isystem", "idirafter" });
6713 if (m.iframework) warn(wtxt, .{ dir, "isystem", "iframework" });
6714 },
6715 .iwithsysroot => {
6716 if (m.iwithsysroot) return;
6717 m.iwithsysroot = true;
6718 if (m.iframeworkwithsysroot) warn(wtxt, .{ dir, "iwithsysroot", "iframeworkwithsysroot" });
6719 },
6720 .idirafter => {
6721 if (m.idirafter) return;
6722 m.idirafter = true;
6723 if (m.I) warn(wtxt, .{ dir, "idirafter", "I" });
6724 if (m.isystem) warn(wtxt, .{ dir, "idirafter", "isystem" });
6725 if (m.iframework) warn(wtxt, .{ dir, "idirafter", "iframework" });
6726 },
6727 .iframework => {
6728 if (m.iframework) return;
6729 m.iframework = true;
6730 if (m.I) warn(wtxt, .{ dir, "iframework", "I" });
6731 if (m.isystem) warn(wtxt, .{ dir, "iframework", "isystem" });
6732 if (m.idirafter) warn(wtxt, .{ dir, "iframework", "idirafter" });
6733 },
6734 .iframeworkwithsysroot => {
6735 if (m.iframeworkwithsysroot) return;
6736 m.iframeworkwithsysroot = true;
6737 if (m.iwithsysroot) warn(wtxt, .{ dir, "iframeworkwithsysroot", "iwithsysroot" });
6738 },
6739 .embed_dir => {
6740 if (m.embed_dir) return;
6741 m.embed_dir = true;
6742 },
6743 }
6744 try argv.ensureUnusedCapacity(ally, 2);
6745 argv.appendAssumeCapacity(arg);
6746 if (!joined) argv.appendAssumeCapacity(dir);
6747 }
6748
6749 const Group = enum { I, isystem, iwithsysroot, idirafter, iframework, iframeworkwithsysroot, embed_dir };
6750
6751 const Membership = packed struct {
6752 I: bool = false,
6753 isystem: bool = false,
6754 iwithsysroot: bool = false,
6755 idirafter: bool = false,
6756 iframework: bool = false,
6757 iframeworkwithsysroot: bool = false,
6758 embed_dir: bool = false,
6759 };
6760};
6761
6762fn accessFrameworkPath(
6763 test_path: *std.array_list.Managed(u8),
6764 checked_paths: *std.array_list.Managed(u8),
6765 framework_dir_path: []const u8,
6766 framework_name: []const u8,
6767) !bool {
6768 const sep = fs.path.sep_str;
6769
6770 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
6771 test_path.clearRetainingCapacity();
6772 try test_path.print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}{s}", .{
6773 framework_dir_path, framework_name, framework_name, ext,
6774 });
6775 try checked_paths.print("\n {s}", .{test_path.items});
6776 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6777 error.FileNotFound => continue,
6778 else => |e| fatal("unable to search for {s} framework '{s}': {s}", .{
6779 ext, test_path.items, @errorName(e),
6780 }),
6781 };
6782 return true;
6783 }
6784
6785 return false;
6786}
6787
6788fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes {
6789 return std.meta.stringToEnum(std.zig.RcIncludes, arg) orelse
6790 fatal("unsupported rc includes type: '{s}'", .{arg});
6791}
6792
6793const usage_fetch =
6794 \\Usage: zig fetch [options] <url>
6795 \\Usage: zig fetch [options] <path>
6796 \\
6797 \\ Copy a package into the global cache and print its hash.
6798 \\ <url> must point to one of the following:
6799 \\ - A git+http / git+https server for the package
6800 \\ - A tarball file (with or without compression) containing
6801 \\ package source
6802 \\ - A git bundle file containing package source
6803 \\
6804 \\Examples:
6805 \\
6806 \\ zig fetch --save git+https://example.com/andrewrk/fun-example-tool.git
6807 \\ zig fetch --save https://example.com/andrewrk/fun-example-tool/archive/refs/heads/master.tar.gz
6808 \\
6809 \\Options:
6810 \\ -h, --help Print this help and exit
6811 \\ --global-cache-dir [path] Override path to global Zig cache directory
6812 \\ --debug-hash Print verbose hash information to stdout
6813 \\ --save Add the fetched package to build.zig.zon
6814 \\ --save=[name] Add the fetched package to build.zig.zon as name
6815 \\ --save-exact Add the fetched package to build.zig.zon, storing the URL verbatim
6816 \\ --save-exact=[name] Add the fetched package to build.zig.zon as name, storing the URL verbatim
6817 \\
6818;
6819
6820fn cmdFetch(
6821 gpa: Allocator,
6822 arena: Allocator,
6823 io: Io,
6824 args: []const []const u8,
6825) !void {
6826 dev.check(.fetch_command);
6827
6828 const color: Color = .auto;
6829 const work_around_btrfs_bug = native_os == .linux and
6830 EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
6831 var opt_path_or_url: ?[]const u8 = null;
6832 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
6833 var debug_hash: bool = false;
6834 var save: union(enum) {
6835 no,
6836 yes: ?[]const u8,
6837 exact: ?[]const u8,
6838 } = .no;
6839
6840 {
6841 var i: usize = 0;
6842 while (i < args.len) : (i += 1) {
6843 const arg = args[i];
6844 if (mem.startsWith(u8, arg, "-")) {
6845 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
6846 try fs.File.stdout().writeAll(usage_fetch);
6847 return cleanExit();
6848 } else if (mem.eql(u8, arg, "--global-cache-dir")) {
6849 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
6850 i += 1;
6851 override_global_cache_dir = args[i];
6852 } else if (mem.eql(u8, arg, "--debug-hash")) {
6853 debug_hash = true;
6854 } else if (mem.eql(u8, arg, "--save")) {
6855 save = .{ .yes = null };
6856 } else if (mem.cutPrefix(u8, arg, "--save=")) |rest| {
6857 save = .{ .yes = rest };
6858 } else if (mem.eql(u8, arg, "--save-exact")) {
6859 save = .{ .exact = null };
6860 } else if (mem.cutPrefix(u8, arg, "--save-exact=")) |rest| {
6861 save = .{ .exact = rest };
6862 } else {
6863 fatal("unrecognized parameter: '{s}'", .{arg});
6864 }
6865 } else if (opt_path_or_url != null) {
6866 fatal("unexpected extra parameter: '{s}'", .{arg});
6867 } else {
6868 opt_path_or_url = arg;
6869 }
6870 }
6871 }
6872
6873 const path_or_url = opt_path_or_url orelse fatal("missing url or path parameter", .{});
6874
6875 var thread_pool: ThreadPool = undefined;
6876 try thread_pool.init(.{ .allocator = gpa });
6877 defer thread_pool.deinit();
6878
6879 var http_client: std.http.Client = .{ .allocator = gpa, .io = io };
6880 defer http_client.deinit();
6881
6882 try http_client.initDefaultProxies(arena);
6883
6884 var root_prog_node = std.Progress.start(.{
6885 .root_name = "Fetch",
6886 });
6887 defer root_prog_node.end();
6888
6889 var global_cache_directory: Directory = l: {
6890 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
6891 break :l .{
6892 .handle = try fs.cwd().makeOpenPath(p, .{}),
6893 .path = p,
6894 };
6895 };
6896 defer global_cache_directory.handle.close();
6897
6898 var job_queue: Package.Fetch.JobQueue = .{
6899 .io = io,
6900 .http_client = &http_client,
6901 .global_cache = global_cache_directory,
6902 .recursive = false,
6903 .read_only = false,
6904 .debug_hash = debug_hash,
6905 .work_around_btrfs_bug = work_around_btrfs_bug,
6906 .mode = .all,
6907 };
6908 defer job_queue.deinit();
6909
6910 var fetch: Package.Fetch = .{
6911 .arena = std.heap.ArenaAllocator.init(gpa),
6912 .location = .{ .path_or_url = path_or_url },
6913 .location_tok = 0,
6914 .hash_tok = .none,
6915 .name_tok = 0,
6916 .lazy_status = .eager,
6917 .parent_package_root = undefined,
6918 .parent_manifest_ast = null,
6919 .prog_node = root_prog_node,
6920 .job_queue = &job_queue,
6921 .omit_missing_hash_error = true,
6922 .allow_missing_paths_field = false,
6923 .allow_missing_fingerprint = true,
6924 .allow_name_string = true,
6925 .use_latest_commit = true,
6926
6927 .package_root = undefined,
6928 .error_bundle = undefined,
6929 .manifest = null,
6930 .manifest_ast = undefined,
6931 .computed_hash = undefined,
6932 .has_build_zig = false,
6933 .oom_flag = false,
6934 .latest_commit = null,
6935
6936 .module = null,
6937 };
6938 defer fetch.deinit();
6939
6940 fetch.run() catch |err| switch (err) {
6941 error.OutOfMemory, error.Canceled => |e| return e,
6942 error.FetchFailed => {}, // error bundle checked below
6943 };
6944
6945 if (fetch.error_bundle.root_list.items.len > 0) {
6946 var errors = try fetch.error_bundle.toOwnedBundle("");
6947 errors.renderToStdErr(.{}, color);
6948 process.exit(1);
6949 }
6950
6951 const package_hash = fetch.computedPackageHash();
6952 const package_hash_slice = package_hash.toSlice();
6953
6954 root_prog_node.end();
6955 root_prog_node = .{ .index = .none };
6956
6957 const name = switch (save) {
6958 .no => {
6959 var stdout = fs.File.stdout().writerStreaming(&stdout_buffer);
6960 try stdout.interface.print("{s}\n", .{package_hash_slice});
6961 try stdout.interface.flush();
6962 return cleanExit();
6963 },
6964 .yes, .exact => |name| name: {
6965 if (name) |n| break :name n;
6966 const fetched_manifest = fetch.manifest orelse
6967 fatal("unable to determine name; fetched package has no build.zig.zon file", .{});
6968 break :name fetched_manifest.name;
6969 },
6970 };
6971
6972 const cwd_path = try introspect.getResolvedCwd(arena);
6973
6974 var build_root = try findBuildRoot(arena, .{
6975 .cwd_path = cwd_path,
6976 });
6977 defer build_root.deinit();
6978
6979 // The name to use in case the manifest file needs to be created now.
6980 const init_root_name = fs.path.basename(build_root.directory.path orelse cwd_path);
6981 var manifest, var ast = try loadManifest(gpa, arena, .{
6982 .root_name = try sanitizeExampleName(arena, init_root_name),
6983 .dir = build_root.directory.handle,
6984 .color = color,
6985 });
6986 defer {
6987 manifest.deinit(gpa);
6988 ast.deinit(gpa);
6989 }
6990
6991 var fixups: Ast.Render.Fixups = .{};
6992 defer fixups.deinit(gpa);
6993
6994 var saved_path_or_url = path_or_url;
6995
6996 if (fetch.latest_commit) |latest_commit| resolved: {
6997 const latest_commit_hex = try std.fmt.allocPrint(arena, "{f}", .{latest_commit});
6998
6999 var uri = try std.Uri.parse(path_or_url);
7000
7001 if (uri.fragment) |fragment| {
7002 const target_ref = try fragment.toRawMaybeAlloc(arena);
7003
7004 // the refspec may already be fully resolved
7005 if (std.mem.eql(u8, target_ref, latest_commit_hex)) break :resolved;
7006
7007 std.log.info("resolved ref '{s}' to commit {s}", .{ target_ref, latest_commit_hex });
7008
7009 // include the original refspec in a query parameter, could be used to check for updates
7010 uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f}", .{
7011 std.fmt.alt(fragment, .formatEscaped),
7012 }) };
7013 } else {
7014 std.log.info("resolved to commit {s}", .{latest_commit_hex});
7015 }
7016
7017 // replace the refspec with the resolved commit SHA
7018 uri.fragment = .{ .raw = latest_commit_hex };
7019
7020 switch (save) {
7021 .yes => saved_path_or_url = try std.fmt.allocPrint(arena, "{f}", .{uri}),
7022 .no, .exact => {}, // keep the original URL
7023 }
7024 }
7025
7026 const new_node_init = try std.fmt.allocPrint(arena,
7027 \\.{{
7028 \\ .url = "{f}",
7029 \\ .hash = "{f}",
7030 \\ }}
7031 , .{
7032 std.zig.fmtString(saved_path_or_url),
7033 std.zig.fmtString(package_hash_slice),
7034 });
7035
7036 const new_node_text = try std.fmt.allocPrint(arena, ".{f} = {s},\n", .{
7037 std.zig.fmtIdPU(name), new_node_init,
7038 });
7039
7040 const dependencies_init = try std.fmt.allocPrint(arena, ".{{\n {s} }}", .{
7041 new_node_text,
7042 });
7043
7044 const dependencies_text = try std.fmt.allocPrint(arena, ".dependencies = {s},\n", .{
7045 dependencies_init,
7046 });
7047
7048 if (manifest.dependencies.get(name)) |dep| {
7049 if (dep.hash) |h| {
7050 switch (dep.location) {
7051 .url => |u| {
7052 if (mem.eql(u8, h, package_hash_slice) and mem.eql(u8, u, saved_path_or_url)) {
7053 std.log.info("existing dependency named '{s}' is up-to-date", .{name});
7054 process.exit(0);
7055 }
7056 },
7057 .path => {},
7058 }
7059 }
7060
7061 const location_replace = try std.fmt.allocPrint(
7062 arena,
7063 "\"{f}\"",
7064 .{std.zig.fmtString(saved_path_or_url)},
7065 );
7066 const hash_replace = try std.fmt.allocPrint(
7067 arena,
7068 "\"{f}\"",
7069 .{std.zig.fmtString(package_hash_slice)},
7070 );
7071
7072 warn("overwriting existing dependency named '{s}'", .{name});
7073 try fixups.replace_nodes_with_string.put(gpa, dep.location_node, location_replace);
7074 if (dep.hash_node.unwrap()) |hash_node| {
7075 try fixups.replace_nodes_with_string.put(gpa, hash_node, hash_replace);
7076 } else {
7077 // https://github.com/ziglang/zig/issues/21690
7078 }
7079 } else if (manifest.dependencies.count() > 0) {
7080 // Add fixup for adding another dependency.
7081 const deps = manifest.dependencies.values();
7082 const last_dep_node = deps[deps.len - 1].node;
7083 try fixups.append_string_after_node.put(gpa, last_dep_node, new_node_text);
7084 } else if (manifest.dependencies_node.unwrap()) |dependencies_node| {
7085 // Add fixup for replacing the entire dependencies struct.
7086 try fixups.replace_nodes_with_string.put(gpa, dependencies_node, dependencies_init);
7087 } else {
7088 // Add fixup for adding dependencies struct.
7089 try fixups.append_string_after_node.put(gpa, manifest.version_node, dependencies_text);
7090 }
7091
7092 var aw: Io.Writer.Allocating = .init(gpa);
7093 defer aw.deinit();
7094 try ast.render(gpa, &aw.writer, fixups);
7095 const rendered = aw.written();
7096
7097 build_root.directory.handle.writeFile(.{ .sub_path = Package.Manifest.basename, .data = rendered }) catch |err| {
7098 fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err });
7099 };
7100
7101 return cleanExit();
7102}
7103
7104fn createEmptyDependenciesModule(
7105 arena: Allocator,
7106 main_mod: *Package.Module,
7107 dirs: Compilation.Directories,
7108 global_options: Compilation.Config,
7109) !void {
7110 var source = std.array_list.Managed(u8).init(arena);
7111 try Package.Fetch.JobQueue.createEmptyDependenciesSource(&source);
7112 _ = try createDependenciesModule(
7113 arena,
7114 source.items,
7115 main_mod,
7116 dirs,
7117 global_options,
7118 );
7119}
7120
7121/// Creates the dependencies.zig file and corresponding `Package.Module` for the
7122/// build runner to obtain via `@import("@dependencies")`.
7123fn createDependenciesModule(
7124 arena: Allocator,
7125 source: []const u8,
7126 main_mod: *Package.Module,
7127 dirs: Compilation.Directories,
7128 global_options: Compilation.Config,
7129) !*Package.Module {
7130 // Atomically create the file in a directory named after the hash of its contents.
7131 const basename = "dependencies.zig";
7132 const rand_int = std.crypto.random.int(u64);
7133 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
7134 {
7135 var tmp_dir = try dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{});
7136 defer tmp_dir.close();
7137 try tmp_dir.writeFile(.{ .sub_path = basename, .data = source });
7138 }
7139
7140 var hh: Cache.HashHelper = .{};
7141 hh.addBytes(build_options.version);
7142 hh.addBytes(source);
7143 const hex_digest = hh.final();
7144
7145 const o_dir_sub_path = try arena.dupe(u8, "o" ++ fs.path.sep_str ++ hex_digest);
7146 try Package.Fetch.renameTmpIntoCache(
7147 dirs.local_cache.handle,
7148 tmp_dir_sub_path,
7149 o_dir_sub_path,
7150 );
7151
7152 const deps_mod = try Package.Module.create(arena, .{
7153 .paths = .{
7154 .root = try .fromRoot(arena, dirs, .local_cache, o_dir_sub_path),
7155 .root_src_path = basename,
7156 },
7157 .fully_qualified_name = "root.@dependencies",
7158 .parent = main_mod,
7159 .cc_argv = &.{},
7160 .inherited = .{},
7161 .global = global_options,
7162 });
7163 try main_mod.deps.put(arena, "@dependencies", deps_mod);
7164 return deps_mod;
7165}
7166
7167const BuildRoot = struct {
7168 directory: Cache.Directory,
7169 build_zig_basename: []const u8,
7170 cleanup_build_dir: ?fs.Dir,
7171
7172 fn deinit(br: *BuildRoot) void {
7173 if (br.cleanup_build_dir) |*dir| dir.close();
7174 br.* = undefined;
7175 }
7176};
7177
7178const FindBuildRootOptions = struct {
7179 build_file: ?[]const u8 = null,
7180 cwd_path: ?[]const u8 = null,
7181};
7182
7183fn findBuildRoot(arena: Allocator, options: FindBuildRootOptions) !BuildRoot {
7184 const cwd_path = options.cwd_path orelse try introspect.getResolvedCwd(arena);
7185 const build_zig_basename = if (options.build_file) |bf|
7186 fs.path.basename(bf)
7187 else
7188 Package.build_zig_basename;
7189
7190 if (options.build_file) |bf| {
7191 if (fs.path.dirname(bf)) |dirname| {
7192 const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
7193 fatal("unable to open directory to build file from argument 'build-file', '{s}': {s}", .{ dirname, @errorName(err) });
7194 };
7195 return .{
7196 .build_zig_basename = build_zig_basename,
7197 .directory = .{ .path = dirname, .handle = dir },
7198 .cleanup_build_dir = dir,
7199 };
7200 }
7201
7202 return .{
7203 .build_zig_basename = build_zig_basename,
7204 .directory = .{ .path = null, .handle = fs.cwd() },
7205 .cleanup_build_dir = null,
7206 };
7207 }
7208 // Search up parent directories until we find build.zig.
7209 var dirname: []const u8 = cwd_path;
7210 while (true) {
7211 const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename });
7212 if (fs.cwd().access(joined_path, .{})) |_| {
7213 const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
7214 fatal("unable to open directory while searching for build.zig file, '{s}': {s}", .{ dirname, @errorName(err) });
7215 };
7216 return .{
7217 .build_zig_basename = build_zig_basename,
7218 .directory = .{
7219 .path = dirname,
7220 .handle = dir,
7221 },
7222 .cleanup_build_dir = dir,
7223 };
7224 } else |err| switch (err) {
7225 error.FileNotFound => {
7226 dirname = fs.path.dirname(dirname) orelse {
7227 std.log.info("initialize {s} template file with 'zig init'", .{
7228 Package.build_zig_basename,
7229 });
7230 std.log.info("see 'zig --help' for more options", .{});
7231 fatal("no build.zig file found, in the current directory or any parent directories", .{});
7232 };
7233 continue;
7234 },
7235 else => |e| return e,
7236 }
7237 }
7238}
7239
7240const LoadManifestOptions = struct {
7241 root_name: []const u8,
7242 dir: fs.Dir,
7243 color: Color,
7244};
7245
7246fn loadManifest(
7247 gpa: Allocator,
7248 arena: Allocator,
7249 options: LoadManifestOptions,
7250) !struct { Package.Manifest, Ast } {
7251 const manifest_bytes = while (true) {
7252 break options.dir.readFileAllocOptions(
7253 Package.Manifest.basename,
7254 arena,
7255 .limited(Package.Manifest.max_bytes),
7256 .@"1",
7257 0,
7258 ) catch |err| switch (err) {
7259 error.FileNotFound => {
7260 writeSimpleTemplateFile(Package.Manifest.basename,
7261 \\.{{
7262 \\ .name = .{s},
7263 \\ .version = "{s}",
7264 \\ .paths = .{{""}},
7265 \\ .fingerprint = 0x{x},
7266 \\}}
7267 \\
7268 , .{
7269 options.root_name,
7270 build_options.version,
7271 Package.Fingerprint.generate(options.root_name).int(),
7272 }) catch |e| {
7273 fatal("unable to write {s}: {s}", .{ Package.Manifest.basename, @errorName(e) });
7274 };
7275 continue;
7276 },
7277 else => |e| fatal("unable to load {s}: {s}", .{
7278 Package.Manifest.basename, @errorName(e),
7279 }),
7280 };
7281 };
7282 var ast = try Ast.parse(gpa, manifest_bytes, .zon);
7283 errdefer ast.deinit(gpa);
7284
7285 if (ast.errors.len > 0) {
7286 try std.zig.printAstErrorsToStderr(gpa, ast, Package.Manifest.basename, options.color);
7287 process.exit(2);
7288 }
7289
7290 var manifest = try Package.Manifest.parse(gpa, ast, .{});
7291 errdefer manifest.deinit(gpa);
7292
7293 if (manifest.errors.len > 0) {
7294 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
7295 try wip_errors.init(gpa);
7296 defer wip_errors.deinit();
7297
7298 const src_path = try wip_errors.addString(Package.Manifest.basename);
7299 try manifest.copyErrorsIntoBundle(ast, src_path, &wip_errors);
7300
7301 var error_bundle = try wip_errors.toOwnedBundle("");
7302 defer error_bundle.deinit(gpa);
7303 error_bundle.renderToStdErr(.{}, options.color);
7304
7305 process.exit(2);
7306 }
7307 return .{ manifest, ast };
7308}
7309
7310const Templates = struct {
7311 zig_lib_directory: Cache.Directory,
7312 dir: fs.Dir,
7313 buffer: std.array_list.Managed(u8),
7314
7315 fn deinit(templates: *Templates) void {
7316 templates.zig_lib_directory.handle.close();
7317 templates.dir.close();
7318 templates.buffer.deinit();
7319 templates.* = undefined;
7320 }
7321
7322 fn write(
7323 templates: *Templates,
7324 arena: Allocator,
7325 out_dir: fs.Dir,
7326 root_name: []const u8,
7327 template_path: []const u8,
7328 fingerprint: Package.Fingerprint,
7329 ) !void {
7330 if (fs.path.dirname(template_path)) |dirname| {
7331 out_dir.makePath(dirname) catch |err| {
7332 fatal("unable to make path '{s}': {s}", .{ dirname, @errorName(err) });
7333 };
7334 }
7335
7336 const max_bytes = 10 * 1024 * 1024;
7337 const contents = templates.dir.readFileAlloc(template_path, arena, .limited(max_bytes)) catch |err| {
7338 fatal("unable to read template file '{s}': {s}", .{ template_path, @errorName(err) });
7339 };
7340 templates.buffer.clearRetainingCapacity();
7341 try templates.buffer.ensureUnusedCapacity(contents.len);
7342 var i: usize = 0;
7343 while (i < contents.len) {
7344 if (contents[i] == '_' or contents[i] == '.') {
7345 // Both '_' and '.' are allowed because depending on the context
7346 // one prefix will be valid, while the other might not.
7347 if (std.mem.startsWith(u8, contents[i + 1 ..], "NAME")) {
7348 try templates.buffer.appendSlice(root_name);
7349 i += "_NAME".len;
7350 continue;
7351 } else if (std.mem.startsWith(u8, contents[i + 1 ..], "FINGERPRINT")) {
7352 try templates.buffer.print("0x{x}", .{fingerprint.int()});
7353 i += "_FINGERPRINT".len;
7354 continue;
7355 } else if (std.mem.startsWith(u8, contents[i + 1 ..], "ZIGVER")) {
7356 try templates.buffer.appendSlice(build_options.version);
7357 i += "_ZIGVER".len;
7358 continue;
7359 }
7360 }
7361
7362 try templates.buffer.append(contents[i]);
7363 i += 1;
7364 }
7365
7366 return out_dir.writeFile(.{
7367 .sub_path = template_path,
7368 .data = templates.buffer.items,
7369 .flags = .{ .exclusive = true },
7370 });
7371 }
7372};
7373fn writeSimpleTemplateFile(file_name: []const u8, comptime fmt: []const u8, args: anytype) !void {
7374 const f = try fs.cwd().createFile(file_name, .{ .exclusive = true });
7375 defer f.close();
7376 var buf: [4096]u8 = undefined;
7377 var fw = f.writer(&buf);
7378 try fw.interface.print(fmt, args);
7379 try fw.interface.flush();
7380}
7381
7382fn findTemplates(gpa: Allocator, arena: Allocator) Templates {
7383 const cwd_path = introspect.getResolvedCwd(arena) catch |err| {
7384 fatal("unable to get cwd: {s}", .{@errorName(err)});
7385 };
7386 const self_exe_path = fs.selfExePathAlloc(arena) catch |err| {
7387 fatal("unable to find self exe path: {s}", .{@errorName(err)});
7388 };
7389 var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, cwd_path, self_exe_path) catch |err| {
7390 fatal("unable to find zig installation directory '{s}': {s}", .{ self_exe_path, @errorName(err) });
7391 };
7392
7393 const s = fs.path.sep_str;
7394 const template_sub_path = "init";
7395 const template_dir = zig_lib_directory.handle.openDir(template_sub_path, .{}) catch |err| {
7396 const path = zig_lib_directory.path orelse ".";
7397 fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{
7398 path, s, template_sub_path, @errorName(err),
7399 });
7400 };
7401
7402 return .{
7403 .zig_lib_directory = zig_lib_directory,
7404 .dir = template_dir,
7405 .buffer = std.array_list.Managed(u8).init(gpa),
7406 };
7407}
7408
7409fn parseOptimizeMode(s: []const u8) std.builtin.OptimizeMode {
7410 return std.meta.stringToEnum(std.builtin.OptimizeMode, s) orelse
7411 fatal("unrecognized optimization mode: '{s}'", .{s});
7412}
7413
7414fn parseWasiExecModel(s: []const u8) std.builtin.WasiExecModel {
7415 return std.meta.stringToEnum(std.builtin.WasiExecModel, s) orelse
7416 fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{s});
7417}
7418
7419fn parseStackSize(s: []const u8) u64 {
7420 return std.fmt.parseUnsigned(u64, s, 0) catch |err|
7421 fatal("unable to parse stack size '{s}': {s}", .{ s, @errorName(err) });
7422}
7423
7424fn parseImageBase(s: []const u8) u64 {
7425 return std.fmt.parseUnsigned(u64, s, 0) catch |err|
7426 fatal("unable to parse image base '{s}': {s}", .{ s, @errorName(err) });
7427}
7428
7429fn handleModArg(
7430 arena: Allocator,
7431 mod_name: []const u8,
7432 opt_root_src_orig: ?[]const u8,
7433 create_module: *CreateModule,
7434 mod_opts: *Package.Module.CreateOptions.Inherited,
7435 cc_argv: *std.ArrayList([]const u8),
7436 target_arch_os_abi: *?[]const u8,
7437 target_mcpu: *?[]const u8,
7438 deps: *std.ArrayList(CliModule.Dep),
7439 c_source_files_owner_index: *usize,
7440 rc_source_files_owner_index: *usize,
7441 cssan: *ClangSearchSanitizer,
7442) !void {
7443 const gop = try create_module.modules.getOrPut(arena, mod_name);
7444
7445 if (gop.found_existing) {
7446 fatal("unable to add module '{s}': already exists as '{s}{c}{s}'", .{
7447 mod_name, gop.value_ptr.root_path, fs.path.sep, gop.value_ptr.root_src_path,
7448 });
7449 }
7450
7451 // See duplicate logic: ModCreationGlobalFlags
7452 if (mod_opts.single_threaded == false)
7453 create_module.opts.any_non_single_threaded = true;
7454 if (mod_opts.sanitize_thread == true)
7455 create_module.opts.any_sanitize_thread = true;
7456 if (mod_opts.sanitize_c) |sc| switch (sc) {
7457 .off => {},
7458 .trap => if (create_module.opts.any_sanitize_c == .off) {
7459 create_module.opts.any_sanitize_c = .trap;
7460 },
7461 .full => create_module.opts.any_sanitize_c = .full,
7462 };
7463 if (mod_opts.fuzz == true)
7464 create_module.opts.any_fuzz = true;
7465 if (mod_opts.unwind_tables) |uwt| switch (uwt) {
7466 .none => {},
7467 .sync, .async => create_module.opts.any_unwind_tables = true,
7468 };
7469 if (mod_opts.strip == false)
7470 create_module.opts.any_non_stripped = true;
7471 if (mod_opts.error_tracing == true)
7472 create_module.opts.any_error_tracing = true;
7473
7474 const root_path: []const u8, const root_src_path: []const u8 = if (opt_root_src_orig) |path| root: {
7475 create_module.opts.have_zcu = true;
7476 break :root .{ fs.path.dirname(path) orelse ".", fs.path.basename(path) };
7477 } else .{ ".", "" };
7478
7479 gop.value_ptr.* = .{
7480 .root_path = root_path,
7481 .root_src_path = root_src_path,
7482 .cc_argv = try cc_argv.toOwnedSlice(arena),
7483 .inherited = mod_opts.*,
7484 .target_arch_os_abi = target_arch_os_abi.*,
7485 .target_mcpu = target_mcpu.*,
7486 .deps = try deps.toOwnedSlice(arena),
7487 .resolved = null,
7488 .c_source_files_start = c_source_files_owner_index.*,
7489 .c_source_files_end = create_module.c_source_files.items.len,
7490 .rc_source_files_start = rc_source_files_owner_index.*,
7491 .rc_source_files_end = create_module.rc_source_files.items.len,
7492 };
7493 cssan.reset();
7494 mod_opts.* = .{};
7495 target_arch_os_abi.* = null;
7496 target_mcpu.* = null;
7497 c_source_files_owner_index.* = create_module.c_source_files.items.len;
7498 rc_source_files_owner_index.* = create_module.rc_source_files.items.len;
7499}
7500
7501fn anyObjectLinkInputs(link_inputs: []const link.UnresolvedInput) bool {
7502 for (link_inputs) |link_input| switch (link_input) {
7503 .path_query => |pq| switch (Compilation.classifyFileExt(pq.path.sub_path)) {
7504 .object, .static_library, .res => return true,
7505 else => continue,
7506 },
7507 else => continue,
7508 };
7509 return false;
7510}
7511
7512fn addLibDirectoryWarn(lib_directories: *std.ArrayList(Directory), path: []const u8) void {
7513 return addLibDirectoryWarn2(lib_directories, path, false);
7514}
7515
7516fn addLibDirectoryWarn2(
7517 lib_directories: *std.ArrayList(Directory),
7518 path: []const u8,
7519 ignore_not_found: bool,
7520) void {
7521 lib_directories.appendAssumeCapacity(.{
7522 .handle = fs.cwd().openDir(path, .{}) catch |err| {
7523 if (err == error.FileNotFound and ignore_not_found) return;
7524 warn("unable to open library directory '{s}': {s}", .{ path, @errorName(err) });
7525 return;
7526 },
7527 .path = path,
7528 });
7529}