Commit ead50ea665
Changed files (3)
src/Compilation.zig
@@ -97,6 +97,10 @@ owned_link_dir: ?std.fs.Dir,
/// Don't use this for anything other than stage1 compatibility.
color: @import("main.zig").Color = .Auto,
+test_filter: ?[]const u8,
+test_name_prefix: ?[]const u8,
+test_evented_io: bool,
+
pub const InnerError = Module.InnerError;
pub const CRTFile = struct {
@@ -327,6 +331,9 @@ pub const InitOptions = struct {
machine_code_model: std.builtin.CodeModel = .default,
/// This is for stage1 and should be deleted upon completion of self-hosting.
color: @import("main.zig").Color = .Auto,
+ test_filter: ?[]const u8 = null,
+ test_name_prefix: ?[]const u8 = null,
+ test_evented_io: bool = false,
};
pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
@@ -554,6 +561,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
hash.add(single_threaded);
hash.add(options.target.os.getVersionRange());
hash.add(dll_export_fns);
+ hash.add(options.is_test);
const digest = hash.final();
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 {
.is_test = options.is_test,
.color = options.color,
.time_report = options.time_report,
+ .test_filter = options.test_filter,
+ .test_name_prefix = options.test_name_prefix,
+ .test_evented_io = options.test_evented_io,
};
break :comp comp;
};
@@ -1996,6 +2007,25 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
comp.bin_file.options.strip,
@tagName(comp.bin_file.options.machine_code_model),
});
+
+ if (comp.is_test) {
+ try buffer.appendSlice(
+ \\pub var test_functions: []TestFn = undefined; // overwritten later
+ \\
+ );
+ if (comp.test_evented_io) {
+ try buffer.appendSlice(
+ \\pub const test_io_mode = .evented;
+ \\
+ );
+ } else {
+ try buffer.appendSlice(
+ \\pub const test_io_mode = .blocking;
+ \\
+ );
+ }
+ }
+
return buffer.toOwnedSlice();
}
@@ -2129,6 +2159,7 @@ fn updateStage1Module(comp: *Compilation) !void {
ch.hash.add(target.os.getVersionRange());
ch.hash.add(comp.bin_file.options.dll_export_fns);
ch.hash.add(comp.bin_file.options.function_sections);
+ ch.hash.add(comp.is_test);
if (try ch.hit()) {
const digest = ch.final();
@@ -2155,7 +2186,7 @@ fn updateStage1Module(comp: *Compilation) !void {
.llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?,
};
var progress: std.Progress = .{};
- var main_progress_node = try progress.start("", 100);
+ var main_progress_node = try progress.start("", null);
defer main_progress_node.end();
if (comp.color == .Off) progress.terminal = null;
@@ -2184,6 +2215,8 @@ fn updateStage1Module(comp: *Compilation) !void {
.parent = null,
};
const output_dir = comp.bin_file.options.directory.path orelse ".";
+ const test_filter = comp.test_filter orelse ""[0..0];
+ const test_name_prefix = comp.test_name_prefix orelse ""[0..0];
stage1_module.* = .{
.root_name_ptr = comp.bin_file.options.root_name.ptr,
.root_name_len = comp.bin_file.options.root_name.len,
@@ -2191,10 +2224,10 @@ fn updateStage1Module(comp: *Compilation) !void {
.output_dir_len = output_dir.len,
.builtin_zig_path_ptr = builtin_zig_path.ptr,
.builtin_zig_path_len = builtin_zig_path.len,
- .test_filter_ptr = "",
- .test_filter_len = 0,
- .test_name_prefix_ptr = "",
- .test_name_prefix_len = 0,
+ .test_filter_ptr = test_filter.ptr,
+ .test_filter_len = test_filter.len,
+ .test_name_prefix_ptr = test_name_prefix.ptr,
+ .test_name_prefix_len = test_name_prefix.len,
.userdata = @ptrToInt(comp),
.root_pkg = stage1_pkg,
.code_model = @enumToInt(comp.bin_file.options.machine_code_model),
@@ -2217,7 +2250,7 @@ fn updateStage1Module(comp: *Compilation) !void {
.emit_bin = true,
.emit_asm = false,
.emit_llvm_ir = false,
- .test_is_evented = false,
+ .test_is_evented = comp.test_evented_io,
.verbose_tokenize = comp.verbose_tokenize,
.verbose_ast = comp.verbose_ast,
.verbose_ir = comp.verbose_ir,
src/main.zig
@@ -43,8 +43,10 @@ const usage =
\\ env Print lib path, std path, compiler id and version
\\ fmt Parse file and render in canonical zig format
\\ libc Display native libc paths file or validate one
+ \\ run Create executable and run immediately
\\ translate-c Convert C code to Zig code
\\ targets List available compilation targets
+ \\ test Create and run a test build
\\ version Print version number and exit
\\ zen Print zen of zig and exit
\\
@@ -120,6 +122,10 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
return buildOutputType(gpa, arena, args, .{ .build = .Lib });
} else if (mem.eql(u8, cmd, "build-obj")) {
return buildOutputType(gpa, arena, args, .{ .build = .Obj });
+ } else if (mem.eql(u8, cmd, "test")) {
+ return buildOutputType(gpa, arena, args, .zig_test);
+ } else if (mem.eql(u8, cmd, "run")) {
+ return buildOutputType(gpa, arena, args, .run);
} else if (mem.eql(u8, cmd, "cc")) {
return buildOutputType(gpa, arena, args, .cc);
} else if (mem.eql(u8, cmd, "c++")) {
@@ -156,6 +162,8 @@ const usage_build_generic =
\\Usage: zig build-exe <options> [files]
\\ zig build-lib <options> [files]
\\ zig build-obj <options> [files]
+ \\ zig test <options> [files]
+ \\ zig run <options> [file] [-- [args]]
\\
\\Supported file types:
\\ .zig Zig source code
@@ -233,6 +241,13 @@ const usage_build_generic =
\\ -dynamic Force output to be dynamically linked
\\ -static Force output to be statically linked
\\
+ \\Test Options:
+ \\ --test-filter [text] Skip tests that do not match filter
+ \\ --test-name-prefix [text] Add prefix to all tests
+ \\ --test-cmd [arg] Specify test execution command one arg at a time
+ \\ --test-cmd-bin Appends test binary path to test cmd args
+ \\ --test-evented-io Runs the test in evented I/O mode
+ \\
\\Debug Options (Zig Compiler Development):
\\ -ftime-report Print timing diagnostics
\\ --verbose-link Display linker invocations
@@ -269,6 +284,8 @@ pub fn buildOutputType(
cc,
cpp,
translate_c,
+ zig_test,
+ run,
},
) !void {
var color: Color = .Auto;
@@ -321,6 +338,7 @@ pub fn buildOutputType(
var linker_bind_global_refs_locally: ?bool = null;
var linker_z_nodelete = false;
var linker_z_defs = false;
+ var test_evented_io = false;
var stack_size_override: ?u64 = null;
var use_llvm: ?bool = null;
var use_lld: ?bool = null;
@@ -328,6 +346,9 @@ pub fn buildOutputType(
var link_eh_frame_hdr = false;
var libc_paths_file: ?[]const u8 = null;
var machine_code_model: std.builtin.CodeModel = .default;
+ var runtime_args_start: ?usize = null;
+ var test_filter: ?[]const u8 = null;
+ var test_name_prefix: ?[]const u8 = null;
var system_libs = std.ArrayList([]const u8).init(gpa);
defer system_libs.deinit();
@@ -356,545 +377,575 @@ pub fn buildOutputType(
var frameworks = std.ArrayList([]const u8).init(gpa);
defer frameworks.deinit();
- if (arg_mode == .build or arg_mode == .translate_c) {
- output_mode = switch (arg_mode) {
- .build => |m| m,
- .translate_c => .Obj,
- else => unreachable,
- };
- switch (arg_mode) {
- .build => switch (output_mode) {
- .Exe => emit_h = .no,
- .Obj, .Lib => emit_h = .yes_default_path,
- },
- .translate_c => emit_h = .no,
- else => unreachable,
- }
- const args = all_args[2..];
- var i: usize = 0;
- while (i < args.len) : (i += 1) {
- const arg = args[i];
- if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
- try io.getStdOut().writeAll(usage_build_generic);
- process.exit(0);
- } else if (mem.eql(u8, arg, "--color")) {
- if (i + 1 >= args.len) {
- fatal("expected [auto|on|off] after --color", .{});
+ // null means replace with the test executable binary
+ var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
+ defer test_exec_args.deinit();
+
+ switch (arg_mode) {
+ .build, .translate_c, .zig_test, .run => {
+ output_mode = switch (arg_mode) {
+ .build => |m| m,
+ .translate_c => .Obj,
+ .zig_test, .run => .Exe,
+ else => unreachable,
+ };
+ switch (arg_mode) {
+ .build => switch (output_mode) {
+ .Exe => emit_h = .no,
+ .Obj, .Lib => emit_h = .yes_default_path,
+ },
+ .translate_c, .zig_test, .run => emit_h = .no,
+ else => unreachable,
+ }
+ const args = all_args[2..];
+ var i: usize = 0;
+ while (i < args.len) : (i += 1) {
+ const arg = args[i];
+ if (mem.startsWith(u8, arg, "-")) {
+ if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
+ try io.getStdOut().writeAll(usage_build_generic);
+ process.exit(0);
+ } else if (mem.eql(u8, arg, "--")) {
+ if (arg_mode == .run) {
+ runtime_args_start = i + 1;
+ } else {
+ fatal("unexpected end-of-parameter mark: --", .{});
+ }
+ } else if (mem.eql(u8, arg, "--color")) {
+ if (i + 1 >= args.len) {
+ fatal("expected [auto|on|off] after --color", .{});
+ }
+ i += 1;
+ const next_arg = args[i];
+ if (mem.eql(u8, next_arg, "auto")) {
+ color = .Auto;
+ } else if (mem.eql(u8, next_arg, "on")) {
+ color = .On;
+ } else if (mem.eql(u8, next_arg, "off")) {
+ color = .Off;
+ } else {
+ fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg});
+ }
+ } else if (mem.eql(u8, arg, "--mode")) {
+ if (i + 1 >= args.len) {
+ fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode", .{});
+ }
+ i += 1;
+ const next_arg = args[i];
+ if (mem.eql(u8, next_arg, "Debug")) {
+ build_mode = .Debug;
+ } else if (mem.eql(u8, next_arg, "ReleaseSafe")) {
+ build_mode = .ReleaseSafe;
+ } else if (mem.eql(u8, next_arg, "ReleaseFast")) {
+ build_mode = .ReleaseFast;
+ } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
+ build_mode = .ReleaseSmall;
+ } else {
+ fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'", .{next_arg});
+ }
+ } else if (mem.eql(u8, arg, "--stack")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
+ } else if (mem.eql(u8, arg, "--name")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ provided_name = args[i];
+ } else if (mem.eql(u8, arg, "-rpath")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try rpath_list.append(args[i]);
+ } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try lib_dirs.append(args[i]);
+ } else if (mem.eql(u8, arg, "-T")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ linker_script = args[i];
+ } else if (mem.eql(u8, arg, "--version-script")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ version_script = args[i];
+ } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ // We don't know whether this library is part of libc or libc++ until we resolve the target.
+ // So we simply append to the list for now.
+ i += 1;
+ try system_libs.append(args[i]);
+ } else if (mem.eql(u8, arg, "-D") or
+ mem.eql(u8, arg, "-isystem") or
+ mem.eql(u8, arg, "-I") or
+ mem.eql(u8, arg, "-dirafter"))
+ {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try clang_argv.append(arg);
+ try clang_argv.append(args[i]);
+ } else if (mem.eql(u8, arg, "--version")) {
+ if (i + 1 >= args.len) {
+ fatal("expected parameter after --version", .{});
+ }
+ i += 1;
+ version = std.builtin.Version.parse(args[i]) catch |err| {
+ fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
+ };
+ have_version = true;
+ } else if (mem.eql(u8, arg, "-target")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ target_arch_os_abi = args[i];
+ } else if (mem.eql(u8, arg, "-mcpu")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ target_mcpu = args[i];
+ } else if (mem.eql(u8, arg, "-mcmodel")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ machine_code_model = parseCodeModel(args[i]);
+ } else if (mem.startsWith(u8, arg, "-ofmt=")) {
+ target_ofmt = arg["-ofmt=".len..];
+ } else if (mem.startsWith(u8, arg, "-mcpu=")) {
+ target_mcpu = arg["-mcpu=".len..];
+ } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
+ machine_code_model = parseCodeModel(arg["-mcmodel=".len..]);
+ } else if (mem.eql(u8, arg, "--dynamic-linker")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ target_dynamic_linker = args[i];
+ } else if (mem.eql(u8, arg, "--libc")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ libc_paths_file = args[i];
+ } else if (mem.eql(u8, arg, "--test-filter")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ test_filter = args[i];
+ } else if (mem.eql(u8, arg, "--test-name-prefix")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ test_name_prefix = args[i];
+ } else if (mem.eql(u8, arg, "--test-cmd")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ try test_exec_args.append(args[i]);
+ } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
+ try test_exec_args.append(null);
+ } else if (mem.eql(u8, arg, "--test-evented-io")) {
+ test_evented_io = true;
+ } else if (mem.eql(u8, arg, "--watch")) {
+ watch = true;
+ } else if (mem.eql(u8, arg, "-ftime-report")) {
+ time_report = true;
+ } else if (mem.eql(u8, arg, "-fPIC")) {
+ want_pic = true;
+ } else if (mem.eql(u8, arg, "-fno-PIC")) {
+ want_pic = false;
+ } else if (mem.eql(u8, arg, "-fstack-check")) {
+ want_stack_check = true;
+ } else if (mem.eql(u8, arg, "-fno-stack-check")) {
+ want_stack_check = false;
+ } else if (mem.eql(u8, arg, "-fsanitize-c")) {
+ want_sanitize_c = true;
+ } else if (mem.eql(u8, arg, "-fno-sanitize-c")) {
+ want_sanitize_c = false;
+ } else if (mem.eql(u8, arg, "-fvalgrind")) {
+ want_valgrind = true;
+ } else if (mem.eql(u8, arg, "-fno-valgrind")) {
+ want_valgrind = false;
+ } else if (mem.eql(u8, arg, "-fLLVM")) {
+ use_llvm = true;
+ } else if (mem.eql(u8, arg, "-fno-LLVM")) {
+ use_llvm = false;
+ } else if (mem.eql(u8, arg, "-fLLD")) {
+ use_lld = true;
+ } else if (mem.eql(u8, arg, "-fno-LLD")) {
+ use_lld = false;
+ } else if (mem.eql(u8, arg, "-fClang")) {
+ use_clang = true;
+ } else if (mem.eql(u8, arg, "-fno-Clang")) {
+ use_clang = false;
+ } else if (mem.eql(u8, arg, "-rdynamic")) {
+ rdynamic = true;
+ } else if (mem.eql(u8, arg, "-femit-bin")) {
+ emit_bin = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
+ emit_bin = .{ .yes = arg["-femit-bin=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
+ emit_bin = .no;
+ } else if (mem.eql(u8, arg, "-femit-zir")) {
+ emit_zir = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-zir=")) {
+ emit_zir = .{ .yes = arg["-femit-zir=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
+ emit_zir = .no;
+ } else if (mem.eql(u8, arg, "-femit-h")) {
+ emit_h = .yes_default_path;
+ } else if (mem.startsWith(u8, arg, "-femit-h=")) {
+ emit_h = .{ .yes = arg["-femit-h=".len..] };
+ } else if (mem.eql(u8, arg, "-fno-emit-h")) {
+ emit_h = .no;
+ } else if (mem.eql(u8, arg, "-dynamic")) {
+ link_mode = .Dynamic;
+ } else if (mem.eql(u8, arg, "-static")) {
+ link_mode = .Static;
+ } else if (mem.eql(u8, arg, "-fdll-export-fns")) {
+ dll_export_fns = true;
+ } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
+ dll_export_fns = false;
+ } else if (mem.eql(u8, arg, "--show-builtin")) {
+ show_builtin = true;
+ } else if (mem.eql(u8, arg, "--strip")) {
+ strip = true;
+ } else if (mem.eql(u8, arg, "--single-threaded")) {
+ single_threaded = true;
+ } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
+ link_eh_frame_hdr = true;
+ } else if (mem.eql(u8, arg, "-Bsymbolic")) {
+ linker_bind_global_refs_locally = true;
+ } else if (mem.eql(u8, arg, "--verbose-link")) {
+ verbose_link = true;
+ } else if (mem.eql(u8, arg, "--verbose-cc")) {
+ verbose_cc = true;
+ } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
+ verbose_tokenize = true;
+ } else if (mem.eql(u8, arg, "--verbose-ast")) {
+ verbose_ast = true;
+ } else if (mem.eql(u8, arg, "--verbose-ir")) {
+ verbose_ir = true;
+ } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
+ verbose_llvm_ir = true;
+ } else if (mem.eql(u8, arg, "--verbose-cimport")) {
+ verbose_cimport = true;
+ } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
+ verbose_llvm_cpu_features = true;
+ } else if (mem.startsWith(u8, arg, "-T")) {
+ linker_script = arg[2..];
+ } else if (mem.startsWith(u8, arg, "-L")) {
+ try lib_dirs.append(arg[2..]);
+ } else if (mem.startsWith(u8, arg, "-l")) {
+ // We don't know whether this library is part of libc or libc++ until we resolve the target.
+ // So we simply append to the list for now.
+ try system_libs.append(arg[2..]);
+ } else if (mem.startsWith(u8, arg, "-D") or
+ mem.startsWith(u8, arg, "-I"))
+ {
+ try clang_argv.append(arg);
+ } else {
+ fatal("unrecognized parameter: '{}'", .{arg});
}
+ } else switch (Compilation.classifyFileExt(arg)) {
+ .object, .static_library => {
+ try link_objects.append(arg);
+ },
+ .assembly, .c, .cpp, .h, .ll, .bc => {
+ // TODO a way to pass extra flags on the CLI
+ try c_source_files.append(.{ .src_path = arg });
+ },
+ .shared_library => {
+ fatal("linking against dynamic libraries not yet supported", .{});
+ },
+ .zig, .zir => {
+ if (root_src_file) |other| {
+ fatal("found another zig file '{}' after root source file '{}'", .{ arg, other });
+ } else {
+ root_src_file = arg;
+ }
+ },
+ .unknown => {
+ fatal("unrecognized file extension of parameter '{}'", .{arg});
+ },
+ }
+ }
+ },
+ .cc, .cpp => {
+ emit_h = .no;
+ strip = true;
+ ensure_libc_on_non_freestanding = true;
+ ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
+ want_native_include_dirs = true;
+
+ var c_arg = false;
+ var is_shared_lib = false;
+ var linker_args = std.ArrayList([]const u8).init(arena);
+ var it = ClangArgIterator.init(arena, all_args);
+ while (it.has_next) {
+ it.next() catch |err| {
+ fatal("unable to parse command line parameters: {}", .{@errorName(err)});
+ };
+ switch (it.zig_equivalent) {
+ .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown
+ .o => {
+ // -o
+ emit_bin = .{ .yes = it.only_arg };
+ enable_cache = true;
+ },
+ .c => c_arg = true, // -c
+ .other => {
+ try clang_argv.appendSlice(it.other_args);
+ },
+ .positional => {
+ const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg));
+ switch (file_ext) {
+ .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }),
+ .unknown, .shared_library, .object, .static_library => {
+ try link_objects.append(it.only_arg);
+ },
+ .zig, .zir => {
+ if (root_src_file) |other| {
+ fatal("found another zig file '{}' after root source file '{}'", .{ it.only_arg, other });
+ } else {
+ root_src_file = it.only_arg;
+ }
+ },
+ }
+ },
+ .l => {
+ // -l
+ // We don't know whether this library is part of libc or libc++ until we resolve the target.
+ // So we simply append to the list for now.
+ try system_libs.append(it.only_arg);
+ },
+ .ignore => {},
+ .driver_punt => {
+ // Never mind what we're doing, just pass the args directly. For example --help.
+ return punt_to_clang(arena, all_args);
+ },
+ .pic => want_pic = true,
+ .no_pic => want_pic = false,
+ .nostdlib => ensure_libc_on_non_freestanding = false,
+ .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
+ .shared => {
+ link_mode = .Dynamic;
+ is_shared_lib = true;
+ },
+ .rdynamic => rdynamic = true,
+ .wl => {
+ var split_it = mem.split(it.only_arg, ",");
+ while (split_it.next()) |linker_arg| {
+ try linker_args.append(linker_arg);
+ }
+ },
+ .pp_or_asm => {
+ // This handles both -E and -S.
+ only_pp_or_asm = true;
+ try clang_argv.appendSlice(it.other_args);
+ },
+ .optimize => {
+ // Alright, what release mode do they want?
+ if (mem.eql(u8, it.only_arg, "Os")) {
+ build_mode = .ReleaseSmall;
+ } else if (mem.eql(u8, it.only_arg, "O2") or
+ mem.eql(u8, it.only_arg, "O3") or
+ mem.eql(u8, it.only_arg, "O4"))
+ {
+ build_mode = .ReleaseFast;
+ } else if (mem.eql(u8, it.only_arg, "Og") or
+ mem.eql(u8, it.only_arg, "O0"))
+ {
+ build_mode = .Debug;
+ } else {
+ try clang_argv.appendSlice(it.other_args);
+ }
+ },
+ .debug => {
+ strip = false;
+ if (mem.eql(u8, it.only_arg, "-g")) {
+ // We handled with strip = false above.
+ } else {
+ try clang_argv.appendSlice(it.other_args);
+ }
+ },
+ .sanitize => {
+ if (mem.eql(u8, it.only_arg, "undefined")) {
+ want_sanitize_c = true;
+ } else {
+ try clang_argv.appendSlice(it.other_args);
+ }
+ },
+ .linker_script => linker_script = it.only_arg,
+ .verbose_cmds => {
+ verbose_cc = true;
+ verbose_link = true;
+ },
+ .for_linker => try linker_args.append(it.only_arg),
+ .linker_input_z => {
+ try linker_args.append("-z");
+ try linker_args.append(it.only_arg);
+ },
+ .lib_dir => try lib_dirs.append(it.only_arg),
+ .mcpu => target_mcpu = it.only_arg,
+ .dep_file => {
+ disable_c_depfile = true;
+ try clang_argv.appendSlice(it.other_args);
+ },
+ .framework_dir => try framework_dirs.append(it.only_arg),
+ .framework => try frameworks.append(it.only_arg),
+ .nostdlibinc => want_native_include_dirs = false,
+ }
+ }
+ // Parse linker args.
+ var i: usize = 0;
+ while (i < linker_args.items.len) : (i += 1) {
+ const arg = linker_args.items[i];
+ if (mem.eql(u8, arg, "-soname")) {
i += 1;
- const next_arg = args[i];
- if (mem.eql(u8, next_arg, "auto")) {
- color = .Auto;
- } else if (mem.eql(u8, next_arg, "on")) {
- color = .On;
- } else if (mem.eql(u8, next_arg, "off")) {
- color = .Off;
- } else {
- fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg});
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
}
- } else if (mem.eql(u8, arg, "--mode")) {
- if (i + 1 >= args.len) {
- fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode", .{});
+ const soname = linker_args.items[i];
+ override_soname = soname;
+ // Use it as --name.
+ // Example: libsoundio.so.2
+ var prefix: usize = 0;
+ if (mem.startsWith(u8, soname, "lib")) {
+ prefix = 3;
}
- i += 1;
- const next_arg = args[i];
- if (mem.eql(u8, next_arg, "Debug")) {
- build_mode = .Debug;
- } else if (mem.eql(u8, next_arg, "ReleaseSafe")) {
- build_mode = .ReleaseSafe;
- } else if (mem.eql(u8, next_arg, "ReleaseFast")) {
- build_mode = .ReleaseFast;
- } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
- build_mode = .ReleaseSmall;
+ var end: usize = soname.len;
+ if (mem.endsWith(u8, soname, ".so")) {
+ end -= 3;
} else {
- fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'", .{next_arg});
+ var found_digit = false;
+ while (end > 0 and std.ascii.isDigit(soname[end - 1])) {
+ found_digit = true;
+ end -= 1;
+ }
+ if (found_digit and end > 0 and soname[end - 1] == '.') {
+ end -= 1;
+ } else {
+ end = soname.len;
+ }
+ if (mem.endsWith(u8, soname[prefix..end], ".so")) {
+ end -= 3;
+ }
}
- } else if (mem.eql(u8, arg, "--stack")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {
- fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
- };
- } else if (mem.eql(u8, arg, "--name")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- provided_name = args[i];
+ provided_name = soname[prefix..end];
} else if (mem.eql(u8, arg, "-rpath")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
i += 1;
- try rpath_list.append(args[i]);
- } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- try lib_dirs.append(args[i]);
- } else if (mem.eql(u8, arg, "-T")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- linker_script = args[i];
- } else if (mem.eql(u8, arg, "--version-script")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- version_script = args[i];
- } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- // We don't know whether this library is part of libc or libc++ until we resolve the target.
- // So we simply append to the list for now.
- i += 1;
- try system_libs.append(args[i]);
- } else if (mem.eql(u8, arg, "-D") or
- mem.eql(u8, arg, "-isystem") or
- mem.eql(u8, arg, "-I") or
- mem.eql(u8, arg, "-dirafter"))
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ try rpath_list.append(linker_args.items[i]);
+ } else if (mem.eql(u8, arg, "-I") or
+ mem.eql(u8, arg, "--dynamic-linker") or
+ mem.eql(u8, arg, "-dynamic-linker"))
{
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
i += 1;
- try clang_argv.append(arg);
- try clang_argv.append(args[i]);
- } else if (mem.eql(u8, arg, "--version")) {
- if (i + 1 >= args.len) {
- fatal("expected parameter after --version", .{});
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
}
- i += 1;
- version = std.builtin.Version.parse(args[i]) catch |err| {
- fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
- };
- have_version = true;
- } else if (mem.eql(u8, arg, "-target")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- target_arch_os_abi = args[i];
- } else if (mem.eql(u8, arg, "-mcpu")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- target_mcpu = args[i];
- } else if (mem.eql(u8, arg, "-mcmodel")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- machine_code_model = parseCodeModel(args[i]);
- } else if (mem.startsWith(u8, arg, "-ofmt=")) {
- target_ofmt = arg["-ofmt=".len..];
- } else if (mem.startsWith(u8, arg, "-mcpu=")) {
- target_mcpu = arg["-mcpu=".len..];
- } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
- machine_code_model = parseCodeModel(arg["-mcmodel=".len..]);
- } else if (mem.eql(u8, arg, "--dynamic-linker")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- target_dynamic_linker = args[i];
- } else if (mem.eql(u8, arg, "--libc")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- libc_paths_file = args[i];
- } else if (mem.eql(u8, arg, "--watch")) {
- watch = true;
- } else if (mem.eql(u8, arg, "-ftime-report")) {
- time_report = true;
- } else if (mem.eql(u8, arg, "-fPIC")) {
- want_pic = true;
- } else if (mem.eql(u8, arg, "-fno-PIC")) {
- want_pic = false;
- } else if (mem.eql(u8, arg, "-fstack-check")) {
- want_stack_check = true;
- } else if (mem.eql(u8, arg, "-fno-stack-check")) {
- want_stack_check = false;
- } else if (mem.eql(u8, arg, "-fsanitize-c")) {
- want_sanitize_c = true;
- } else if (mem.eql(u8, arg, "-fno-sanitize-c")) {
- want_sanitize_c = false;
- } else if (mem.eql(u8, arg, "-fvalgrind")) {
- want_valgrind = true;
- } else if (mem.eql(u8, arg, "-fno-valgrind")) {
- want_valgrind = false;
- } else if (mem.eql(u8, arg, "-fLLVM")) {
- use_llvm = true;
- } else if (mem.eql(u8, arg, "-fno-LLVM")) {
- use_llvm = false;
- } else if (mem.eql(u8, arg, "-fLLD")) {
- use_lld = true;
- } else if (mem.eql(u8, arg, "-fno-LLD")) {
- use_lld = false;
- } else if (mem.eql(u8, arg, "-fClang")) {
- use_clang = true;
- } else if (mem.eql(u8, arg, "-fno-Clang")) {
- use_clang = false;
- } else if (mem.eql(u8, arg, "-rdynamic")) {
+ target_dynamic_linker = linker_args.items[i];
+ } else if (mem.eql(u8, arg, "-E") or
+ mem.eql(u8, arg, "--export-dynamic") or
+ mem.eql(u8, arg, "-export-dynamic"))
+ {
rdynamic = true;
- } else if (mem.eql(u8, arg, "-femit-bin")) {
- emit_bin = .yes_default_path;
- } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
- emit_bin = .{ .yes = arg["-femit-bin=".len..] };
- } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
- emit_bin = .no;
- } else if (mem.eql(u8, arg, "-femit-zir")) {
- emit_zir = .yes_default_path;
- } else if (mem.startsWith(u8, arg, "-femit-zir=")) {
- emit_zir = .{ .yes = arg["-femit-zir=".len..] };
- } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
- emit_zir = .no;
- } else if (mem.eql(u8, arg, "-femit-h")) {
- emit_h = .yes_default_path;
- } else if (mem.startsWith(u8, arg, "-femit-h=")) {
- emit_h = .{ .yes = arg["-femit-h=".len..] };
- } else if (mem.eql(u8, arg, "-fno-emit-h")) {
- emit_h = .no;
- } else if (mem.eql(u8, arg, "-dynamic")) {
- link_mode = .Dynamic;
- } else if (mem.eql(u8, arg, "-static")) {
- link_mode = .Static;
- } else if (mem.eql(u8, arg, "-fdll-export-fns")) {
- dll_export_fns = true;
- } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
- dll_export_fns = false;
- } else if (mem.eql(u8, arg, "--show-builtin")) {
- show_builtin = true;
- } else if (mem.eql(u8, arg, "--strip")) {
- strip = true;
- } else if (mem.eql(u8, arg, "--single-threaded")) {
- single_threaded = true;
- } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
- link_eh_frame_hdr = true;
+ } else if (mem.eql(u8, arg, "--version-script")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ version_script = linker_args.items[i];
+ } else if (mem.startsWith(u8, arg, "-O")) {
+ try lld_argv.append(arg);
+ } else if (mem.eql(u8, arg, "--gc-sections")) {
+ linker_gc_sections = true;
+ } else if (mem.eql(u8, arg, "--no-gc-sections")) {
+ linker_gc_sections = false;
+ } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
+ mem.eql(u8, arg, "-allow-shlib-undefined"))
+ {
+ linker_allow_shlib_undefined = true;
+ } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
+ mem.eql(u8, arg, "-no-allow-shlib-undefined"))
+ {
+ linker_allow_shlib_undefined = false;
} else if (mem.eql(u8, arg, "-Bsymbolic")) {
linker_bind_global_refs_locally = true;
- } else if (mem.eql(u8, arg, "--verbose-link")) {
- verbose_link = true;
- } else if (mem.eql(u8, arg, "--verbose-cc")) {
- verbose_cc = true;
- } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
- verbose_tokenize = true;
- } else if (mem.eql(u8, arg, "--verbose-ast")) {
- verbose_ast = true;
- } else if (mem.eql(u8, arg, "--verbose-ir")) {
- verbose_ir = true;
- } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
- verbose_llvm_ir = true;
- } else if (mem.eql(u8, arg, "--verbose-cimport")) {
- verbose_cimport = true;
- } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
- verbose_llvm_cpu_features = true;
- } else if (mem.startsWith(u8, arg, "-T")) {
- linker_script = arg[2..];
- } else if (mem.startsWith(u8, arg, "-L")) {
- try lib_dirs.append(arg[2..]);
- } else if (mem.startsWith(u8, arg, "-l")) {
- // We don't know whether this library is part of libc or libc++ until we resolve the target.
- // So we simply append to the list for now.
- try system_libs.append(arg[2..]);
- } else if (mem.startsWith(u8, arg, "-D") or
- mem.startsWith(u8, arg, "-I"))
- {
- try clang_argv.append(arg);
- } else {
- fatal("unrecognized parameter: '{}'", .{arg});
- }
- } else switch (Compilation.classifyFileExt(arg)) {
- .object, .static_library => {
- try link_objects.append(arg);
- },
- .assembly, .c, .cpp, .h, .ll, .bc => {
- // TODO a way to pass extra flags on the CLI
- try c_source_files.append(.{ .src_path = arg });
- },
- .shared_library => {
- fatal("linking against dynamic libraries not yet supported", .{});
- },
- .zig, .zir => {
- if (root_src_file) |other| {
- fatal("found another zig file '{}' after root source file '{}'", .{ arg, other });
- } else {
- root_src_file = arg;
- }
- },
- .unknown => {
- fatal("unrecognized file extension of parameter '{}'", .{arg});
- },
- }
- }
- } else {
- emit_h = .no;
- strip = true;
- ensure_libc_on_non_freestanding = true;
- ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
- want_native_include_dirs = true;
-
- var c_arg = false;
- var is_shared_lib = false;
- var linker_args = std.ArrayList([]const u8).init(arena);
- var it = ClangArgIterator.init(arena, all_args);
- while (it.has_next) {
- it.next() catch |err| {
- fatal("unable to parse command line parameters: {}", .{@errorName(err)});
- };
- switch (it.zig_equivalent) {
- .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown
- .o => {
- // -o
- emit_bin = .{ .yes = it.only_arg };
- enable_cache = true;
- },
- .c => c_arg = true, // -c
- .other => {
- try clang_argv.appendSlice(it.other_args);
- },
- .positional => {
- const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg));
- switch (file_ext) {
- .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }),
- .unknown, .shared_library, .object, .static_library => {
- try link_objects.append(it.only_arg);
- },
- .zig, .zir => {
- if (root_src_file) |other| {
- fatal("found another zig file '{}' after root source file '{}'", .{ it.only_arg, other });
- } else {
- root_src_file = it.only_arg;
- }
- },
- }
- },
- .l => {
- // -l
- // We don't know whether this library is part of libc or libc++ until we resolve the target.
- // So we simply append to the list for now.
- try system_libs.append(it.only_arg);
- },
- .ignore => {},
- .driver_punt => {
- // Never mind what we're doing, just pass the args directly. For example --help.
- return punt_to_clang(arena, all_args);
- },
- .pic => want_pic = true,
- .no_pic => want_pic = false,
- .nostdlib => ensure_libc_on_non_freestanding = false,
- .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
- .shared => {
- link_mode = .Dynamic;
- is_shared_lib = true;
- },
- .rdynamic => rdynamic = true,
- .wl => {
- var split_it = mem.split(it.only_arg, ",");
- while (split_it.next()) |linker_arg| {
- try linker_args.append(linker_arg);
- }
- },
- .pp_or_asm => {
- // This handles both -E and -S.
- only_pp_or_asm = true;
- try clang_argv.appendSlice(it.other_args);
- },
- .optimize => {
- // Alright, what release mode do they want?
- if (mem.eql(u8, it.only_arg, "Os")) {
- build_mode = .ReleaseSmall;
- } else if (mem.eql(u8, it.only_arg, "O2") or
- mem.eql(u8, it.only_arg, "O3") or
- mem.eql(u8, it.only_arg, "O4"))
- {
- build_mode = .ReleaseFast;
- } else if (mem.eql(u8, it.only_arg, "Og") or
- mem.eql(u8, it.only_arg, "O0"))
- {
- build_mode = .Debug;
- } else {
- try clang_argv.appendSlice(it.other_args);
- }
- },
- .debug => {
- strip = false;
- if (mem.eql(u8, it.only_arg, "-g")) {
- // We handled with strip = false above.
- } else {
- try clang_argv.appendSlice(it.other_args);
+ } else if (mem.eql(u8, arg, "-z")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
}
- },
- .sanitize => {
- if (mem.eql(u8, it.only_arg, "undefined")) {
- want_sanitize_c = true;
+ const z_arg = linker_args.items[i];
+ if (mem.eql(u8, z_arg, "nodelete")) {
+ linker_z_nodelete = true;
+ } else if (mem.eql(u8, z_arg, "defs")) {
+ linker_z_defs = true;
} else {
- try clang_argv.appendSlice(it.other_args);
+ warn("unsupported linker arg: -z {}", .{z_arg});
}
- },
- .linker_script => linker_script = it.only_arg,
- .verbose_cmds => {
- verbose_cc = true;
- verbose_link = true;
- },
- .for_linker => try linker_args.append(it.only_arg),
- .linker_input_z => {
- try linker_args.append("-z");
- try linker_args.append(it.only_arg);
- },
- .lib_dir => try lib_dirs.append(it.only_arg),
- .mcpu => target_mcpu = it.only_arg,
- .dep_file => {
- disable_c_depfile = true;
- try clang_argv.appendSlice(it.other_args);
- },
- .framework_dir => try framework_dirs.append(it.only_arg),
- .framework => try frameworks.append(it.only_arg),
- .nostdlibinc => want_native_include_dirs = false,
- }
- }
- // Parse linker args.
- var i: usize = 0;
- while (i < linker_args.items.len) : (i += 1) {
- const arg = linker_args.items[i];
- if (mem.eql(u8, arg, "-soname")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- const soname = linker_args.items[i];
- override_soname = soname;
- // Use it as --name.
- // Example: libsoundio.so.2
- var prefix: usize = 0;
- if (mem.startsWith(u8, soname, "lib")) {
- prefix = 3;
- }
- var end: usize = soname.len;
- if (mem.endsWith(u8, soname, ".so")) {
- end -= 3;
- } else {
- var found_digit = false;
- while (end > 0 and std.ascii.isDigit(soname[end - 1])) {
- found_digit = true;
- end -= 1;
+ } else if (mem.eql(u8, arg, "--major-image-version")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
}
- if (found_digit and end > 0 and soname[end - 1] == '.') {
- end -= 1;
- } else {
- end = soname.len;
+ version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
+ have_version = true;
+ } else if (mem.eql(u8, arg, "--minor-image-version")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
}
- if (mem.endsWith(u8, soname[prefix..end], ".so")) {
- end -= 3;
+ version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
+ have_version = true;
+ } else if (mem.eql(u8, arg, "--stack")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
}
- }
- provided_name = soname[prefix..end];
- } else if (mem.eql(u8, arg, "-rpath")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- try rpath_list.append(linker_args.items[i]);
- } else if (mem.eql(u8, arg, "-I") or
- mem.eql(u8, arg, "--dynamic-linker") or
- mem.eql(u8, arg, "-dynamic-linker"))
- {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- target_dynamic_linker = linker_args.items[i];
- } else if (mem.eql(u8, arg, "-E") or
- mem.eql(u8, arg, "--export-dynamic") or
- mem.eql(u8, arg, "-export-dynamic"))
- {
- rdynamic = true;
- } else if (mem.eql(u8, arg, "--version-script")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- version_script = linker_args.items[i];
- } else if (mem.startsWith(u8, arg, "-O")) {
- try lld_argv.append(arg);
- } else if (mem.eql(u8, arg, "--gc-sections")) {
- linker_gc_sections = true;
- } else if (mem.eql(u8, arg, "--no-gc-sections")) {
- linker_gc_sections = false;
- } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
- mem.eql(u8, arg, "-allow-shlib-undefined"))
- {
- linker_allow_shlib_undefined = true;
- } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
- mem.eql(u8, arg, "-no-allow-shlib-undefined"))
- {
- linker_allow_shlib_undefined = false;
- } else if (mem.eql(u8, arg, "-Bsymbolic")) {
- linker_bind_global_refs_locally = true;
- } else if (mem.eql(u8, arg, "-z")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- const z_arg = linker_args.items[i];
- if (mem.eql(u8, z_arg, "nodelete")) {
- linker_z_nodelete = true;
- } else if (mem.eql(u8, z_arg, "defs")) {
- linker_z_defs = true;
+ stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
+ fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
+ };
} else {
- warn("unsupported linker arg: -z {}", .{z_arg});
- }
- } else if (mem.eql(u8, arg, "--major-image-version")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
- fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
- };
- have_version = true;
- } else if (mem.eql(u8, arg, "--minor-image-version")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
+ warn("unsupported linker arg: {}", .{arg});
}
- version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
- fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
- };
- have_version = true;
- } else if (mem.eql(u8, arg, "--stack")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
- fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
- };
- } else {
- warn("unsupported linker arg: {}", .{arg});
}
- }
- if (want_sanitize_c) |wsc| {
- if (wsc and build_mode == .ReleaseFast) {
- build_mode = .ReleaseSafe;
+ if (want_sanitize_c) |wsc| {
+ if (wsc and build_mode == .ReleaseFast) {
+ build_mode = .ReleaseSafe;
+ }
}
- }
- if (only_pp_or_asm) {
- output_mode = .Obj;
- fatal("TODO implement using zig cc as a preprocessor", .{});
- //// Transfer "link_objects" into c_source_files so that all those
- //// args make it onto the command line.
- //try c_source_files.appendSlice(link_objects.items);
- //for (c_source_files.items) |c_source_file| {
- // const src_path = switch (emit_bin) {
- // .yes => |p| p,
- // else => c_source_file.source_path,
- // };
- // const basename = fs.path.basename(src_path);
- // c_source_file.preprocessor_only_basename = basename;
- //}
- //emit_bin = .no;
- } else if (!c_arg) {
- output_mode = if (is_shared_lib) .Lib else .Exe;
- switch (emit_bin) {
- .no, .yes_default_path => {
- emit_bin = .{ .yes = "a.out" };
- enable_cache = true;
- },
- .yes => {},
+ if (only_pp_or_asm) {
+ output_mode = .Obj;
+ fatal("TODO implement using zig cc as a preprocessor", .{});
+ //// Transfer "link_objects" into c_source_files so that all those
+ //// args make it onto the command line.
+ //try c_source_files.appendSlice(link_objects.items);
+ //for (c_source_files.items) |c_source_file| {
+ // const src_path = switch (emit_bin) {
+ // .yes => |p| p,
+ // else => c_source_file.source_path,
+ // };
+ // const basename = fs.path.basename(src_path);
+ // c_source_file.preprocessor_only_basename = basename;
+ //}
+ //emit_bin = .no;
+ } else if (!c_arg) {
+ output_mode = if (is_shared_lib) .Lib else .Exe;
+ switch (emit_bin) {
+ .no, .yes_default_path => {
+ emit_bin = .{ .yes = "a.out" };
+ enable_cache = true;
+ },
+ .yes => {},
+ }
+ } else {
+ output_mode = .Obj;
}
- } else {
- output_mode = .Obj;
- }
- if (c_source_files.items.len == 0 and link_objects.items.len == 0) {
- // For example `zig cc` and no args should print the "no input files" message.
- return punt_to_clang(arena, all_args);
- }
+ if (c_source_files.items.len == 0 and link_objects.items.len == 0) {
+ // For example `zig cc` and no args should print the "no input files" message.
+ return punt_to_clang(arena, all_args);
+ }
+ },
}
if (arg_mode == .translate_c and c_source_files.items.len != 1) {
@@ -902,7 +953,9 @@ pub fn buildOutputType(
}
const root_name = if (provided_name) |n| n else blk: {
- if (root_src_file) |file| {
+ if (arg_mode == .zig_test) {
+ break :blk "test";
+ } else if (root_src_file) |file| {
const basename = fs.path.basename(file);
break :blk mem.split(basename, ".").next().?;
} else if (c_source_files.items.len == 1) {
@@ -916,6 +969,8 @@ pub fn buildOutputType(
break :blk mem.split(basename, ".").next().?;
} else if (show_builtin) {
break :blk "builtin";
+ } else if (arg_mode == .run) {
+ break :blk "run";
} else {
fatal("--name [name] not provided and unable to infer", .{});
}
@@ -1220,6 +1275,10 @@ pub fn buildOutputType(
.machine_code_model = machine_code_model,
.color = color,
.time_report = time_report,
+ .is_test = arg_mode == .zig_test,
+ .test_evented_io = test_evented_io,
+ .test_filter = test_filter,
+ .test_name_prefix = test_name_prefix,
}) catch |err| {
fatal("unable to create compilation: {}", .{@errorName(err)});
};
@@ -1243,6 +1302,52 @@ pub fn buildOutputType(
std.log.warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});
}
+ switch (arg_mode) {
+ .run, .zig_test => run: {
+ const exe_loc = emit_bin_loc orelse break :run;
+ const exe_directory = exe_loc.directory orelse comp.bin_file.options.directory;
+ const exe_path = try fs.path.join(arena, &[_][]const u8{
+ exe_directory.path orelse ".", exe_loc.basename,
+ });
+
+ var argv = std.ArrayList([]const u8).init(gpa);
+ defer argv.deinit();
+
+ if (test_exec_args.items.len == 0) {
+ try argv.append(exe_path);
+ } else {
+ for (test_exec_args.items) |arg| {
+ try argv.append(arg orelse exe_path);
+ }
+ }
+ if (runtime_args_start) |i| {
+ try argv.appendSlice(all_args[i..]);
+ }
+ // TODO On operating systems that support it, do an execve here rather than child process,
+ // when watch=false.
+ const child = try std.ChildProcess.init(argv.items, gpa);
+ defer child.deinit();
+
+ child.stdin_behavior = .Inherit;
+ child.stdout_behavior = .Inherit;
+ child.stderr_behavior = .Inherit;
+
+ const term = try child.spawnAndWait();
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO https://github.com/ziglang/zig/issues/6342
+ process.exit(1);
+ }
+ },
+ else => process.exit(1),
+ }
+ if (!watch)
+ process.exit(0);
+ },
+ else => {},
+ }
+
const stdin = std.io.getStdIn().inStream();
const stderr = std.io.getStdErr().outStream();
var repl_buf: [1024]u8 = undefined;
BRANCH_TODO
@@ -1,6 +1,9 @@
* build & link against libcxx and libcxxabi
- * `zig test`
* `zig build`
+ * repair @cImport
+ * make sure zig cc works
+ - using it as a preprocessor (-E)
+ - try building some software
* `-ftime-report`
* -fstack-report print stack size diagnostics\n"
* -fdump-analysis write analysis.json file with type information\n"
@@ -11,17 +14,14 @@
* -femit-llvm-ir produce a .ll file with LLVM IR\n"
* -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n"
* --cache-dir [path] override the local cache directory\n"
- * make sure zig cc works
- - using it as a preprocessor (-E)
- - try building some software
* implement proper parsing of LLD stderr/stdout and exposing compile errors
* implement proper parsing of clang stderr/stdout and exposing compile errors
* support rpaths in ELF linker code
- * repair @cImport
* add CLI support for a way to pass extra flags to c source files
* musl
* mingw-w64
* use global zig-cache dir for crt files
+ * use global zig-cache dir for `zig run` executables but not `zig test`
* MachO LLD linking
* COFF LLD linking
* WASM LLD linking
@@ -30,9 +30,9 @@
* audit the CLI options for stage2
* `zig init-lib`
* `zig init-exe`
- * `zig run`
* restore error messages for stage2_add_link_lib
* audit the base cache hash
+ * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process.
* implement proper compile errors for failing to build glibc crt files and shared libs
* implement -fno-emit-bin
@@ -66,3 +66,4 @@
* some kind of "zig identifier escape" function rather than unconditionally using @"" syntax
in builtin.zig
* rename std.builtin.Mode to std.builtin.OptimizeMode
+ * implement `zig run` and `zig test` when combined with `--watch`