diff options
author | 2022-12-14 18:00:56 -0800 | |
---|---|---|
committer | 2022-12-15 16:28:47 -0800 | |
commit | e38a3e5d85dd3b07b0ed61981e874907fa90296b (patch) | |
tree | 705adba8ce9a173e3d749625baedbcbe188586fb | |
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.
-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, |