diff options
author | 2022-12-14 18:00:56 -0800 | |
---|---|---|
committer | 2022-12-15 16:28:47 -0800 | |
commit | e38a3e5d85dd3b07b0ed61981e874907fa90296b (patch) | |
tree | 705adba8ce9a173e3d749625baedbcbe188586fb /src | |
parent | 6a1fc208354d173a66b61060ec5bb79699038006 (diff) | |
download | bun-e38a3e5d85dd3b07b0ed61981e874907fa90296b.tar.gz bun-e38a3e5d85dd3b07b0ed61981e874907fa90296b.tar.zst bun-e38a3e5d85dd3b07b0ed61981e874907fa90296b.zip |
[bun run] Use `execve` instead of `posix_spawn` when bun no longer needs to run script/bin
When we launch a script & there's nothing left to do, we should replace the process image with the new process instead of keeping the bun process alive while the other script is running.
Diffstat (limited to 'src')
-rw-r--r-- | src/cli/run_command.zig | 98 |
1 files changed, 78 insertions, 20 deletions
diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index abf7fdbc7..bbeed40a2 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -203,6 +203,8 @@ pub const RunCommand = struct { } } + const log = Output.scoped(.RUN, false); + pub fn runPackageScript( allocator: std.mem.Allocator, original_script: string, @@ -212,6 +214,19 @@ pub const RunCommand = struct { passthrough: []const string, silent: bool, ) !bool { + return runPackageScriptWithIsLast(allocator, original_script, name, cwd, env, passthrough, silent, false); + } + + pub fn runPackageScriptWithIsLast( + allocator: std.mem.Allocator, + original_script: string, + name: string, + cwd: string, + env: *DotEnv.Loader, + passthrough: []const string, + silent: bool, + is_last: bool, + ) !bool { const shell_bin = findShell(env.map.get("PATH") orelse "", cwd) orelse return error.MissingShell; var script = original_script; @@ -219,16 +234,19 @@ pub const RunCommand = struct { // We're going to do this slowly. // Find exact matches of yarn, pnpm, npm + try replacePackageManagerRun(©_script, script); - var combined_script: string = copy_script.items; + var combined_script: []u8 = copy_script.items; + + log("Script: \"{s}\"", .{combined_script}); if (passthrough.len > 0) { var combined_script_len: usize = script.len; for (passthrough) |p| { combined_script_len += p.len + 1; } - var combined_script_buf = try allocator.alloc(u8, combined_script_len); + var combined_script_buf = try allocator.alloc(u8, combined_script_len + @as(usize, @boolToInt(is_last))); std.mem.copy(u8, combined_script_buf, script); var remaining_script_buf = combined_script_buf[script.len..]; for (passthrough) |part| { @@ -241,35 +259,75 @@ pub const RunCommand = struct { } var argv = [_]string{ shell_bin, "-c", combined_script }; - var child_process = std.ChildProcess.init(&argv, allocator); if (!silent) { Output.prettyErrorln("<r><d><magenta>$<r> <d><b>{s}<r>", .{combined_script}); Output.flush(); } - var buf_map = try env.map.cloneToEnvMap(allocator); + if (!is_last) { + var child_process = std.ChildProcess.init(&argv, allocator); + var buf_map = try env.map.cloneToEnvMap(allocator); - child_process.env_map = &buf_map; - child_process.cwd = cwd; - child_process.stderr_behavior = .Inherit; - child_process.stdin_behavior = .Inherit; - child_process.stdout_behavior = .Inherit; + child_process.env_map = &buf_map; + child_process.cwd = cwd; + child_process.stderr_behavior = .Inherit; + child_process.stdin_behavior = .Inherit; + child_process.stdout_behavior = .Inherit; - const result = child_process.spawnAndWait() catch |err| { - Output.prettyErrorln("<r><red>error<r>: Failed to run script <b>{s}<r> due to error <b>{s}<r>", .{ name, @errorName(err) }); - Output.flush(); - return true; - }; + const result = child_process.spawnAndWait() catch |err| { + Output.prettyErrorln("<r><red>error<r>: Failed to run script <b>{s}<r> due to error <b>{s}<r>", .{ name, @errorName(err) }); + Output.flush(); + return true; + }; - if (result.Exited > 0) { - Output.prettyErrorln("<r><red>Script error<r> <b>\"{s}\"<r> exited with {d} status<r>", .{ name, result.Exited }); - Output.flush(); + switch (result) { + .Exited => |code| { + if (code > 0) { + Output.prettyErrorln("<r><red>Script error<r> <b>\"{s}\"<r> exited with {d} status<r>", .{ name, code }); + Output.flush(); - Global.exit(result.Exited); - } + Global.exit(code); + } + }, + .Signal => |signal| { + Output.prettyErrorln("<r><red>Script error<r> <b>\"{s}\"<r> was terminated by signal {d}<r>", .{ name, signal }); + Output.flush(); - return true; + Global.exit(1); + }, + .Stopped => |signal| { + Output.prettyErrorln("<r><red>Script error<r> <b>\"{s}\"<r> was stopped by signal {d}<r>", .{ name, signal }); + Output.flush(); + + Global.exit(1); + }, + + else => {}, + } + + return true; + } else { + // extra byte added above + combined_script.ptr[combined_script.len] = 0; + var combined_script_z = combined_script.ptr[0..combined_script.len :0]; + var binZ = allocator.dupeZ(u8, shell_bin) catch unreachable; + var argvZ = [_]?[*:0]const u8{ binZ, "-c", combined_script_z, "" }; + argvZ[argvZ.len - 1] = null; + + var map = env.map.createNullDelimitedEnvMap(allocator) catch |err| { + Output.prettyErrorln("<r><red>error<r>: Failed to run script <b>{s}<r> due to error <b>{s}<r>", .{ name, @errorName(err) }); + Output.flush(); + return true; + }; + + { + const err = std.os.execveZ(binZ, @ptrCast([*:null]const ?[*:0]const u8, &argvZ), map); + Output.prettyErrorln("<r><red>error<r>: Failed to run script <b>{s}<r> due to error <b>{s}<r>", .{ name, @errorName(err) }); + Output.flush(); + return true; + } + } } pub fn runBinary( ctx: Command.Context, |