diff --git a/src/main.zig b/src/main.zig index bd5cc13cd3..b43086cccd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -114,15 +114,15 @@ pub fn main() anyerror!void { return mainArgs(gpa, arena, args); } +const os_can_execve = std.builtin.os.tag != .windows; + pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void { if (args.len <= 1) { std.log.info("{}", .{usage}); fatal("expected command argument", .{}); } - if (std.Target.current.os.tag != .windows and - std.os.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) - { + if (os_can_execve and std.os.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) { // In this case we have accidentally invoked ourselves as "the system C compiler" // to figure out where libc is installed. This is essentially infinite recursion // via child process execution due to the CC environment variable pointing to Zig. @@ -1710,40 +1710,50 @@ fn buildOutputType( 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.emit.?.directory; - const exe_path = try fs.path.join(arena, &[_][]const u8{ - exe_directory.path orelse ".", exe_loc.basename, - }); + const run_or_test = switch (arg_mode) { + .run, .zig_test => true, + else => false, + }; + if (run_or_test) run: { + const exe_loc = emit_bin_loc orelse break :run; + const exe_directory = exe_loc.directory orelse comp.bin_file.options.emit.?.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(); + var argv = std.ArrayList([]const u8).init(gpa); + defer argv.deinit(); - if (test_exec_args.items.len == 0) { - if (!std.Target.current.canExecBinariesOf(target_info.target)) { - switch (arg_mode) { - .zig_test => { - warn("created {s} but skipping execution because it is non-native", .{exe_path}); - if (!watch) return cleanExit(); - break :run; - }, - .run => fatal("unable to execute {s}: non-native", .{exe_path}), - else => unreachable, - } - } - try argv.append(exe_path); - } else { - for (test_exec_args.items) |arg| { - try argv.append(arg orelse exe_path); + if (test_exec_args.items.len == 0) { + if (!std.Target.current.canExecBinariesOf(target_info.target)) { + switch (arg_mode) { + .zig_test => { + warn("created {s} but skipping execution because it is non-native", .{exe_path}); + if (!watch) return cleanExit(); + break :run; + }, + .run => fatal("unable to execute {s}: non-native", .{exe_path}), + else => unreachable, } } - if (runtime_args_start) |i| { - try argv.appendSlice(all_args[i..]); + try argv.append(exe_path); + } else { + for (test_exec_args.items) |arg| { + try argv.append(arg orelse exe_path); } - // TODO On operating systems that support it, do an execve here rather than child process, - // when watch=false and arg_mode == .run + } + if (runtime_args_start) |i| { + try argv.appendSlice(all_args[i..]); + } + // We do not execve for tests because if the test fails we want to print the error message and + // invocation below. + if (os_can_execve and arg_mode == .run and !watch) { + // TODO improve the std lib so that we don't need a call to getEnvMap here. + var env_vars = try process.getEnvMap(arena); + const err = std.os.execvpe(gpa, argv.items, &env_vars); + const cmd = try argvCmd(arena, argv.items); + fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); + } else { const child = try std.ChildProcess.init(argv.items, gpa); defer child.deinit(); @@ -1784,8 +1794,7 @@ fn buildOutputType( }, else => unreachable, } - }, - else => {}, + } } const stdin = std.io.getStdIn().inStream();