authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-21 21:01:04-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-21 21:01:04-07:00
logead50ea6657d31632f5661f788b035a66c344d13
treede5463e98afaf3b3751e3786191324e4790b806f
parent528832bd3a2e7b686ee84aef5887df740a6114db

stage2: implement `zig run` and `zig test`


3 files changed, 664 insertions(+), 525 deletions(-)

BRANCH_TODO+7-6
...@@ -1,6 +1,9 @@...@@ -1,6 +1,9 @@
1 * build & link against libcxx and libcxxabi1 * build & link against libcxx and libcxxabi
2 * `zig test`
3 * `zig build`2 * `zig build`
3 * repair @cImport
4 * make sure zig cc works
5 - using it as a preprocessor (-E)
6 - try building some software
4 * `-ftime-report`7 * `-ftime-report`
5 * -fstack-report print stack size diagnostics\n"8 * -fstack-report print stack size diagnostics\n"
6 * -fdump-analysis write analysis.json file with type information\n"9 * -fdump-analysis write analysis.json file with type information\n"
...@@ -11,17 +14,14 @@...@@ -11,17 +14,14 @@
11 * -femit-llvm-ir produce a .ll file with LLVM IR\n"14 * -femit-llvm-ir produce a .ll file with LLVM IR\n"
12 * -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n"15 * -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n"
13 * --cache-dir [path] override the local cache directory\n"16 * --cache-dir [path] override the local cache directory\n"
14 * make sure zig cc works
15 - using it as a preprocessor (-E)
16 - try building some software
17 * implement proper parsing of LLD stderr/stdout and exposing compile errors17 * implement proper parsing of LLD stderr/stdout and exposing compile errors
18 * implement proper parsing of clang stderr/stdout and exposing compile errors18 * implement proper parsing of clang stderr/stdout and exposing compile errors
19 * support rpaths in ELF linker code19 * support rpaths in ELF linker code
20 * repair @cImport
21 * add CLI support for a way to pass extra flags to c source files20 * add CLI support for a way to pass extra flags to c source files
22 * musl21 * musl
23 * mingw-w6422 * mingw-w64
24 * use global zig-cache dir for crt files23 * use global zig-cache dir for crt files
24 * use global zig-cache dir for `zig run` executables but not `zig test`
25 * MachO LLD linking25 * MachO LLD linking
26 * COFF LLD linking26 * COFF LLD linking
27 * WASM LLD linking27 * WASM LLD linking
...@@ -30,9 +30,9 @@...@@ -30,9 +30,9 @@
30 * audit the CLI options for stage230 * audit the CLI options for stage2
31 * `zig init-lib`31 * `zig init-lib`
32 * `zig init-exe`32 * `zig init-exe`
33 * `zig run`
34 * restore error messages for stage2_add_link_lib33 * restore error messages for stage2_add_link_lib
35 * audit the base cache hash34 * audit the base cache hash
35 * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process.
3636
37 * implement proper compile errors for failing to build glibc crt files and shared libs37 * implement proper compile errors for failing to build glibc crt files and shared libs
38 * implement -fno-emit-bin38 * implement -fno-emit-bin
...@@ -66,3 +66,4 @@...@@ -66,3 +66,4 @@
66 * some kind of "zig identifier escape" function rather than unconditionally using @"" syntax66 * some kind of "zig identifier escape" function rather than unconditionally using @"" syntax
67 in builtin.zig67 in builtin.zig
68 * rename std.builtin.Mode to std.builtin.OptimizeMode68 * rename std.builtin.Mode to std.builtin.OptimizeMode
69 * implement `zig run` and `zig test` when combined with `--watch`
src/Compilation.zig+39-6
...@@ -97,6 +97,10 @@ owned_link_dir: ?std.fs.Dir,...@@ -97,6 +97,10 @@ owned_link_dir: ?std.fs.Dir,
97/// Don't use this for anything other than stage1 compatibility.97/// Don't use this for anything other than stage1 compatibility.
98color: @import("main.zig").Color = .Auto,98color: @import("main.zig").Color = .Auto,
9999
100test_filter: ?[]const u8,
101test_name_prefix: ?[]const u8,
102test_evented_io: bool,
103
100pub const InnerError = Module.InnerError;104pub const InnerError = Module.InnerError;
101105
102pub const CRTFile = struct {106pub const CRTFile = struct {
...@@ -327,6 +331,9 @@ pub const InitOptions = struct {...@@ -327,6 +331,9 @@ pub const InitOptions = struct {
327 machine_code_model: std.builtin.CodeModel = .default,331 machine_code_model: std.builtin.CodeModel = .default,
328 /// This is for stage1 and should be deleted upon completion of self-hosting.332 /// This is for stage1 and should be deleted upon completion of self-hosting.
329 color: @import("main.zig").Color = .Auto,333 color: @import("main.zig").Color = .Auto,
334 test_filter: ?[]const u8 = null,
335 test_name_prefix: ?[]const u8 = null,
336 test_evented_io: bool = false,
330};337};
331338
332pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {339pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
...@@ -554,6 +561,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -554,6 +561,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
554 hash.add(single_threaded);561 hash.add(single_threaded);
555 hash.add(options.target.os.getVersionRange());562 hash.add(options.target.os.getVersionRange());
556 hash.add(dll_export_fns);563 hash.add(dll_export_fns);
564 hash.add(options.is_test);
557565
558 const digest = hash.final();566 const digest = hash.final();
559 const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });567 const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
...@@ -728,6 +736,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -728,6 +736,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
728 .is_test = options.is_test,736 .is_test = options.is_test,
729 .color = options.color,737 .color = options.color,
730 .time_report = options.time_report,738 .time_report = options.time_report,
739 .test_filter = options.test_filter,
740 .test_name_prefix = options.test_name_prefix,
741 .test_evented_io = options.test_evented_io,
731 };742 };
732 break :comp comp;743 break :comp comp;
733 };744 };
...@@ -1996,6 +2007,25 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8...@@ -1996,6 +2007,25 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
1996 comp.bin_file.options.strip,2007 comp.bin_file.options.strip,
1997 @tagName(comp.bin_file.options.machine_code_model),2008 @tagName(comp.bin_file.options.machine_code_model),
1998 });2009 });
2010
2011 if (comp.is_test) {
2012 try buffer.appendSlice(
2013 \\pub var test_functions: []TestFn = undefined; // overwritten later
2014 \\
2015 );
2016 if (comp.test_evented_io) {
2017 try buffer.appendSlice(
2018 \\pub const test_io_mode = .evented;
2019 \\
2020 );
2021 } else {
2022 try buffer.appendSlice(
2023 \\pub const test_io_mode = .blocking;
2024 \\
2025 );
2026 }
2027 }
2028
1999 return buffer.toOwnedSlice();2029 return buffer.toOwnedSlice();
2000}2030}
20012031
...@@ -2129,6 +2159,7 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2129,6 +2159,7 @@ fn updateStage1Module(comp: *Compilation) !void {
2129 ch.hash.add(target.os.getVersionRange());2159 ch.hash.add(target.os.getVersionRange());
2130 ch.hash.add(comp.bin_file.options.dll_export_fns);2160 ch.hash.add(comp.bin_file.options.dll_export_fns);
2131 ch.hash.add(comp.bin_file.options.function_sections);2161 ch.hash.add(comp.bin_file.options.function_sections);
2162 ch.hash.add(comp.is_test);
21322163
2133 if (try ch.hit()) {2164 if (try ch.hit()) {
2134 const digest = ch.final();2165 const digest = ch.final();
...@@ -2155,7 +2186,7 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2155,7 +2186,7 @@ fn updateStage1Module(comp: *Compilation) !void {
2155 .llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?,2186 .llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?,
2156 };2187 };
2157 var progress: std.Progress = .{};2188 var progress: std.Progress = .{};
2158 var main_progress_node = try progress.start("", 100);2189 var main_progress_node = try progress.start("", null);
2159 defer main_progress_node.end();2190 defer main_progress_node.end();
2160 if (comp.color == .Off) progress.terminal = null;2191 if (comp.color == .Off) progress.terminal = null;
21612192
...@@ -2184,6 +2215,8 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2184,6 +2215,8 @@ fn updateStage1Module(comp: *Compilation) !void {
2184 .parent = null,2215 .parent = null,
2185 };2216 };
2186 const output_dir = comp.bin_file.options.directory.path orelse ".";2217 const output_dir = comp.bin_file.options.directory.path orelse ".";
2218 const test_filter = comp.test_filter orelse ""[0..0];
2219 const test_name_prefix = comp.test_name_prefix orelse ""[0..0];
2187 stage1_module.* = .{2220 stage1_module.* = .{
2188 .root_name_ptr = comp.bin_file.options.root_name.ptr,2221 .root_name_ptr = comp.bin_file.options.root_name.ptr,
2189 .root_name_len = comp.bin_file.options.root_name.len,2222 .root_name_len = comp.bin_file.options.root_name.len,
...@@ -2191,10 +2224,10 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2191,10 +2224,10 @@ fn updateStage1Module(comp: *Compilation) !void {
2191 .output_dir_len = output_dir.len,2224 .output_dir_len = output_dir.len,
2192 .builtin_zig_path_ptr = builtin_zig_path.ptr,2225 .builtin_zig_path_ptr = builtin_zig_path.ptr,
2193 .builtin_zig_path_len = builtin_zig_path.len,2226 .builtin_zig_path_len = builtin_zig_path.len,
2194 .test_filter_ptr = "",2227 .test_filter_ptr = test_filter.ptr,
2195 .test_filter_len = 0,2228 .test_filter_len = test_filter.len,
2196 .test_name_prefix_ptr = "",2229 .test_name_prefix_ptr = test_name_prefix.ptr,
2197 .test_name_prefix_len = 0,2230 .test_name_prefix_len = test_name_prefix.len,
2198 .userdata = @ptrToInt(comp),2231 .userdata = @ptrToInt(comp),
2199 .root_pkg = stage1_pkg,2232 .root_pkg = stage1_pkg,
2200 .code_model = @enumToInt(comp.bin_file.options.machine_code_model),2233 .code_model = @enumToInt(comp.bin_file.options.machine_code_model),
...@@ -2217,7 +2250,7 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2217,7 +2250,7 @@ fn updateStage1Module(comp: *Compilation) !void {
2217 .emit_bin = true,2250 .emit_bin = true,
2218 .emit_asm = false,2251 .emit_asm = false,
2219 .emit_llvm_ir = false,2252 .emit_llvm_ir = false,
2220 .test_is_evented = false,2253 .test_is_evented = comp.test_evented_io,
2221 .verbose_tokenize = comp.verbose_tokenize,2254 .verbose_tokenize = comp.verbose_tokenize,
2222 .verbose_ast = comp.verbose_ast,2255 .verbose_ast = comp.verbose_ast,
2223 .verbose_ir = comp.verbose_ir,2256 .verbose_ir = comp.verbose_ir,
src/main.zig+618-513
...@@ -43,8 +43,10 @@ const usage =...@@ -43,8 +43,10 @@ const usage =
43 \\ env Print lib path, std path, compiler id and version43 \\ env Print lib path, std path, compiler id and version
44 \\ fmt Parse file and render in canonical zig format44 \\ fmt Parse file and render in canonical zig format
45 \\ libc Display native libc paths file or validate one45 \\ libc Display native libc paths file or validate one
46 \\ run Create executable and run immediately
46 \\ translate-c Convert C code to Zig code47 \\ translate-c Convert C code to Zig code
47 \\ targets List available compilation targets48 \\ targets List available compilation targets
49 \\ test Create and run a test build
48 \\ version Print version number and exit50 \\ version Print version number and exit
49 \\ zen Print zen of zig and exit51 \\ zen Print zen of zig and exit
50 \\52 \\
...@@ -120,6 +122,10 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -120,6 +122,10 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
120 return buildOutputType(gpa, arena, args, .{ .build = .Lib });122 return buildOutputType(gpa, arena, args, .{ .build = .Lib });
121 } else if (mem.eql(u8, cmd, "build-obj")) {123 } else if (mem.eql(u8, cmd, "build-obj")) {
122 return buildOutputType(gpa, arena, args, .{ .build = .Obj });124 return buildOutputType(gpa, arena, args, .{ .build = .Obj });
125 } else if (mem.eql(u8, cmd, "test")) {
126 return buildOutputType(gpa, arena, args, .zig_test);
127 } else if (mem.eql(u8, cmd, "run")) {
128 return buildOutputType(gpa, arena, args, .run);
123 } else if (mem.eql(u8, cmd, "cc")) {129 } else if (mem.eql(u8, cmd, "cc")) {
124 return buildOutputType(gpa, arena, args, .cc);130 return buildOutputType(gpa, arena, args, .cc);
125 } else if (mem.eql(u8, cmd, "c++")) {131 } else if (mem.eql(u8, cmd, "c++")) {
...@@ -156,6 +162,8 @@ const usage_build_generic =...@@ -156,6 +162,8 @@ const usage_build_generic =
156 \\Usage: zig build-exe <options> [files]162 \\Usage: zig build-exe <options> [files]
157 \\ zig build-lib <options> [files]163 \\ zig build-lib <options> [files]
158 \\ zig build-obj <options> [files]164 \\ zig build-obj <options> [files]
165 \\ zig test <options> [files]
166 \\ zig run <options> [file] [-- [args]]
159 \\167 \\
160 \\Supported file types:168 \\Supported file types:
161 \\ .zig Zig source code169 \\ .zig Zig source code
...@@ -233,6 +241,13 @@ const usage_build_generic =...@@ -233,6 +241,13 @@ const usage_build_generic =
233 \\ -dynamic Force output to be dynamically linked241 \\ -dynamic Force output to be dynamically linked
234 \\ -static Force output to be statically linked242 \\ -static Force output to be statically linked
235 \\243 \\
244 \\Test Options:
245 \\ --test-filter [text] Skip tests that do not match filter
246 \\ --test-name-prefix [text] Add prefix to all tests
247 \\ --test-cmd [arg] Specify test execution command one arg at a time
248 \\ --test-cmd-bin Appends test binary path to test cmd args
249 \\ --test-evented-io Runs the test in evented I/O mode
250 \\
236 \\Debug Options (Zig Compiler Development):251 \\Debug Options (Zig Compiler Development):
237 \\ -ftime-report Print timing diagnostics252 \\ -ftime-report Print timing diagnostics
238 \\ --verbose-link Display linker invocations253 \\ --verbose-link Display linker invocations
...@@ -269,6 +284,8 @@ pub fn buildOutputType(...@@ -269,6 +284,8 @@ pub fn buildOutputType(
269 cc,284 cc,
270 cpp,285 cpp,
271 translate_c,286 translate_c,
287 zig_test,
288 run,
272 },289 },
273) !void {290) !void {
274 var color: Color = .Auto;291 var color: Color = .Auto;
...@@ -321,6 +338,7 @@ pub fn buildOutputType(...@@ -321,6 +338,7 @@ pub fn buildOutputType(
321 var linker_bind_global_refs_locally: ?bool = null;338 var linker_bind_global_refs_locally: ?bool = null;
322 var linker_z_nodelete = false;339 var linker_z_nodelete = false;
323 var linker_z_defs = false;340 var linker_z_defs = false;
341 var test_evented_io = false;
324 var stack_size_override: ?u64 = null;342 var stack_size_override: ?u64 = null;
325 var use_llvm: ?bool = null;343 var use_llvm: ?bool = null;
326 var use_lld: ?bool = null;344 var use_lld: ?bool = null;
...@@ -328,6 +346,9 @@ pub fn buildOutputType(...@@ -328,6 +346,9 @@ pub fn buildOutputType(
328 var link_eh_frame_hdr = false;346 var link_eh_frame_hdr = false;
329 var libc_paths_file: ?[]const u8 = null;347 var libc_paths_file: ?[]const u8 = null;
330 var machine_code_model: std.builtin.CodeModel = .default;348 var machine_code_model: std.builtin.CodeModel = .default;
349 var runtime_args_start: ?usize = null;
350 var test_filter: ?[]const u8 = null;
351 var test_name_prefix: ?[]const u8 = null;
331352
332 var system_libs = std.ArrayList([]const u8).init(gpa);353 var system_libs = std.ArrayList([]const u8).init(gpa);
333 defer system_libs.deinit();354 defer system_libs.deinit();
...@@ -356,545 +377,575 @@ pub fn buildOutputType(...@@ -356,545 +377,575 @@ pub fn buildOutputType(
356 var frameworks = std.ArrayList([]const u8).init(gpa);377 var frameworks = std.ArrayList([]const u8).init(gpa);
357 defer frameworks.deinit();378 defer frameworks.deinit();
358379
359 if (arg_mode == .build or arg_mode == .translate_c) {380 // null means replace with the test executable binary
360 output_mode = switch (arg_mode) {381 var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
361 .build => |m| m,382 defer test_exec_args.deinit();
362 .translate_c => .Obj,383
363 else => unreachable,384 switch (arg_mode) {
364 };385 .build, .translate_c, .zig_test, .run => {
365 switch (arg_mode) {386 output_mode = switch (arg_mode) {
366 .build => switch (output_mode) {387 .build => |m| m,
367 .Exe => emit_h = .no,388 .translate_c => .Obj,
368 .Obj, .Lib => emit_h = .yes_default_path,389 .zig_test, .run => .Exe,
369 },390 else => unreachable,
370 .translate_c => emit_h = .no,391 };
371 else => unreachable,392 switch (arg_mode) {
372 }393 .build => switch (output_mode) {
373 const args = all_args[2..];394 .Exe => emit_h = .no,
374 var i: usize = 0;395 .Obj, .Lib => emit_h = .yes_default_path,
375 while (i < args.len) : (i += 1) {396 },
376 const arg = args[i];397 .translate_c, .zig_test, .run => emit_h = .no,
377 if (mem.startsWith(u8, arg, "-")) {398 else => unreachable,
378 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {399 }
379 try io.getStdOut().writeAll(usage_build_generic);400 const args = all_args[2..];
380 process.exit(0);401 var i: usize = 0;
381 } else if (mem.eql(u8, arg, "--color")) {402 while (i < args.len) : (i += 1) {
382 if (i + 1 >= args.len) {403 const arg = args[i];
383 fatal("expected [auto|on|off] after --color", .{});404 if (mem.startsWith(u8, arg, "-")) {
405 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
406 try io.getStdOut().writeAll(usage_build_generic);
407 process.exit(0);
408 } else if (mem.eql(u8, arg, "--")) {
409 if (arg_mode == .run) {
410 runtime_args_start = i + 1;
411 } else {
412 fatal("unexpected end-of-parameter mark: --", .{});
413 }
414 } else if (mem.eql(u8, arg, "--color")) {
415 if (i + 1 >= args.len) {
416 fatal("expected [auto|on|off] after --color", .{});
417 }
418 i += 1;
419 const next_arg = args[i];
420 if (mem.eql(u8, next_arg, "auto")) {
421 color = .Auto;
422 } else if (mem.eql(u8, next_arg, "on")) {
423 color = .On;
424 } else if (mem.eql(u8, next_arg, "off")) {
425 color = .Off;
426 } else {
427 fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg});
428 }
429 } else if (mem.eql(u8, arg, "--mode")) {
430 if (i + 1 >= args.len) {
431 fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode", .{});
432 }
433 i += 1;
434 const next_arg = args[i];
435 if (mem.eql(u8, next_arg, "Debug")) {
436 build_mode = .Debug;
437 } else if (mem.eql(u8, next_arg, "ReleaseSafe")) {
438 build_mode = .ReleaseSafe;
439 } else if (mem.eql(u8, next_arg, "ReleaseFast")) {
440 build_mode = .ReleaseFast;
441 } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
442 build_mode = .ReleaseSmall;
443 } else {
444 fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'", .{next_arg});
445 }
446 } else if (mem.eql(u8, arg, "--stack")) {
447 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
448 i += 1;
449 stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {
450 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
451 };
452 } else if (mem.eql(u8, arg, "--name")) {
453 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
454 i += 1;
455 provided_name = args[i];
456 } else if (mem.eql(u8, arg, "-rpath")) {
457 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
458 i += 1;
459 try rpath_list.append(args[i]);
460 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
461 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
462 i += 1;
463 try lib_dirs.append(args[i]);
464 } else if (mem.eql(u8, arg, "-T")) {
465 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
466 i += 1;
467 linker_script = args[i];
468 } else if (mem.eql(u8, arg, "--version-script")) {
469 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
470 i += 1;
471 version_script = args[i];
472 } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
473 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
474 // We don't know whether this library is part of libc or libc++ until we resolve the target.
475 // So we simply append to the list for now.
476 i += 1;
477 try system_libs.append(args[i]);
478 } else if (mem.eql(u8, arg, "-D") or
479 mem.eql(u8, arg, "-isystem") or
480 mem.eql(u8, arg, "-I") or
481 mem.eql(u8, arg, "-dirafter"))
482 {
483 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
484 i += 1;
485 try clang_argv.append(arg);
486 try clang_argv.append(args[i]);
487 } else if (mem.eql(u8, arg, "--version")) {
488 if (i + 1 >= args.len) {
489 fatal("expected parameter after --version", .{});
490 }
491 i += 1;
492 version = std.builtin.Version.parse(args[i]) catch |err| {
493 fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
494 };
495 have_version = true;
496 } else if (mem.eql(u8, arg, "-target")) {
497 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
498 i += 1;
499 target_arch_os_abi = args[i];
500 } else if (mem.eql(u8, arg, "-mcpu")) {
501 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
502 i += 1;
503 target_mcpu = args[i];
504 } else if (mem.eql(u8, arg, "-mcmodel")) {
505 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
506 i += 1;
507 machine_code_model = parseCodeModel(args[i]);
508 } else if (mem.startsWith(u8, arg, "-ofmt=")) {
509 target_ofmt = arg["-ofmt=".len..];
510 } else if (mem.startsWith(u8, arg, "-mcpu=")) {
511 target_mcpu = arg["-mcpu=".len..];
512 } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
513 machine_code_model = parseCodeModel(arg["-mcmodel=".len..]);
514 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
515 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
516 i += 1;
517 target_dynamic_linker = args[i];
518 } else if (mem.eql(u8, arg, "--libc")) {
519 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
520 i += 1;
521 libc_paths_file = args[i];
522 } else if (mem.eql(u8, arg, "--test-filter")) {
523 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
524 i += 1;
525 test_filter = args[i];
526 } else if (mem.eql(u8, arg, "--test-name-prefix")) {
527 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
528 i += 1;
529 test_name_prefix = args[i];
530 } else if (mem.eql(u8, arg, "--test-cmd")) {
531 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
532 i += 1;
533 try test_exec_args.append(args[i]);
534 } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
535 try test_exec_args.append(null);
536 } else if (mem.eql(u8, arg, "--test-evented-io")) {
537 test_evented_io = true;
538 } else if (mem.eql(u8, arg, "--watch")) {
539 watch = true;
540 } else if (mem.eql(u8, arg, "-ftime-report")) {
541 time_report = true;
542 } else if (mem.eql(u8, arg, "-fPIC")) {
543 want_pic = true;
544 } else if (mem.eql(u8, arg, "-fno-PIC")) {
545 want_pic = false;
546 } else if (mem.eql(u8, arg, "-fstack-check")) {
547 want_stack_check = true;
548 } else if (mem.eql(u8, arg, "-fno-stack-check")) {
549 want_stack_check = false;
550 } else if (mem.eql(u8, arg, "-fsanitize-c")) {
551 want_sanitize_c = true;
552 } else if (mem.eql(u8, arg, "-fno-sanitize-c")) {
553 want_sanitize_c = false;
554 } else if (mem.eql(u8, arg, "-fvalgrind")) {
555 want_valgrind = true;
556 } else if (mem.eql(u8, arg, "-fno-valgrind")) {
557 want_valgrind = false;
558 } else if (mem.eql(u8, arg, "-fLLVM")) {
559 use_llvm = true;
560 } else if (mem.eql(u8, arg, "-fno-LLVM")) {
561 use_llvm = false;
562 } else if (mem.eql(u8, arg, "-fLLD")) {
563 use_lld = true;
564 } else if (mem.eql(u8, arg, "-fno-LLD")) {
565 use_lld = false;
566 } else if (mem.eql(u8, arg, "-fClang")) {
567 use_clang = true;
568 } else if (mem.eql(u8, arg, "-fno-Clang")) {
569 use_clang = false;
570 } else if (mem.eql(u8, arg, "-rdynamic")) {
571 rdynamic = true;
572 } else if (mem.eql(u8, arg, "-femit-bin")) {
573 emit_bin = .yes_default_path;
574 } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
575 emit_bin = .{ .yes = arg["-femit-bin=".len..] };
576 } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
577 emit_bin = .no;
578 } else if (mem.eql(u8, arg, "-femit-zir")) {
579 emit_zir = .yes_default_path;
580 } else if (mem.startsWith(u8, arg, "-femit-zir=")) {
581 emit_zir = .{ .yes = arg["-femit-zir=".len..] };
582 } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
583 emit_zir = .no;
584 } else if (mem.eql(u8, arg, "-femit-h")) {
585 emit_h = .yes_default_path;
586 } else if (mem.startsWith(u8, arg, "-femit-h=")) {
587 emit_h = .{ .yes = arg["-femit-h=".len..] };
588 } else if (mem.eql(u8, arg, "-fno-emit-h")) {
589 emit_h = .no;
590 } else if (mem.eql(u8, arg, "-dynamic")) {
591 link_mode = .Dynamic;
592 } else if (mem.eql(u8, arg, "-static")) {
593 link_mode = .Static;
594 } else if (mem.eql(u8, arg, "-fdll-export-fns")) {
595 dll_export_fns = true;
596 } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
597 dll_export_fns = false;
598 } else if (mem.eql(u8, arg, "--show-builtin")) {
599 show_builtin = true;
600 } else if (mem.eql(u8, arg, "--strip")) {
601 strip = true;
602 } else if (mem.eql(u8, arg, "--single-threaded")) {
603 single_threaded = true;
604 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
605 link_eh_frame_hdr = true;
606 } else if (mem.eql(u8, arg, "-Bsymbolic")) {
607 linker_bind_global_refs_locally = true;
608 } else if (mem.eql(u8, arg, "--verbose-link")) {
609 verbose_link = true;
610 } else if (mem.eql(u8, arg, "--verbose-cc")) {
611 verbose_cc = true;
612 } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
613 verbose_tokenize = true;
614 } else if (mem.eql(u8, arg, "--verbose-ast")) {
615 verbose_ast = true;
616 } else if (mem.eql(u8, arg, "--verbose-ir")) {
617 verbose_ir = true;
618 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
619 verbose_llvm_ir = true;
620 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
621 verbose_cimport = true;
622 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
623 verbose_llvm_cpu_features = true;
624 } else if (mem.startsWith(u8, arg, "-T")) {
625 linker_script = arg[2..];
626 } else if (mem.startsWith(u8, arg, "-L")) {
627 try lib_dirs.append(arg[2..]);
628 } else if (mem.startsWith(u8, arg, "-l")) {
629 // We don't know whether this library is part of libc or libc++ until we resolve the target.
630 // So we simply append to the list for now.
631 try system_libs.append(arg[2..]);
632 } else if (mem.startsWith(u8, arg, "-D") or
633 mem.startsWith(u8, arg, "-I"))
634 {
635 try clang_argv.append(arg);
636 } else {
637 fatal("unrecognized parameter: '{}'", .{arg});
384 }638 }
639 } else switch (Compilation.classifyFileExt(arg)) {
640 .object, .static_library => {
641 try link_objects.append(arg);
642 },
643 .assembly, .c, .cpp, .h, .ll, .bc => {
644 // TODO a way to pass extra flags on the CLI
645 try c_source_files.append(.{ .src_path = arg });
646 },
647 .shared_library => {
648 fatal("linking against dynamic libraries not yet supported", .{});
649 },
650 .zig, .zir => {
651 if (root_src_file) |other| {
652 fatal("found another zig file '{}' after root source file '{}'", .{ arg, other });
653 } else {
654 root_src_file = arg;
655 }
656 },
657 .unknown => {
658 fatal("unrecognized file extension of parameter '{}'", .{arg});
659 },
660 }
661 }
662 },
663 .cc, .cpp => {
664 emit_h = .no;
665 strip = true;
666 ensure_libc_on_non_freestanding = true;
667 ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
668 want_native_include_dirs = true;
669
670 var c_arg = false;
671 var is_shared_lib = false;
672 var linker_args = std.ArrayList([]const u8).init(arena);
673 var it = ClangArgIterator.init(arena, all_args);
674 while (it.has_next) {
675 it.next() catch |err| {
676 fatal("unable to parse command line parameters: {}", .{@errorName(err)});
677 };
678 switch (it.zig_equivalent) {
679 .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown
680 .o => {
681 // -o
682 emit_bin = .{ .yes = it.only_arg };
683 enable_cache = true;
684 },
685 .c => c_arg = true, // -c
686 .other => {
687 try clang_argv.appendSlice(it.other_args);
688 },
689 .positional => {
690 const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg));
691 switch (file_ext) {
692 .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }),
693 .unknown, .shared_library, .object, .static_library => {
694 try link_objects.append(it.only_arg);
695 },
696 .zig, .zir => {
697 if (root_src_file) |other| {
698 fatal("found another zig file '{}' after root source file '{}'", .{ it.only_arg, other });
699 } else {
700 root_src_file = it.only_arg;
701 }
702 },
703 }
704 },
705 .l => {
706 // -l
707 // We don't know whether this library is part of libc or libc++ until we resolve the target.
708 // So we simply append to the list for now.
709 try system_libs.append(it.only_arg);
710 },
711 .ignore => {},
712 .driver_punt => {
713 // Never mind what we're doing, just pass the args directly. For example --help.
714 return punt_to_clang(arena, all_args);
715 },
716 .pic => want_pic = true,
717 .no_pic => want_pic = false,
718 .nostdlib => ensure_libc_on_non_freestanding = false,
719 .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
720 .shared => {
721 link_mode = .Dynamic;
722 is_shared_lib = true;
723 },
724 .rdynamic => rdynamic = true,
725 .wl => {
726 var split_it = mem.split(it.only_arg, ",");
727 while (split_it.next()) |linker_arg| {
728 try linker_args.append(linker_arg);
729 }
730 },
731 .pp_or_asm => {
732 // This handles both -E and -S.
733 only_pp_or_asm = true;
734 try clang_argv.appendSlice(it.other_args);
735 },
736 .optimize => {
737 // Alright, what release mode do they want?
738 if (mem.eql(u8, it.only_arg, "Os")) {
739 build_mode = .ReleaseSmall;
740 } else if (mem.eql(u8, it.only_arg, "O2") or
741 mem.eql(u8, it.only_arg, "O3") or
742 mem.eql(u8, it.only_arg, "O4"))
743 {
744 build_mode = .ReleaseFast;
745 } else if (mem.eql(u8, it.only_arg, "Og") or
746 mem.eql(u8, it.only_arg, "O0"))
747 {
748 build_mode = .Debug;
749 } else {
750 try clang_argv.appendSlice(it.other_args);
751 }
752 },
753 .debug => {
754 strip = false;
755 if (mem.eql(u8, it.only_arg, "-g")) {
756 // We handled with strip = false above.
757 } else {
758 try clang_argv.appendSlice(it.other_args);
759 }
760 },
761 .sanitize => {
762 if (mem.eql(u8, it.only_arg, "undefined")) {
763 want_sanitize_c = true;
764 } else {
765 try clang_argv.appendSlice(it.other_args);
766 }
767 },
768 .linker_script => linker_script = it.only_arg,
769 .verbose_cmds => {
770 verbose_cc = true;
771 verbose_link = true;
772 },
773 .for_linker => try linker_args.append(it.only_arg),
774 .linker_input_z => {
775 try linker_args.append("-z");
776 try linker_args.append(it.only_arg);
777 },
778 .lib_dir => try lib_dirs.append(it.only_arg),
779 .mcpu => target_mcpu = it.only_arg,
780 .dep_file => {
781 disable_c_depfile = true;
782 try clang_argv.appendSlice(it.other_args);
783 },
784 .framework_dir => try framework_dirs.append(it.only_arg),
785 .framework => try frameworks.append(it.only_arg),
786 .nostdlibinc => want_native_include_dirs = false,
787 }
788 }
789 // Parse linker args.
790 var i: usize = 0;
791 while (i < linker_args.items.len) : (i += 1) {
792 const arg = linker_args.items[i];
793 if (mem.eql(u8, arg, "-soname")) {
385 i += 1;794 i += 1;
386 const next_arg = args[i];795 if (i >= linker_args.items.len) {
387 if (mem.eql(u8, next_arg, "auto")) {796 fatal("expected linker arg after '{}'", .{arg});
388 color = .Auto;
389 } else if (mem.eql(u8, next_arg, "on")) {
390 color = .On;
391 } else if (mem.eql(u8, next_arg, "off")) {
392 color = .Off;
393 } else {
394 fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg});
395 }797 }
396 } else if (mem.eql(u8, arg, "--mode")) {798 const soname = linker_args.items[i];
397 if (i + 1 >= args.len) {799 override_soname = soname;
398 fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode", .{});800 // Use it as --name.
801 // Example: libsoundio.so.2
802 var prefix: usize = 0;
803 if (mem.startsWith(u8, soname, "lib")) {
804 prefix = 3;
399 }805 }
400 i += 1;806 var end: usize = soname.len;
401 const next_arg = args[i];807 if (mem.endsWith(u8, soname, ".so")) {
402 if (mem.eql(u8, next_arg, "Debug")) {808 end -= 3;
403 build_mode = .Debug;
404 } else if (mem.eql(u8, next_arg, "ReleaseSafe")) {
405 build_mode = .ReleaseSafe;
406 } else if (mem.eql(u8, next_arg, "ReleaseFast")) {
407 build_mode = .ReleaseFast;
408 } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
409 build_mode = .ReleaseSmall;
410 } else {809 } else {
411 fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'", .{next_arg});810 var found_digit = false;
811 while (end > 0 and std.ascii.isDigit(soname[end - 1])) {
812 found_digit = true;
813 end -= 1;
814 }
815 if (found_digit and end > 0 and soname[end - 1] == '.') {
816 end -= 1;
817 } else {
818 end = soname.len;
819 }
820 if (mem.endsWith(u8, soname[prefix..end], ".so")) {
821 end -= 3;
822 }
412 }823 }
413 } else if (mem.eql(u8, arg, "--stack")) {824 provided_name = soname[prefix..end];
414 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
415 i += 1;
416 stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {
417 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
418 };
419 } else if (mem.eql(u8, arg, "--name")) {
420 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
421 i += 1;
422 provided_name = args[i];
423 } else if (mem.eql(u8, arg, "-rpath")) {825 } else if (mem.eql(u8, arg, "-rpath")) {
424 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
425 i += 1;826 i += 1;
426 try rpath_list.append(args[i]);827 if (i >= linker_args.items.len) {
427 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {828 fatal("expected linker arg after '{}'", .{arg});
428 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});829 }
429 i += 1;830 try rpath_list.append(linker_args.items[i]);
430 try lib_dirs.append(args[i]);831 } else if (mem.eql(u8, arg, "-I") or
431 } else if (mem.eql(u8, arg, "-T")) {832 mem.eql(u8, arg, "--dynamic-linker") or
432 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});833 mem.eql(u8, arg, "-dynamic-linker"))
433 i += 1;
434 linker_script = args[i];
435 } else if (mem.eql(u8, arg, "--version-script")) {
436 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
437 i += 1;
438 version_script = args[i];
439 } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
440 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
441 // We don't know whether this library is part of libc or libc++ until we resolve the target.
442 // So we simply append to the list for now.
443 i += 1;
444 try system_libs.append(args[i]);
445 } else if (mem.eql(u8, arg, "-D") or
446 mem.eql(u8, arg, "-isystem") or
447 mem.eql(u8, arg, "-I") or
448 mem.eql(u8, arg, "-dirafter"))
449 {834 {
450 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
451 i += 1;835 i += 1;
452 try clang_argv.append(arg);836 if (i >= linker_args.items.len) {
453 try clang_argv.append(args[i]);837 fatal("expected linker arg after '{}'", .{arg});
454 } else if (mem.eql(u8, arg, "--version")) {
455 if (i + 1 >= args.len) {
456 fatal("expected parameter after --version", .{});
457 }838 }
458 i += 1;839 target_dynamic_linker = linker_args.items[i];
459 version = std.builtin.Version.parse(args[i]) catch |err| {840 } else if (mem.eql(u8, arg, "-E") or
460 fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });841 mem.eql(u8, arg, "--export-dynamic") or
461 };842 mem.eql(u8, arg, "-export-dynamic"))
462 have_version = true;843 {
463 } else if (mem.eql(u8, arg, "-target")) {
464 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
465 i += 1;
466 target_arch_os_abi = args[i];
467 } else if (mem.eql(u8, arg, "-mcpu")) {
468 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
469 i += 1;
470 target_mcpu = args[i];
471 } else if (mem.eql(u8, arg, "-mcmodel")) {
472 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
473 i += 1;
474 machine_code_model = parseCodeModel(args[i]);
475 } else if (mem.startsWith(u8, arg, "-ofmt=")) {
476 target_ofmt = arg["-ofmt=".len..];
477 } else if (mem.startsWith(u8, arg, "-mcpu=")) {
478 target_mcpu = arg["-mcpu=".len..];
479 } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
480 machine_code_model = parseCodeModel(arg["-mcmodel=".len..]);
481 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
482 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
483 i += 1;
484 target_dynamic_linker = args[i];
485 } else if (mem.eql(u8, arg, "--libc")) {
486 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
487 i += 1;
488 libc_paths_file = args[i];
489 } else if (mem.eql(u8, arg, "--watch")) {
490 watch = true;
491 } else if (mem.eql(u8, arg, "-ftime-report")) {
492 time_report = true;
493 } else if (mem.eql(u8, arg, "-fPIC")) {
494 want_pic = true;
495 } else if (mem.eql(u8, arg, "-fno-PIC")) {
496 want_pic = false;
497 } else if (mem.eql(u8, arg, "-fstack-check")) {
498 want_stack_check = true;
499 } else if (mem.eql(u8, arg, "-fno-stack-check")) {
500 want_stack_check = false;
501 } else if (mem.eql(u8, arg, "-fsanitize-c")) {
502 want_sanitize_c = true;
503 } else if (mem.eql(u8, arg, "-fno-sanitize-c")) {
504 want_sanitize_c = false;
505 } else if (mem.eql(u8, arg, "-fvalgrind")) {
506 want_valgrind = true;
507 } else if (mem.eql(u8, arg, "-fno-valgrind")) {
508 want_valgrind = false;
509 } else if (mem.eql(u8, arg, "-fLLVM")) {
510 use_llvm = true;
511 } else if (mem.eql(u8, arg, "-fno-LLVM")) {
512 use_llvm = false;
513 } else if (mem.eql(u8, arg, "-fLLD")) {
514 use_lld = true;
515 } else if (mem.eql(u8, arg, "-fno-LLD")) {
516 use_lld = false;
517 } else if (mem.eql(u8, arg, "-fClang")) {
518 use_clang = true;
519 } else if (mem.eql(u8, arg, "-fno-Clang")) {
520 use_clang = false;
521 } else if (mem.eql(u8, arg, "-rdynamic")) {
522 rdynamic = true;844 rdynamic = true;
523 } else if (mem.eql(u8, arg, "-femit-bin")) {845 } else if (mem.eql(u8, arg, "--version-script")) {
524 emit_bin = .yes_default_path;846 i += 1;
525 } else if (mem.startsWith(u8, arg, "-femit-bin=")) {847 if (i >= linker_args.items.len) {
526 emit_bin = .{ .yes = arg["-femit-bin=".len..] };848 fatal("expected linker arg after '{}'", .{arg});
527 } else if (mem.eql(u8, arg, "-fno-emit-bin")) {849 }
528 emit_bin = .no;850 version_script = linker_args.items[i];
529 } else if (mem.eql(u8, arg, "-femit-zir")) {851 } else if (mem.startsWith(u8, arg, "-O")) {
530 emit_zir = .yes_default_path;852 try lld_argv.append(arg);
531 } else if (mem.startsWith(u8, arg, "-femit-zir=")) {853 } else if (mem.eql(u8, arg, "--gc-sections")) {
532 emit_zir = .{ .yes = arg["-femit-zir=".len..] };854 linker_gc_sections = true;
533 } else if (mem.eql(u8, arg, "-fno-emit-zir")) {855 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
534 emit_zir = .no;856 linker_gc_sections = false;
535 } else if (mem.eql(u8, arg, "-femit-h")) {857 } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
536 emit_h = .yes_default_path;858 mem.eql(u8, arg, "-allow-shlib-undefined"))
537 } else if (mem.startsWith(u8, arg, "-femit-h=")) {859 {
538 emit_h = .{ .yes = arg["-femit-h=".len..] };860 linker_allow_shlib_undefined = true;
539 } else if (mem.eql(u8, arg, "-fno-emit-h")) {861 } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
540 emit_h = .no;862 mem.eql(u8, arg, "-no-allow-shlib-undefined"))
541 } else if (mem.eql(u8, arg, "-dynamic")) {863 {
542 link_mode = .Dynamic;864 linker_allow_shlib_undefined = false;
543 } else if (mem.eql(u8, arg, "-static")) {
544 link_mode = .Static;
545 } else if (mem.eql(u8, arg, "-fdll-export-fns")) {
546 dll_export_fns = true;
547 } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
548 dll_export_fns = false;
549 } else if (mem.eql(u8, arg, "--show-builtin")) {
550 show_builtin = true;
551 } else if (mem.eql(u8, arg, "--strip")) {
552 strip = true;
553 } else if (mem.eql(u8, arg, "--single-threaded")) {
554 single_threaded = true;
555 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
556 link_eh_frame_hdr = true;
557 } else if (mem.eql(u8, arg, "-Bsymbolic")) {865 } else if (mem.eql(u8, arg, "-Bsymbolic")) {
558 linker_bind_global_refs_locally = true;866 linker_bind_global_refs_locally = true;
559 } else if (mem.eql(u8, arg, "--verbose-link")) {867 } else if (mem.eql(u8, arg, "-z")) {
560 verbose_link = true;868 i += 1;
561 } else if (mem.eql(u8, arg, "--verbose-cc")) {869 if (i >= linker_args.items.len) {
562 verbose_cc = true;870 fatal("expected linker arg after '{}'", .{arg});
563 } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
564 verbose_tokenize = true;
565 } else if (mem.eql(u8, arg, "--verbose-ast")) {
566 verbose_ast = true;
567 } else if (mem.eql(u8, arg, "--verbose-ir")) {
568 verbose_ir = true;
569 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
570 verbose_llvm_ir = true;
571 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
572 verbose_cimport = true;
573 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
574 verbose_llvm_cpu_features = true;
575 } else if (mem.startsWith(u8, arg, "-T")) {
576 linker_script = arg[2..];
577 } else if (mem.startsWith(u8, arg, "-L")) {
578 try lib_dirs.append(arg[2..]);
579 } else if (mem.startsWith(u8, arg, "-l")) {
580 // We don't know whether this library is part of libc or libc++ until we resolve the target.
581 // So we simply append to the list for now.
582 try system_libs.append(arg[2..]);
583 } else if (mem.startsWith(u8, arg, "-D") or
584 mem.startsWith(u8, arg, "-I"))
585 {
586 try clang_argv.append(arg);
587 } else {
588 fatal("unrecognized parameter: '{}'", .{arg});
589 }
590 } else switch (Compilation.classifyFileExt(arg)) {
591 .object, .static_library => {
592 try link_objects.append(arg);
593 },
594 .assembly, .c, .cpp, .h, .ll, .bc => {
595 // TODO a way to pass extra flags on the CLI
596 try c_source_files.append(.{ .src_path = arg });
597 },
598 .shared_library => {
599 fatal("linking against dynamic libraries not yet supported", .{});
600 },
601 .zig, .zir => {
602 if (root_src_file) |other| {
603 fatal("found another zig file '{}' after root source file '{}'", .{ arg, other });
604 } else {
605 root_src_file = arg;
606 }
607 },
608 .unknown => {
609 fatal("unrecognized file extension of parameter '{}'", .{arg});
610 },
611 }
612 }
613 } else {
614 emit_h = .no;
615 strip = true;
616 ensure_libc_on_non_freestanding = true;
617 ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
618 want_native_include_dirs = true;
619
620 var c_arg = false;
621 var is_shared_lib = false;
622 var linker_args = std.ArrayList([]const u8).init(arena);
623 var it = ClangArgIterator.init(arena, all_args);
624 while (it.has_next) {
625 it.next() catch |err| {
626 fatal("unable to parse command line parameters: {}", .{@errorName(err)});
627 };
628 switch (it.zig_equivalent) {
629 .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown
630 .o => {
631 // -o
632 emit_bin = .{ .yes = it.only_arg };
633 enable_cache = true;
634 },
635 .c => c_arg = true, // -c
636 .other => {
637 try clang_argv.appendSlice(it.other_args);
638 },
639 .positional => {
640 const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg));
641 switch (file_ext) {
642 .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }),
643 .unknown, .shared_library, .object, .static_library => {
644 try link_objects.append(it.only_arg);
645 },
646 .zig, .zir => {
647 if (root_src_file) |other| {
648 fatal("found another zig file '{}' after root source file '{}'", .{ it.only_arg, other });
649 } else {
650 root_src_file = it.only_arg;
651 }
652 },
653 }
654 },
655 .l => {
656 // -l
657 // We don't know whether this library is part of libc or libc++ until we resolve the target.
658 // So we simply append to the list for now.
659 try system_libs.append(it.only_arg);
660 },
661 .ignore => {},
662 .driver_punt => {
663 // Never mind what we're doing, just pass the args directly. For example --help.
664 return punt_to_clang(arena, all_args);
665 },
666 .pic => want_pic = true,
667 .no_pic => want_pic = false,
668 .nostdlib => ensure_libc_on_non_freestanding = false,
669 .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
670 .shared => {
671 link_mode = .Dynamic;
672 is_shared_lib = true;
673 },
674 .rdynamic => rdynamic = true,
675 .wl => {
676 var split_it = mem.split(it.only_arg, ",");
677 while (split_it.next()) |linker_arg| {
678 try linker_args.append(linker_arg);
679 }
680 },
681 .pp_or_asm => {
682 // This handles both -E and -S.
683 only_pp_or_asm = true;
684 try clang_argv.appendSlice(it.other_args);
685 },
686 .optimize => {
687 // Alright, what release mode do they want?
688 if (mem.eql(u8, it.only_arg, "Os")) {
689 build_mode = .ReleaseSmall;
690 } else if (mem.eql(u8, it.only_arg, "O2") or
691 mem.eql(u8, it.only_arg, "O3") or
692 mem.eql(u8, it.only_arg, "O4"))
693 {
694 build_mode = .ReleaseFast;
695 } else if (mem.eql(u8, it.only_arg, "Og") or
696 mem.eql(u8, it.only_arg, "O0"))
697 {
698 build_mode = .Debug;
699 } else {
700 try clang_argv.appendSlice(it.other_args);
701 }
702 },
703 .debug => {
704 strip = false;
705 if (mem.eql(u8, it.only_arg, "-g")) {
706 // We handled with strip = false above.
707 } else {
708 try clang_argv.appendSlice(it.other_args);
709 }871 }
710 },872 const z_arg = linker_args.items[i];
711 .sanitize => {873 if (mem.eql(u8, z_arg, "nodelete")) {
712 if (mem.eql(u8, it.only_arg, "undefined")) {874 linker_z_nodelete = true;
713 want_sanitize_c = true;875 } else if (mem.eql(u8, z_arg, "defs")) {
876 linker_z_defs = true;
714 } else {877 } else {
715 try clang_argv.appendSlice(it.other_args);878 warn("unsupported linker arg: -z {}", .{z_arg});
716 }879 }
717 },880 } else if (mem.eql(u8, arg, "--major-image-version")) {
718 .linker_script => linker_script = it.only_arg,881 i += 1;
719 .verbose_cmds => {882 if (i >= linker_args.items.len) {
720 verbose_cc = true;883 fatal("expected linker arg after '{}'", .{arg});
721 verbose_link = true;
722 },
723 .for_linker => try linker_args.append(it.only_arg),
724 .linker_input_z => {
725 try linker_args.append("-z");
726 try linker_args.append(it.only_arg);
727 },
728 .lib_dir => try lib_dirs.append(it.only_arg),
729 .mcpu => target_mcpu = it.only_arg,
730 .dep_file => {
731 disable_c_depfile = true;
732 try clang_argv.appendSlice(it.other_args);
733 },
734 .framework_dir => try framework_dirs.append(it.only_arg),
735 .framework => try frameworks.append(it.only_arg),
736 .nostdlibinc => want_native_include_dirs = false,
737 }
738 }
739 // Parse linker args.
740 var i: usize = 0;
741 while (i < linker_args.items.len) : (i += 1) {
742 const arg = linker_args.items[i];
743 if (mem.eql(u8, arg, "-soname")) {
744 i += 1;
745 if (i >= linker_args.items.len) {
746 fatal("expected linker arg after '{}'", .{arg});
747 }
748 const soname = linker_args.items[i];
749 override_soname = soname;
750 // Use it as --name.
751 // Example: libsoundio.so.2
752 var prefix: usize = 0;
753 if (mem.startsWith(u8, soname, "lib")) {
754 prefix = 3;
755 }
756 var end: usize = soname.len;
757 if (mem.endsWith(u8, soname, ".so")) {
758 end -= 3;
759 } else {
760 var found_digit = false;
761 while (end > 0 and std.ascii.isDigit(soname[end - 1])) {
762 found_digit = true;
763 end -= 1;
764 }884 }
765 if (found_digit and end > 0 and soname[end - 1] == '.') {885 version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
766 end -= 1;886 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
767 } else {887 };
768 end = soname.len;888 have_version = true;
889 } else if (mem.eql(u8, arg, "--minor-image-version")) {
890 i += 1;
891 if (i >= linker_args.items.len) {
892 fatal("expected linker arg after '{}'", .{arg});
769 }893 }
770 if (mem.endsWith(u8, soname[prefix..end], ".so")) {894 version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
771 end -= 3;895 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
896 };
897 have_version = true;
898 } else if (mem.eql(u8, arg, "--stack")) {
899 i += 1;
900 if (i >= linker_args.items.len) {
901 fatal("expected linker arg after '{}'", .{arg});
772 }902 }
773 }903 stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
774 provided_name = soname[prefix..end];904 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
775 } else if (mem.eql(u8, arg, "-rpath")) {905 };
776 i += 1;
777 if (i >= linker_args.items.len) {
778 fatal("expected linker arg after '{}'", .{arg});
779 }
780 try rpath_list.append(linker_args.items[i]);
781 } else if (mem.eql(u8, arg, "-I") or
782 mem.eql(u8, arg, "--dynamic-linker") or
783 mem.eql(u8, arg, "-dynamic-linker"))
784 {
785 i += 1;
786 if (i >= linker_args.items.len) {
787 fatal("expected linker arg after '{}'", .{arg});
788 }
789 target_dynamic_linker = linker_args.items[i];
790 } else if (mem.eql(u8, arg, "-E") or
791 mem.eql(u8, arg, "--export-dynamic") or
792 mem.eql(u8, arg, "-export-dynamic"))
793 {
794 rdynamic = true;
795 } else if (mem.eql(u8, arg, "--version-script")) {
796 i += 1;
797 if (i >= linker_args.items.len) {
798 fatal("expected linker arg after '{}'", .{arg});
799 }
800 version_script = linker_args.items[i];
801 } else if (mem.startsWith(u8, arg, "-O")) {
802 try lld_argv.append(arg);
803 } else if (mem.eql(u8, arg, "--gc-sections")) {
804 linker_gc_sections = true;
805 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
806 linker_gc_sections = false;
807 } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
808 mem.eql(u8, arg, "-allow-shlib-undefined"))
809 {
810 linker_allow_shlib_undefined = true;
811 } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
812 mem.eql(u8, arg, "-no-allow-shlib-undefined"))
813 {
814 linker_allow_shlib_undefined = false;
815 } else if (mem.eql(u8, arg, "-Bsymbolic")) {
816 linker_bind_global_refs_locally = true;
817 } else if (mem.eql(u8, arg, "-z")) {
818 i += 1;
819 if (i >= linker_args.items.len) {
820 fatal("expected linker arg after '{}'", .{arg});
821 }
822 const z_arg = linker_args.items[i];
823 if (mem.eql(u8, z_arg, "nodelete")) {
824 linker_z_nodelete = true;
825 } else if (mem.eql(u8, z_arg, "defs")) {
826 linker_z_defs = true;
827 } else {906 } else {
828 warn("unsupported linker arg: -z {}", .{z_arg});907 warn("unsupported linker arg: {}", .{arg});
829 }
830 } else if (mem.eql(u8, arg, "--major-image-version")) {
831 i += 1;
832 if (i >= linker_args.items.len) {
833 fatal("expected linker arg after '{}'", .{arg});
834 }
835 version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
836 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
837 };
838 have_version = true;
839 } else if (mem.eql(u8, arg, "--minor-image-version")) {
840 i += 1;
841 if (i >= linker_args.items.len) {
842 fatal("expected linker arg after '{}'", .{arg});
843 }908 }
844 version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
845 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
846 };
847 have_version = true;
848 } else if (mem.eql(u8, arg, "--stack")) {
849 i += 1;
850 if (i >= linker_args.items.len) {
851 fatal("expected linker arg after '{}'", .{arg});
852 }
853 stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
854 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
855 };
856 } else {
857 warn("unsupported linker arg: {}", .{arg});
858 }909 }
859 }
860910
861 if (want_sanitize_c) |wsc| {911 if (want_sanitize_c) |wsc| {
862 if (wsc and build_mode == .ReleaseFast) {912 if (wsc and build_mode == .ReleaseFast) {
863 build_mode = .ReleaseSafe;913 build_mode = .ReleaseSafe;
914 }
864 }915 }
865 }
866916
867 if (only_pp_or_asm) {917 if (only_pp_or_asm) {
868 output_mode = .Obj;918 output_mode = .Obj;
869 fatal("TODO implement using zig cc as a preprocessor", .{});919 fatal("TODO implement using zig cc as a preprocessor", .{});
870 //// Transfer "link_objects" into c_source_files so that all those920 //// Transfer "link_objects" into c_source_files so that all those
871 //// args make it onto the command line.921 //// args make it onto the command line.
872 //try c_source_files.appendSlice(link_objects.items);922 //try c_source_files.appendSlice(link_objects.items);
873 //for (c_source_files.items) |c_source_file| {923 //for (c_source_files.items) |c_source_file| {
874 // const src_path = switch (emit_bin) {924 // const src_path = switch (emit_bin) {
875 // .yes => |p| p,925 // .yes => |p| p,
876 // else => c_source_file.source_path,926 // else => c_source_file.source_path,
877 // };927 // };
878 // const basename = fs.path.basename(src_path);928 // const basename = fs.path.basename(src_path);
879 // c_source_file.preprocessor_only_basename = basename;929 // c_source_file.preprocessor_only_basename = basename;
880 //}930 //}
881 //emit_bin = .no;931 //emit_bin = .no;
882 } else if (!c_arg) {932 } else if (!c_arg) {
883 output_mode = if (is_shared_lib) .Lib else .Exe;933 output_mode = if (is_shared_lib) .Lib else .Exe;
884 switch (emit_bin) {934 switch (emit_bin) {
885 .no, .yes_default_path => {935 .no, .yes_default_path => {
886 emit_bin = .{ .yes = "a.out" };936 emit_bin = .{ .yes = "a.out" };
887 enable_cache = true;937 enable_cache = true;
888 },938 },
889 .yes => {},939 .yes => {},
940 }
941 } else {
942 output_mode = .Obj;
890 }943 }
891 } else {944 if (c_source_files.items.len == 0 and link_objects.items.len == 0) {
892 output_mode = .Obj;945 // For example `zig cc` and no args should print the "no input files" message.
893 }946 return punt_to_clang(arena, all_args);
894 if (c_source_files.items.len == 0 and link_objects.items.len == 0) {947 }
895 // For example `zig cc` and no args should print the "no input files" message.948 },
896 return punt_to_clang(arena, all_args);
897 }
898 }949 }
899950
900 if (arg_mode == .translate_c and c_source_files.items.len != 1) {951 if (arg_mode == .translate_c and c_source_files.items.len != 1) {
...@@ -902,7 +953,9 @@ pub fn buildOutputType(...@@ -902,7 +953,9 @@ pub fn buildOutputType(
902 }953 }
903954
904 const root_name = if (provided_name) |n| n else blk: {955 const root_name = if (provided_name) |n| n else blk: {
905 if (root_src_file) |file| {956 if (arg_mode == .zig_test) {
957 break :blk "test";
958 } else if (root_src_file) |file| {
906 const basename = fs.path.basename(file);959 const basename = fs.path.basename(file);
907 break :blk mem.split(basename, ".").next().?;960 break :blk mem.split(basename, ".").next().?;
908 } else if (c_source_files.items.len == 1) {961 } else if (c_source_files.items.len == 1) {
...@@ -916,6 +969,8 @@ pub fn buildOutputType(...@@ -916,6 +969,8 @@ pub fn buildOutputType(
916 break :blk mem.split(basename, ".").next().?;969 break :blk mem.split(basename, ".").next().?;
917 } else if (show_builtin) {970 } else if (show_builtin) {
918 break :blk "builtin";971 break :blk "builtin";
972 } else if (arg_mode == .run) {
973 break :blk "run";
919 } else {974 } else {
920 fatal("--name [name] not provided and unable to infer", .{});975 fatal("--name [name] not provided and unable to infer", .{});
921 }976 }
...@@ -1220,6 +1275,10 @@ pub fn buildOutputType(...@@ -1220,6 +1275,10 @@ pub fn buildOutputType(
1220 .machine_code_model = machine_code_model,1275 .machine_code_model = machine_code_model,
1221 .color = color,1276 .color = color,
1222 .time_report = time_report,1277 .time_report = time_report,
1278 .is_test = arg_mode == .zig_test,
1279 .test_evented_io = test_evented_io,
1280 .test_filter = test_filter,
1281 .test_name_prefix = test_name_prefix,
1223 }) catch |err| {1282 }) catch |err| {
1224 fatal("unable to create compilation: {}", .{@errorName(err)});1283 fatal("unable to create compilation: {}", .{@errorName(err)});
1225 };1284 };
...@@ -1243,6 +1302,52 @@ pub fn buildOutputType(...@@ -1243,6 +1302,52 @@ pub fn buildOutputType(
1243 std.log.warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});1302 std.log.warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});
1244 }1303 }
12451304
1305 switch (arg_mode) {
1306 .run, .zig_test => run: {
1307 const exe_loc = emit_bin_loc orelse break :run;
1308 const exe_directory = exe_loc.directory orelse comp.bin_file.options.directory;
1309 const exe_path = try fs.path.join(arena, &[_][]const u8{
1310 exe_directory.path orelse ".", exe_loc.basename,
1311 });
1312
1313 var argv = std.ArrayList([]const u8).init(gpa);
1314 defer argv.deinit();
1315
1316 if (test_exec_args.items.len == 0) {
1317 try argv.append(exe_path);
1318 } else {
1319 for (test_exec_args.items) |arg| {
1320 try argv.append(arg orelse exe_path);
1321 }
1322 }
1323 if (runtime_args_start) |i| {
1324 try argv.appendSlice(all_args[i..]);
1325 }
1326 // TODO On operating systems that support it, do an execve here rather than child process,
1327 // when watch=false.
1328 const child = try std.ChildProcess.init(argv.items, gpa);
1329 defer child.deinit();
1330
1331 child.stdin_behavior = .Inherit;
1332 child.stdout_behavior = .Inherit;
1333 child.stderr_behavior = .Inherit;
1334
1335 const term = try child.spawnAndWait();
1336 switch (term) {
1337 .Exited => |code| {
1338 if (code != 0) {
1339 // TODO https://github.com/ziglang/zig/issues/6342
1340 process.exit(1);
1341 }
1342 },
1343 else => process.exit(1),
1344 }
1345 if (!watch)
1346 process.exit(0);
1347 },
1348 else => {},
1349 }
1350
1246 const stdin = std.io.getStdIn().inStream();1351 const stdin = std.io.getStdIn().inStream();
1247 const stderr = std.io.getStdErr().outStream();1352 const stderr = std.io.getStdErr().outStream();
1248 var repl_buf: [1024]u8 = undefined;1353 var repl_buf: [1024]u8 = undefined;