Compare commits

...

16 Commits

Author SHA1 Message Date
Michael H
c225c12e82 oops 2025-02-06 00:12:23 +11:00
RiskyMH
b898442d8b maybe pass tests 2025-02-05 12:43:52 +00:00
RiskyMH
d8b5f60ac6 maybe? 2025-02-03 14:01:32 +00:00
RiskyMH
97984890f7 ? 2025-02-03 11:21:09 +00:00
RiskyMH
814f273d88 maybe this fixes back? 2025-02-03 08:39:53 +00:00
Michael H
ac7d09061a Merge branch 'main' into riskymh/standalone-bun 2025-02-03 18:39:52 +11:00
RiskyMH
41574ecb77 support bunx 2025-02-03 07:20:49 +00:00
RiskyMH
ad79565978 . 2025-02-01 07:21:29 +00:00
RiskyMH
d8f63ac52e child_process.fork support 2025-02-01 07:19:32 +00:00
Michael H
e3c9353470 Merge branch 'main' into riskymh/standalone-bun 2025-02-01 17:47:11 +11:00
Michael H
4b3c50b504 fix feedback 2025-02-01 17:43:34 +11:00
RiskyMH
6d9f0a9733 lol 2025-01-29 05:45:09 +01:00
RiskyMH
fa2c77ee8d appears can mess with PATH on windows 2025-01-29 01:20:10 +01:00
RiskyMH
2838f534d9 prefer same used bun in bun shell 2025-01-29 01:10:30 +01:00
RiskyMH
f0c809d462 tests 2025-01-28 11:44:25 +01:00
RiskyMH
3f977f4596 maybe allow standalone bun (with no other bun in env) to use bun's cli (ie bun install) 2025-01-28 08:56:00 +01:00
6 changed files with 189 additions and 5 deletions

View File

