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 @@
11 * build & link against libcxx and libcxxabi
2 * `zig test`
32 * `zig build`
3 * repair @cImport
4 * make sure zig cc works
5 - using it as a preprocessor (-E)
6 - try building some software
47 * `-ftime-report`
58 * -fstack-report print stack size diagnostics\n"
69 * -fdump-analysis write analysis.json file with type information\n"
......@@ -11,17 +14,14 @@
1114 * -femit-llvm-ir produce a .ll file with LLVM IR\n"
1215 * -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n"
1316 * --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
1717 * implement proper parsing of LLD stderr/stdout and exposing compile errors
1818 * implement proper parsing of clang stderr/stdout and exposing compile errors
1919 * support rpaths in ELF linker code
20 * repair @cImport
2120 * add CLI support for a way to pass extra flags to c source files
2221 * musl
2322 * mingw-w64
2423 * use global zig-cache dir for crt files
24 * use global zig-cache dir for `zig run` executables but not `zig test`
2525 * MachO LLD linking
2626 * COFF LLD linking
2727 * WASM LLD linking
......@@ -30,9 +30,9 @@
3030 * audit the CLI options for stage2
3131 * `zig init-lib`
3232 * `zig init-exe`
33 * `zig run`
3433 * restore error messages for stage2_add_link_lib
3534 * 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
3737 * implement proper compile errors for failing to build glibc crt files and shared libs
3838 * implement -fno-emit-bin
......@@ -66,3 +66,4 @@
6666 * some kind of "zig identifier escape" function rather than unconditionally using @"" syntax
6767 in builtin.zig
6868 * 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,
9797/// Don't use this for anything other than stage1 compatibility.
9898color: @import("main.zig").Color = .Auto,
9999
100test_filter: ?[]const u8,
101test_name_prefix: ?[]const u8,
102test_evented_io: bool,
103
100104pub const InnerError = Module.InnerError;
101105
102106pub const CRTFile = struct {
......@@ -327,6 +331,9 @@ pub const InitOptions = struct {
327331 machine_code_model: std.builtin.CodeModel = .default,
328332 /// This is for stage1 and should be deleted upon completion of self-hosting.
329333 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,
330337};
331338
332339pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
......@@ -554,6 +561,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
554561 hash.add(single_threaded);
555562 hash.add(options.target.os.getVersionRange());
556563 hash.add(dll_export_fns);
564 hash.add(options.is_test);
557565
558566 const digest = hash.final();
559567 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 {
728736 .is_test = options.is_test,
729737 .color = options.color,
730738 .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,
731742 };
732743 break :comp comp;
733744 };
......@@ -1996,6 +2007,25 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
19962007 comp.bin_file.options.strip,
19972008 @tagName(comp.bin_file.options.machine_code_model),
19982009 });
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
19992029 return buffer.toOwnedSlice();
20002030}
20012031
......@@ -2129,6 +2159,7 @@ fn updateStage1Module(comp: *Compilation) !void {
21292159 ch.hash.add(target.os.getVersionRange());
21302160 ch.hash.add(comp.bin_file.options.dll_export_fns);
21312161 ch.hash.add(comp.bin_file.options.function_sections);
2162 ch.hash.add(comp.is_test);
21322163
21332164 if (try ch.hit()) {
21342165 const digest = ch.final();
......@@ -2155,7 +2186,7 @@ fn updateStage1Module(comp: *Compilation) !void {
21552186 .llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?,
21562187 };
21572188 var progress: std.Progress = .{};
2158 var main_progress_node = try progress.start("", 100);
2189 var main_progress_node = try progress.start("", null);
21592190 defer main_progress_node.end();
21602191 if (comp.color == .Off) progress.terminal = null;
21612192
......@@ -2184,6 +2215,8 @@ fn updateStage1Module(comp: *Compilation) !void {
21842215 .parent = null,
21852216 };
21862217 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];
21872220 stage1_module.* = .{
21882221 .root_name_ptr = comp.bin_file.options.root_name.ptr,
21892222 .root_name_len = comp.bin_file.options.root_name.len,
......@@ -2191,10 +2224,10 @@ fn updateStage1Module(comp: *Compilation) !void {
21912224 .output_dir_len = output_dir.len,
21922225 .builtin_zig_path_ptr = builtin_zig_path.ptr,
21932226 .builtin_zig_path_len = builtin_zig_path.len,
2194 .test_filter_ptr = "",
2195 .test_filter_len = 0,
2196 .test_name_prefix_ptr = "",
2197 .test_name_prefix_len = 0,
2227 .test_filter_ptr = test_filter.ptr,
2228 .test_filter_len = test_filter.len,
2229 .test_name_prefix_ptr = test_name_prefix.ptr,
2230 .test_name_prefix_len = test_name_prefix.len,
21982231 .userdata = @ptrToInt(comp),
21992232 .root_pkg = stage1_pkg,
22002233 .code_model = @enumToInt(comp.bin_file.options.machine_code_model),
......@@ -2217,7 +2250,7 @@ fn updateStage1Module(comp: *Compilation) !void {
22172250 .emit_bin = true,
22182251 .emit_asm = false,
22192252 .emit_llvm_ir = false,
2220 .test_is_evented = false,
2253 .test_is_evented = comp.test_evented_io,
22212254 .verbose_tokenize = comp.verbose_tokenize,
22222255 .verbose_ast = comp.verbose_ast,
22232256 .verbose_ir = comp.verbose_ir,
src/main.zig+618-513
......@@ -43,8 +43,10 @@ const usage =
4343 \\ env Print lib path, std path, compiler id and version
4444 \\ fmt Parse file and render in canonical zig format
4545 \\ libc Display native libc paths file or validate one
46 \\ run Create executable and run immediately
4647 \\ translate-c Convert C code to Zig code
4748 \\ targets List available compilation targets
49 \\ test Create and run a test build
4850 \\ version Print version number and exit
4951 \\ zen Print zen of zig and exit
5052 \\
......@@ -120,6 +122,10 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
120122 return buildOutputType(gpa, arena, args, .{ .build = .Lib });
121123 } else if (mem.eql(u8, cmd, "build-obj")) {
122124 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);
123129 } else if (mem.eql(u8, cmd, "cc")) {
124130 return buildOutputType(gpa, arena, args, .cc);
125131 } else if (mem.eql(u8, cmd, "c++")) {
......@@ -156,6 +162,8 @@ const usage_build_generic =
156162 \\Usage: zig build-exe <options> [files]
157163 \\ zig build-lib <options> [files]
158164 \\ zig build-obj <options> [files]
165 \\ zig test <options> [files]
166 \\ zig run <options> [file] [-- [args]]
159167 \\
160168 \\Supported file types:
161169 \\ .zig Zig source code
......@@ -233,6 +241,13 @@ const usage_build_generic =
233241 \\ -dynamic Force output to be dynamically linked
234242 \\ -static Force output to be statically linked
235243 \\
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 \\
236251 \\Debug Options (Zig Compiler Development):
237252 \\ -ftime-report Print timing diagnostics
238253 \\ --verbose-link Display linker invocations
......@@ -269,6 +284,8 @@ pub fn buildOutputType(
269284 cc,
270285 cpp,
271286 translate_c,
287 zig_test,
288 run,
272289 },
273290) !void {
274291 var color: Color = .Auto;
......@@ -321,6 +338,7 @@ pub fn buildOutputType(
321338 var linker_bind_global_refs_locally: ?bool = null;
322339 var linker_z_nodelete = false;
323340 var linker_z_defs = false;
341 var test_evented_io = false;
324342 var stack_size_override: ?u64 = null;
325343 var use_llvm: ?bool = null;
326344 var use_lld: ?bool = null;
......@@ -328,6 +346,9 @@ pub fn buildOutputType(
328346 var link_eh_frame_hdr = false;
329347 var libc_paths_file: ?[]const u8 = null;
330348 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
332353 var system_libs = std.ArrayList([]const u8).init(gpa);
333354 defer system_libs.deinit();
......@@ -356,545 +377,575 @@ pub fn buildOutputType(
356377 var frameworks = std.ArrayList([]const u8).init(gpa);
357378 defer frameworks.deinit();
358379
359 if (arg_mode == .build or arg_mode == .translate_c) {
360 output_mode = switch (arg_mode) {
361 .build => |m| m,
362 .translate_c => .Obj,
363 else => unreachable,
364 };
365 switch (arg_mode) {
366 .build => switch (output_mode) {
367 .Exe => emit_h = .no,
368 .Obj, .Lib => emit_h = .yes_default_path,
369 },
370 .translate_c => emit_h = .no,
371 else => unreachable,
372 }
373 const args = all_args[2..];
374 var i: usize = 0;
375 while (i < args.len) : (i += 1) {
376 const arg = args[i];
377 if (mem.startsWith(u8, arg, "-")) {
378 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
379 try io.getStdOut().writeAll(usage_build_generic);
380 process.exit(0);
381 } else if (mem.eql(u8, arg, "--color")) {
382 if (i + 1 >= args.len) {
383 fatal("expected [auto|on|off] after --color", .{});
380 // null means replace with the test executable binary
381 var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
382 defer test_exec_args.deinit();
383
384 switch (arg_mode) {
385 .build, .translate_c, .zig_test, .run => {
386 output_mode = switch (arg_mode) {
387 .build => |m| m,
388 .translate_c => .Obj,
389 .zig_test, .run => .Exe,
390 else => unreachable,
391 };
392 switch (arg_mode) {
393 .build => switch (output_mode) {
394 .Exe => emit_h = .no,
395 .Obj, .Lib => emit_h = .yes_default_path,
396 },
397 .translate_c, .zig_test, .run => emit_h = .no,
398 else => unreachable,
399 }
400 const args = all_args[2..];
401 var i: usize = 0;
402 while (i < args.len) : (i += 1) {
403 const arg = args[i];
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});
384638 }
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")) {
385794 i += 1;
386 const next_arg = args[i];
387 if (mem.eql(u8, next_arg, "auto")) {
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});
795 if (i >= linker_args.items.len) {
796 fatal("expected linker arg after '{}'", .{arg});
395797 }
396 } else if (mem.eql(u8, arg, "--mode")) {
397 if (i + 1 >= args.len) {
398 fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode", .{});
798 const soname = linker_args.items[i];
799 override_soname = soname;
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;
399805 }
400 i += 1;
401 const next_arg = args[i];
402 if (mem.eql(u8, next_arg, "Debug")) {
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;
806 var end: usize = soname.len;
807 if (mem.endsWith(u8, soname, ".so")) {
808 end -= 3;
410809 } 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 }
412823 }
413 } else if (mem.eql(u8, arg, "--stack")) {
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];
824 provided_name = soname[prefix..end];
423825 } else if (mem.eql(u8, arg, "-rpath")) {
424 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
425826 i += 1;
426 try rpath_list.append(args[i]);
427 } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
428 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
429 i += 1;
430 try lib_dirs.append(args[i]);
431 } else if (mem.eql(u8, arg, "-T")) {
432 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
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"))
827 if (i >= linker_args.items.len) {
828 fatal("expected linker arg after '{}'", .{arg});
829 }
830 try rpath_list.append(linker_args.items[i]);
831 } else if (mem.eql(u8, arg, "-I") or
832 mem.eql(u8, arg, "--dynamic-linker") or
833 mem.eql(u8, arg, "-dynamic-linker"))
449834 {
450 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
451835 i += 1;
452 try clang_argv.append(arg);
453 try clang_argv.append(args[i]);
454 } else if (mem.eql(u8, arg, "--version")) {
455 if (i + 1 >= args.len) {
456 fatal("expected parameter after --version", .{});
836 if (i >= linker_args.items.len) {
837 fatal("expected linker arg after '{}'", .{arg});
457838 }
458 i += 1;
459 version = std.builtin.Version.parse(args[i]) catch |err| {
460 fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
461 };
462 have_version = true;
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")) {
839 target_dynamic_linker = linker_args.items[i];
840 } else if (mem.eql(u8, arg, "-E") or
841 mem.eql(u8, arg, "--export-dynamic") or
842 mem.eql(u8, arg, "-export-dynamic"))
843 {
522844 rdynamic = true;
523 } else if (mem.eql(u8, arg, "-femit-bin")) {
524 emit_bin = .yes_default_path;
525 } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
526 emit_bin = .{ .yes = arg["-femit-bin=".len..] };
527 } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
528 emit_bin = .no;
529 } else if (mem.eql(u8, arg, "-femit-zir")) {
530 emit_zir = .yes_default_path;
531 } else if (mem.startsWith(u8, arg, "-femit-zir=")) {
532 emit_zir = .{ .yes = arg["-femit-zir=".len..] };
533 } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
534 emit_zir = .no;
535 } else if (mem.eql(u8, arg, "-femit-h")) {
536 emit_h = .yes_default_path;
537 } else if (mem.startsWith(u8, arg, "-femit-h=")) {
538 emit_h = .{ .yes = arg["-femit-h=".len..] };
539 } else if (mem.eql(u8, arg, "-fno-emit-h")) {
540 emit_h = .no;
541 } else if (mem.eql(u8, arg, "-dynamic")) {
542 link_mode = .Dynamic;
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;
845 } else if (mem.eql(u8, arg, "--version-script")) {
846 i += 1;
847 if (i >= linker_args.items.len) {
848 fatal("expected linker arg after '{}'", .{arg});
849 }
850 version_script = linker_args.items[i];
851 } else if (mem.startsWith(u8, arg, "-O")) {
852 try lld_argv.append(arg);
853 } else if (mem.eql(u8, arg, "--gc-sections")) {
854 linker_gc_sections = true;
855 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
856 linker_gc_sections = false;
857 } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
858 mem.eql(u8, arg, "-allow-shlib-undefined"))
859 {
860 linker_allow_shlib_undefined = true;
861 } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
862 mem.eql(u8, arg, "-no-allow-shlib-undefined"))
863 {
864 linker_allow_shlib_undefined = false;
557865 } else if (mem.eql(u8, arg, "-Bsymbolic")) {
558866 linker_bind_global_refs_locally = true;
559 } else if (mem.eql(u8, arg, "--verbose-link")) {
560 verbose_link = true;
561 } else if (mem.eql(u8, arg, "--verbose-cc")) {
562 verbose_cc = true;
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);
867 } else if (mem.eql(u8, arg, "-z")) {
868 i += 1;
869 if (i >= linker_args.items.len) {
870 fatal("expected linker arg after '{}'", .{arg});
709871 }
710 },
711 .sanitize => {
712 if (mem.eql(u8, it.only_arg, "undefined")) {
713 want_sanitize_c = true;
872 const z_arg = linker_args.items[i];
873 if (mem.eql(u8, z_arg, "nodelete")) {
874 linker_z_nodelete = true;
875 } else if (mem.eql(u8, z_arg, "defs")) {
876 linker_z_defs = true;
714877 } else {
715 try clang_argv.appendSlice(it.other_args);
878 warn("unsupported linker arg: -z {}", .{z_arg});
716879 }
717 },
718 .linker_script => linker_script = it.only_arg,
719 .verbose_cmds => {
720 verbose_cc = true;
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;
880 } else if (mem.eql(u8, arg, "--major-image-version")) {
881 i += 1;
882 if (i >= linker_args.items.len) {
883 fatal("expected linker arg after '{}'", .{arg});
764884 }
765 if (found_digit and end > 0 and soname[end - 1] == '.') {
766 end -= 1;
767 } else {
768 end = soname.len;
885 version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
886 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
887 };
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});
769893 }
770 if (mem.endsWith(u8, soname[prefix..end], ".so")) {
771 end -= 3;
894 version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
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});
772902 }
773 }
774 provided_name = soname[prefix..end];
775 } else if (mem.eql(u8, arg, "-rpath")) {
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;
903 stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
904 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
905 };
827906 } else {
828 warn("unsupported linker arg: -z {}", .{z_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});
907 warn("unsupported linker arg: {}", .{arg});
843908 }
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});
858909 }
859 }
860910
861 if (want_sanitize_c) |wsc| {
862 if (wsc and build_mode == .ReleaseFast) {
863 build_mode = .ReleaseSafe;
911 if (want_sanitize_c) |wsc| {
912 if (wsc and build_mode == .ReleaseFast) {
913 build_mode = .ReleaseSafe;
914 }
864915 }
865 }
866916
867 if (only_pp_or_asm) {
868 output_mode = .Obj;
869 fatal("TODO implement using zig cc as a preprocessor", .{});
870 //// Transfer "link_objects" into c_source_files so that all those
871 //// args make it onto the command line.
872 //try c_source_files.appendSlice(link_objects.items);
873 //for (c_source_files.items) |c_source_file| {
874 // const src_path = switch (emit_bin) {
875 // .yes => |p| p,
876 // else => c_source_file.source_path,
877 // };
878 // const basename = fs.path.basename(src_path);
879 // c_source_file.preprocessor_only_basename = basename;
880 //}
881 //emit_bin = .no;
882 } else if (!c_arg) {
883 output_mode = if (is_shared_lib) .Lib else .Exe;
884 switch (emit_bin) {
885 .no, .yes_default_path => {
886 emit_bin = .{ .yes = "a.out" };
887 enable_cache = true;
888 },
889 .yes => {},
917 if (only_pp_or_asm) {
918 output_mode = .Obj;
919 fatal("TODO implement using zig cc as a preprocessor", .{});
920 //// Transfer "link_objects" into c_source_files so that all those
921 //// args make it onto the command line.
922 //try c_source_files.appendSlice(link_objects.items);
923 //for (c_source_files.items) |c_source_file| {
924 // const src_path = switch (emit_bin) {
925 // .yes => |p| p,
926 // else => c_source_file.source_path,
927 // };
928 // const basename = fs.path.basename(src_path);
929 // c_source_file.preprocessor_only_basename = basename;
930 //}
931 //emit_bin = .no;
932 } else if (!c_arg) {
933 output_mode = if (is_shared_lib) .Lib else .Exe;
934 switch (emit_bin) {
935 .no, .yes_default_path => {
936 emit_bin = .{ .yes = "a.out" };
937 enable_cache = true;
938 },
939 .yes => {},
940 }
941 } else {
942 output_mode = .Obj;
890943 }
891 } else {
892 output_mode = .Obj;
893 }
894 if (c_source_files.items.len == 0 and link_objects.items.len == 0) {
895 // For example `zig cc` and no args should print the "no input files" message.
896 return punt_to_clang(arena, all_args);
897 }
944 if (c_source_files.items.len == 0 and link_objects.items.len == 0) {
945 // For example `zig cc` and no args should print the "no input files" message.
946 return punt_to_clang(arena, all_args);
947 }
948 },
898949 }
899950
900951 if (arg_mode == .translate_c and c_source_files.items.len != 1) {
......@@ -902,7 +953,9 @@ pub fn buildOutputType(
902953 }
903954
904955 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| {
906959 const basename = fs.path.basename(file);
907960 break :blk mem.split(basename, ".").next().?;
908961 } else if (c_source_files.items.len == 1) {
......@@ -916,6 +969,8 @@ pub fn buildOutputType(
916969 break :blk mem.split(basename, ".").next().?;
917970 } else if (show_builtin) {
918971 break :blk "builtin";
972 } else if (arg_mode == .run) {
973 break :blk "run";
919974 } else {
920975 fatal("--name [name] not provided and unable to infer", .{});
921976 }
......@@ -1220,6 +1275,10 @@ pub fn buildOutputType(
12201275 .machine_code_model = machine_code_model,
12211276 .color = color,
12221277 .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,
12231282 }) catch |err| {
12241283 fatal("unable to create compilation: {}", .{@errorName(err)});
12251284 };
......@@ -1243,6 +1302,52 @@ pub fn buildOutputType(
12431302 std.log.warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});
12441303 }
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
12461351 const stdin = std.io.getStdIn().inStream();
12471352 const stderr = std.io.getStdErr().outStream();
12481353 var repl_buf: [1024]u8 = undefined;