authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-06 12:51:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-06 12:51:51-07:00
log3791cd6781ce92478b8a17c8d56c5cf66d4ecfb0
treecd1b0bef65345a8be9d0ace813cabe694d87c9c2
parent426e4c784cee76d3711bb17fb53ef062e3b0f072

CLI: add 'run' command to the repl


1 files changed, 165 insertions(+), 110 deletions(-)

src/main.zig+165-110
...@@ -488,18 +488,20 @@ fn optionalStringEnvVar(arena: *Allocator, name: []const u8) !?[]const u8 {...@@ -488,18 +488,20 @@ fn optionalStringEnvVar(arena: *Allocator, name: []const u8) !?[]const u8 {
488 }488 }
489}489}
490490
491const ArgMode = union(enum) {
492 build: std.builtin.OutputMode,
493 cc,
494 cpp,
495 translate_c,
496 zig_test,
497 run,
498};
499
491fn buildOutputType(500fn buildOutputType(
492 gpa: *Allocator,501 gpa: *Allocator,
493 arena: *Allocator,502 arena: *Allocator,
494 all_args: []const []const u8,503 all_args: []const []const u8,
495 arg_mode: union(enum) {504 arg_mode: ArgMode,
496 build: std.builtin.OutputMode,
497 cc,
498 cpp,
499 translate_c,
500 zig_test,
501 run,
502 },
503) !void {505) !void {
504 var color: Color = .auto;506 var color: Color = .auto;
505 var optimize_mode: std.builtin.Mode = .Debug;507 var optimize_mode: std.builtin.Mode = .Debug;
...@@ -1967,108 +1969,21 @@ fn buildOutputType(...@@ -1967,108 +1969,21 @@ fn buildOutputType(
1967 .run, .zig_test => true,1969 .run, .zig_test => true,
1968 else => false,1970 else => false,
1969 };1971 };
1970 if (run_or_test) run: {1972 if (run_or_test) {
1971 const exe_loc = emit_bin_loc orelse break :run;1973 try runOrTest(
1972 const exe_directory = exe_loc.directory orelse comp.bin_file.options.emit.?.directory;1974 comp,
1973 const exe_path = try fs.path.join(arena, &[_][]const u8{1975 gpa,
1974 exe_directory.path orelse ".", exe_loc.basename,1976 arena,
1975 });1977 emit_bin_loc,
19761978 test_exec_args.items,
1977 var argv = std.ArrayList([]const u8).init(gpa);1979 self_exe_path,
1978 defer argv.deinit();1980 arg_mode,
19791981 target_info.target,
1980 if (test_exec_args.items.len == 0) {1982 watch,
1981 if (!std.Target.current.canExecBinariesOf(target_info.target)) {1983 &comp_destroyed,
1982 switch (arg_mode) {1984 all_args,
1983 .zig_test => {1985 runtime_args_start,
1984 warn("created {s} but skipping execution because it is non-native", .{exe_path});1986 );
1985 if (!watch) return cleanExit();
1986 break :run;
1987 },
1988 .run => fatal("unable to execute {s}: non-native", .{exe_path}),
1989 else => unreachable,
1990 }
1991 }
1992 // when testing pass the zig_exe_path to argv
1993 if (arg_mode == .zig_test)
1994 try argv.appendSlice(&[_][]const u8{
1995 exe_path, self_exe_path,
1996 })
1997 // when running just pass the current exe
1998 else
1999 try argv.appendSlice(&[_][]const u8{
2000 exe_path,
2001 });
2002 } else {
2003 for (test_exec_args.items) |arg| {
2004 if (arg) |a| {
2005 try argv.append(a);
2006 } else {
2007 try argv.appendSlice(&[_][]const u8{
2008 exe_path, self_exe_path,
2009 });
2010 }
2011 }
2012 }
2013 if (runtime_args_start) |i| {
2014 try argv.appendSlice(all_args[i..]);
2015 }
2016 // We do not execve for tests because if the test fails we want to print
2017 // the error message and invocation below.
2018 if (std.process.can_execv and arg_mode == .run and !watch) {
2019 // execv releases the locks; no need to destroy the Compilation here.
2020 const err = std.process.execv(gpa, argv.items);
2021 const cmd = try argvCmd(arena, argv.items);
2022 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
2023 } else {
2024 const child = try std.ChildProcess.init(argv.items, gpa);
2025 defer child.deinit();
2026
2027 child.stdin_behavior = .Inherit;
2028 child.stdout_behavior = .Inherit;
2029 child.stderr_behavior = .Inherit;
2030
2031 if (!watch) {
2032 // Here we release all the locks associated with the Compilation so
2033 // that whatever this child process wants to do won't deadlock.
2034 comp.destroy();
2035 comp_destroyed = true;
2036 }
2037
2038 const term = try child.spawnAndWait();
2039 switch (arg_mode) {
2040 .run => {
2041 switch (term) {
2042 .Exited => |code| {
2043 if (code == 0) {
2044 if (!watch) return cleanExit();
2045 } else {
2046 // TODO https://github.com/ziglang/zig/issues/6342
2047 process.exit(1);
2048 }
2049 },
2050 else => process.exit(1),
2051 }
2052 },
2053 .zig_test => {
2054 switch (term) {
2055 .Exited => |code| {
2056 if (code == 0) {
2057 if (!watch) return cleanExit();
2058 } else {
2059 const cmd = try argvCmd(arena, argv.items);
2060 fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
2061 }
2062 },
2063 else => {
2064 const cmd = try argvCmd(arena, argv.items);
2065 fatal("the following test command crashed:\n{s}", .{cmd});
2066 },
2067 }
2068 },
2069 else => unreachable,
2070 }
2071 }
2072 }1987 }
20731988
2074 const stdin = std.io.getStdIn().reader();1989 const stdin = std.io.getStdIn().reader();
...@@ -2096,6 +2011,21 @@ fn buildOutputType(...@@ -2096,6 +2011,21 @@ fn buildOutputType(
2096 break;2011 break;
2097 } else if (mem.eql(u8, actual_line, "help")) {2012 } else if (mem.eql(u8, actual_line, "help")) {
2098 try stderr.writeAll(repl_help);2013 try stderr.writeAll(repl_help);
2014 } else if (mem.eql(u8, actual_line, "run")) {
2015 try runOrTest(
2016 comp,
2017 gpa,
2018 arena,
2019 emit_bin_loc,
2020 test_exec_args.items,
2021 self_exe_path,
2022 arg_mode,
2023 target_info.target,
2024 watch,
2025 &comp_destroyed,
2026 all_args,
2027 runtime_args_start,
2028 );
2099 } else {2029 } else {
2100 try stderr.print("unknown command: {s}\n", .{actual_line});2030 try stderr.print("unknown command: {s}\n", .{actual_line});
2101 }2031 }
...@@ -2105,6 +2035,131 @@ fn buildOutputType(...@@ -2105,6 +2035,131 @@ fn buildOutputType(
2105 }2035 }
2106}2036}
21072037
2038fn runOrTest(
2039 comp: *Compilation,
2040 gpa: *Allocator,
2041 arena: *Allocator,
2042 emit_bin_loc: ?Compilation.EmitLoc,
2043 test_exec_args: []const ?[]const u8,
2044 self_exe_path: []const u8,
2045 arg_mode: ArgMode,
2046 target: std.Target,
2047 watch: bool,
2048 comp_destroyed: *bool,
2049 all_args: []const []const u8,
2050 runtime_args_start: ?usize,
2051) !void {
2052 const exe_loc = emit_bin_loc orelse return;
2053 const exe_directory = exe_loc.directory orelse comp.bin_file.options.emit.?.directory;
2054 const exe_path = try fs.path.join(arena, &[_][]const u8{
2055 exe_directory.path orelse ".", exe_loc.basename,
2056 });
2057
2058 var argv = std.ArrayList([]const u8).init(gpa);
2059 defer argv.deinit();
2060
2061 if (test_exec_args.len == 0) {
2062 if (!std.Target.current.canExecBinariesOf(target)) {
2063 switch (arg_mode) {
2064 .zig_test => {
2065 warn("created {s} but skipping execution because it is non-native", .{exe_path});
2066 if (!watch) return cleanExit();
2067 return;
2068 },
2069 .run => fatal("unable to execute {s}: non-native", .{exe_path}),
2070 else => unreachable,
2071 }
2072 }
2073 // when testing pass the zig_exe_path to argv
2074 if (arg_mode == .zig_test)
2075 try argv.appendSlice(&[_][]const u8{
2076 exe_path, self_exe_path,
2077 })
2078 // when running just pass the current exe
2079 else
2080 try argv.appendSlice(&[_][]const u8{
2081 exe_path,
2082 });
2083 } else {
2084 for (test_exec_args) |arg| {
2085 if (arg) |a| {
2086 try argv.append(a);
2087 } else {
2088 try argv.appendSlice(&[_][]const u8{
2089 exe_path, self_exe_path,
2090 });
2091 }
2092 }
2093 }
2094 if (runtime_args_start) |i| {
2095 try argv.appendSlice(all_args[i..]);
2096 }
2097 // We do not execve for tests because if the test fails we want to print
2098 // the error message and invocation below.
2099 if (std.process.can_execv and arg_mode == .run and !watch) {
2100 // execv releases the locks; no need to destroy the Compilation here.
2101 const err = std.process.execv(gpa, argv.items);
2102 const cmd = try argvCmd(arena, argv.items);
2103 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
2104 } else {
2105 const child = try std.ChildProcess.init(argv.items, gpa);
2106 defer child.deinit();
2107
2108 child.stdin_behavior = .Inherit;
2109 child.stdout_behavior = .Inherit;
2110 child.stderr_behavior = .Inherit;
2111
2112 if (!watch) {
2113 // Here we release all the locks associated with the Compilation so
2114 // that whatever this child process wants to do won't deadlock.
2115 comp.destroy();
2116 comp_destroyed.* = true;
2117 }
2118
2119 const term = try child.spawnAndWait();
2120 switch (arg_mode) {
2121 .run, .build => {
2122 switch (term) {
2123 .Exited => |code| {
2124 if (code == 0) {
2125 if (!watch) return cleanExit();
2126 } else if (watch) {
2127 warn("process exited with code {d}", .{code});
2128 } else {
2129 // TODO https://github.com/ziglang/zig/issues/6342
2130 process.exit(1);
2131 }
2132 },
2133 else => {
2134 if (watch) {
2135 warn("process aborted abnormally", .{});
2136 } else {
2137 process.exit(1);
2138 }
2139 },
2140 }
2141 },
2142 .zig_test => {
2143 switch (term) {
2144 .Exited => |code| {
2145 if (code == 0) {
2146 if (!watch) return cleanExit();
2147 } else {
2148 const cmd = try argvCmd(arena, argv.items);
2149 fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
2150 }
2151 },
2152 else => {
2153 const cmd = try argvCmd(arena, argv.items);
2154 fatal("the following test command crashed:\n{s}", .{cmd});
2155 },
2156 }
2157 },
2158 else => unreachable,
2159 }
2160 }
2161}
2162
2108const AfterUpdateHook = union(enum) {2163const AfterUpdateHook = union(enum) {
2109 none,2164 none,
2110 print: []const u8,2165 print: []const u8,