@@ -785,6 +785,8 @@ pub const StandaloneModuleGraph = struct {
const self_exe = openSelf() catch return null;
defer _ = Syscall.close(self_exe);
if (bun.getRuntimeFeatureFlag("BUN_SKIP_STANDALONE_MODULE_GRAPH")) return null;
var trailer_bytes: [4096]u8 = undefined;
std.posix.lseek_END(self_exe.cast(), -4096) catch return null;

View File

@@ -3499,6 +3499,7 @@ pub fn selfExePath() ![:0]u8 {
return memo.load();
}
pub const exe_suffix = if (Environment.isWindows) ".exe" else "";
pub var is_standalone: ?bool = null;
pub const spawnSync = @This().spawn.sync.spawn;

View File

@@ -1766,6 +1766,8 @@ pub const Command = struct {
// bun build --compile entry point
if (try bun.StandaloneModuleGraph.fromExecutable(bun.default_allocator)) |graph| {
bun.is_standalone = true;
context_data = .{
.args = std.mem.zeroes(Api.TransformOptions),
.log = log,

View File

@@ -744,7 +744,12 @@ function fork(modulePath, args = [], options) {
if (options != null) {
validateObject(options, "options");
}
options = { __proto__: null, ...options, shell: false };
options = {
__proto__: null,
...options,
shell: false,
env: { BUN_SKIP_STANDALONE_MODULE_GRAPH: "1", ...options?.env },
};
options.execPath = options.execPath || process.execPath;
validateArgumentNullCheck(options.execPath, "options.execPath");

View File

@@ -4825,12 +4825,22 @@ pub const Interpreter = struct {
const path_buf = bun.PathBufferPool.get();
defer bun.PathBufferPool.put(path_buf);
const resolved = which(path_buf, spawn_args.PATH, spawn_args.cwd, first_arg_real) orelse blk: {
const resolved = blk: {
if (bun.strings.eqlComptime(first_arg_real, "bun") or bun.strings.eqlComptime(first_arg_real, "bun-debug")) blk2: {
if (bun.is_standalone == true) spawn_args.env_array.append(arena_allocator, "BUN_SKIP_STANDALONE_MODULE_GRAPH=1") catch break :blk2;
break :blk bun.selfExePath() catch break :blk2;
}
this.writeFailingError("bun: command not found: {s}\n", .{first_arg});
return;
break :blk which(path_buf, spawn_args.PATH, spawn_args.cwd, first_arg_real) orelse {
if (bun.strings.eqlComptime(first_arg_real, "bunx")) blk2: {
if (bun.is_standalone == true) spawn_args.env_array.append(arena_allocator, "BUN_SKIP_STANDALONE_MODULE_GRAPH=1") catch break :blk2;
this.args.insert(1, "x") catch break :blk2;
break :blk bun.selfExePath() catch break :blk2;
}
this.writeFailingError("bun: command not found: {s}\n", .{first_arg});
return;
};
};
const duped = arena_allocator.dupeZ(u8, bun.span(resolved)) catch bun.outOfMemory();

View File

@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe, tmpdirSync } from "harness";
import fs, { mkdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
import fs, { copyFileSync, mkdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
import path, { join } from "node:path";
import { isWindows } from "harness";
@@ -53,6 +53,170 @@ describe("bun build", () => {
}
});
test("running a standalone binary and run its own bun (Bun.$)", () => {
const tmp = tmpdirSync();
const src = path.join(tmp, "index.js");
fs.writeFileSync(src, "console.log(await Bun.$`bun --version`.text());", { encoding: "utf8" });
const outfile = path.join(tmp, "index.exe");
expect(["build", src, "--compile", "--outfile", outfile]).toRun();
// this is the important one, as it should be able to have a runnable bun
const {
exitCode: exitCode1,
stderr: stderr1,
stdout: stdout1,
} = Bun.spawnSync({
cmd: [outfile],
env: {
...bunEnv,
PATH: "",
},
stdout: "pipe",
stderr: "pipe",
});
expect(stdout1.toString("utf8")).toBe(`${Bun.version}\n\n`);
expect(stderr1.toString("utf8")).toBeEmpty();
expect(exitCode1).toBe(0);
// ensure it prefers standalone binary bun instead of a "fake" bun in path
const srcE = path.join(tmp, "bun.js");
const outfileE = path.join(tmp, "bunn");
fs.writeFileSync(srcE, "console.log('hi');", { encoding: "utf8" });
expect(["build", srcE, "--compile", "--outfile", outfileE]).toRun();
if (isWindows) copyFileSync(outfileE + ".exe", path.join(tmp, "bun.exe"));
else copyFileSync(outfileE, path.join(tmp, "bun"));
const {
exitCode: exitCode2,
stderr: stderr2,
stdout: stdout2,
} = Bun.spawnSync({
cmd: [outfile],
env: {
...bunEnv,
PATH: tmp,
},
stdout: "pipe",
stderr: "pipe",
});
expect(stdout2.toString("utf8")).toBe(`${Bun.version}\n\n`);
expect(stderr2.toString("utf8")).toBeEmpty();
expect(exitCode2).toBe(0);
// bunx
const srcX = path.join(tmp, "indexx.js");
fs.writeFileSync(srcX, "console.log(await Bun.$`bunx cowsay hi`.text());", {
encoding: "utf8",
});
const outfileX = path.join(tmp, "indexx.exe");
expect(["build", srcX, "--compile", "--outfile", outfileX]).toRun();
const {
exitCode: exitCode3,
stderr: stderr3,
stdout: stdout3,
} = Bun.spawnSync({
cmd: [outfileX],
env: {
...bunEnv,
PATH: "",
},
stdout: "pipe",
stderr: "pipe",
});
expect(stdout3.toString("utf8")).toMatchInlineSnapshot(`
" ____
< hi >
----
\\ ^__^
\\ (oo)\\_______
(__)\\ )\\/\\
||----w |
|| ||
"
`);
expect(stderr3.toString("utf8")).toBeEmpty();
expect(exitCode3).toBe(0);
fs.rmSync(tmp, { recursive: true, force: true });
});
test("running a standalone binary and run its own bun (child_process.fork)", () => {
const tmp = tmpdirSync();
const src = path.join(tmp, "index.js");
fs.writeFileSync(
src,
`
import { fork } from "child_process";
const f = fork("-p", ["1 + 1"], { env: { BUN_DEBUG_QUIET_LOGS: "1" } });
f.on("message", console.log);`,
{
encoding: "utf8",
},
);
const outfile = path.join(tmp, "index.exe");
expect(["build", src, "--compile", "--outfile", outfile]).toRun();
// ensure no infinite loop with child_process fork
const { exitCode, stderr, stdout } = Bun.spawnSync({
cmd: [outfile],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
expect(stderr.toString("utf8")).toBeEmpty();
expect(stdout.toString("utf8")).toBe("2\n");
expect(exitCode).toBe(0);
fs.rmSync(tmp, { recursive: true, force: true });
});
test("BUN_SKIP_STANDALONE_MODULE_GRAPH=1 to bypass running the standalone code and skip to bun cli", () => {
const tmp = tmpdirSync();
const src = path.join(tmp, "index.js");
fs.writeFileSync(src, "console.log('hello world');", { encoding: "utf8" });
const outfile = path.join(tmp, "index.exe");
expect(["build", src, "--compile", "--outfile", outfile]).toRun();
const {
exitCode: exitCode1,
stderr: stderr1,
stdout: stdout1,
} = Bun.spawnSync({
cmd: [outfile, "--version"],
env: {
...bunEnv,
BUN_SKIP_STANDALONE_MODULE_GRAPH: "1",
},
stdout: "pipe",
stderr: "pipe",
});
expect(stdout1.toString("utf8")).toBe(`${Bun.version}\n`);
expect(stderr1.toString("utf8")).toBeEmpty();
expect(exitCode1).toBe(0);
const {
exitCode: exitCode2,
stderr: stderr2,
stdout: stdout2,
} = Bun.spawnSync({
cmd: [outfile, "--version"],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
expect(stdout2.toString("utf8")).toBe("hello world\n");
expect(stderr2.toString("utf8")).toBeEmpty();
expect(exitCode2).toBe(0);
fs.rmSync(tmp, { recursive: true, force: true });
});
test("works with utf8 bom", () => {
const tmp = tmpdirSync();
const src = path.join(tmp, "index.js");