aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-12-14 18:00:56 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-12-15 16:28:47 -0800
commite38a3e5d85dd3b07b0ed61981e874907fa90296b (patch)
tree705adba8ce9a173e3d749625baedbcbe188586fb /src
parent6a1fc208354d173a66b61060ec5bb79699038006 (diff)
downloadbun-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.zig98
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(&copy_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,