aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/c.zig9
-rw-r--r--src/standalone_bun.zig6
-rw-r--r--test/bundler/cli.test.ts41
3 files changed, 49 insertions, 7 deletions
diff --git a/src/c.zig b/src/c.zig
index 8a88adddb..7b6f190bd 100644
--- a/src/c.zig
+++ b/src/c.zig
@@ -116,7 +116,8 @@ pub fn moveFileZWithHandle(from_handle: std.os.fd_t, from_dir: std.os.fd_t, file
std.os.renameatZ(from_dir, filename, to_dir, destination) catch |err| {
switch (err) {
error.RenameAcrossMountPoints => {
- try moveFileZSlowWithHandle(from_handle, to_dir, destination);
+ try copyFileZSlowWithHandle(from_handle, to_dir, destination);
+ std.os.unlinkatZ(from_dir, filename, 0) catch {};
},
else => {
return err;
@@ -129,10 +130,12 @@ pub fn moveFileZWithHandle(from_handle: std.os.fd_t, from_dir: std.os.fd_t, file
// macOS & BSDs will be slow because
pub fn moveFileZSlow(from_dir: std.os.fd_t, filename: [*:0]const u8, to_dir: std.os.fd_t, destination: [*:0]const u8) !void {
const in_handle = try std.os.openatZ(from_dir, filename, std.os.O.RDONLY | std.os.O.CLOEXEC, 0o600);
- try moveFileZSlowWithHandle(in_handle, to_dir, destination);
+ defer std.os.close(in_handle);
+ try copyFileZSlowWithHandle(in_handle, to_dir, destination);
+ std.os.unlinkatZ(from_dir, filename, 0) catch {};
}
-pub fn moveFileZSlowWithHandle(in_handle: std.os.fd_t, to_dir: std.os.fd_t, destination: [*:0]const u8) !void {
+pub fn copyFileZSlowWithHandle(in_handle: std.os.fd_t, to_dir: std.os.fd_t, destination: [*:0]const u8) !void {
const stat_ = try std.os.fstat(in_handle);
// delete if exists, don't care if it fails. it may fail due to the file not existing
// delete here because we run into weird truncation issues if we do not
diff --git a/src/standalone_bun.zig b/src/standalone_bun.zig
index 03cec9370..77cc92b15 100644
--- a/src/standalone_bun.zig
+++ b/src/standalone_bun.zig
@@ -257,7 +257,7 @@ pub const StandaloneModuleGraph = struct {
// if we're on a mac, use clonefile() if we can
// failure is okay, clonefile is just a fast path.
if (Syscall.clonefile(self_exeZ, zname) == .result) {
- switch (Syscall.open(zname, std.os.O.WRONLY | std.os.O.CLOEXEC, 0)) {
+ switch (Syscall.open(zname, std.os.O.RDWR | std.os.O.CLOEXEC, 0)) {
.result => |res| break :brk res,
.err => {},
}
@@ -269,7 +269,7 @@ pub const StandaloneModuleGraph = struct {
const fd = brk2: {
var tried_changing_abs_dir = false;
for (0..3) |retry| {
- switch (Syscall.open(zname, std.os.O.CLOEXEC | std.os.O.WRONLY | std.os.O.CREAT, 0)) {
+ switch (Syscall.open(zname, std.os.O.CLOEXEC | std.os.O.RDWR | std.os.O.CREAT, 0)) {
.result => |res| break :brk2 res,
.err => |err| {
if (retry < 2) {
@@ -439,7 +439,7 @@ pub const StandaloneModuleGraph = struct {
std.fs.cwd().fd,
&(try std.os.toPosixPath(temp_location)),
root_dir.dir.fd,
- &(try std.os.toPosixPath(outfile)),
+ &(try std.os.toPosixPath(std.fs.path.basename(outfile))),
) catch |err| {
if (err == error.IsDir) {
Output.prettyErrorln("<r><red>error<r><d>:<r> {} is a directory. Please choose a different --outfile or delete the directory", .{bun.fmt.quote(outfile)});
diff --git a/test/bundler/cli.test.ts b/test/bundler/cli.test.ts
index e2f99a9ed..3df2ab75e 100644
--- a/test/bundler/cli.test.ts
+++ b/test/bundler/cli.test.ts
@@ -1,6 +1,8 @@
import { bunEnv, bunExe } from "harness";
-import path from "path";
import { describe, expect, test } from "bun:test";
+import fs from "node:fs";
+import { tmpdir } from "node:os";
+import path from "node:path";
describe("bun build", () => {
test("warnings dont return exit code 1", () => {
@@ -13,4 +15,41 @@ describe("bun build", () => {
'warn: "key" prop before a {...spread} is deprecated in JSX. Falling back to classic runtime.',
);
});
+
+ test("generating a standalone binary in nested path, issue #4195", () => {
+ function testCompile(outfile: string) {
+ const { exitCode } = Bun.spawnSync({
+ cmd: [
+ bunExe(),
+ "build",
+ path.join(import.meta.dir, "./fixtures/trivial/index.js"),
+ "--compile",
+ "--outfile",
+ outfile,
+ ],
+ env: bunEnv,
+ });
+ expect(exitCode).toBe(0);
+ }
+ function testExec(outfile: string) {
+ const { exitCode } = Bun.spawnSync({
+ cmd: [outfile],
+ });
+ expect(exitCode).toBe(0);
+ }
+ {
+ const baseDir = `${tmpdir()}/bun-build-outfile-${Date.now()}`;
+ const outfile = path.join(baseDir, "index.exe");
+ testCompile(outfile);
+ testExec(outfile);
+ fs.rmSync(baseDir, { recursive: true, force: true });
+ }
+ {
+ const baseDir = `${tmpdir()}/bun-build-outfile2-${Date.now()}`;
+ const outfile = path.join(baseDir, "b/u/n", "index.exe");
+ testCompile(outfile);
+ testExec(outfile);
+ fs.rmSync(baseDir, { recursive: true, force: true });
+ }
+ });
});