This commit is contained in:
Claude Bot
2025-07-21 21:41:32 +00:00
parent 899e327047
commit f299efe096
3 changed files with 159 additions and 42 deletions

View File

@@ -380,7 +380,9 @@ pub const StandaloneModuleGraph = struct {
var entry_point_id: ?usize = null;
var string_builder = bun.StringBuilder{};
var module_count: usize = 0;
for (output_files) |output_file| {
std.debug.print("[DEBUG] toBytes - processing {d} output files\n", .{output_files.len});
for (output_files, 0..) |output_file, i| {
std.debug.print("[DEBUG] toBytes - file {d}: dest_path={s}, output_kind={}, side={?}, value={}\n", .{ i, output_file.dest_path, output_file.output_kind, output_file.side, output_file.value });
string_builder.countZ(output_file.dest_path);
string_builder.countZ(prefix);
if (output_file.value == .buffer) {
@@ -395,10 +397,15 @@ pub const StandaloneModuleGraph = struct {
string_builder.cap += (output_file.value.buffer.bytes.len + 255) / 256 * 256 + 256;
} else {
if (entry_point_id == null) {
if (output_file.side == null or output_file.side.? == .server) {
std.debug.print("[DEBUG] toBytes - checking entry-point: side={?}, output_kind={}\n", .{ output_file.side, output_file.output_kind });
// For standalone executables, accept client-side entry points as well as server-side
if (output_file.side == null or output_file.side.? == .server or output_file.side.? == .client) {
if (output_file.output_kind == .@"entry-point") {
std.debug.print("[DEBUG] toBytes - setting entry_point_id = {d}\n", .{module_count});
entry_point_id = module_count;
}
} else {
std.debug.print("[DEBUG] toBytes - skipping entry-point due to side: {?}\n", .{output_file.side});
}
}
@@ -408,7 +415,11 @@ pub const StandaloneModuleGraph = struct {
}
}
if (module_count == 0 or entry_point_id == null) return &[_]u8{};
std.debug.print("[DEBUG] toBytes - module_count: {d}, entry_point_id: {?}\n", .{ module_count, entry_point_id });
if (module_count == 0 or entry_point_id == null) {
std.debug.print("[DEBUG] toBytes - returning empty array because module_count={d} or entry_point_id={?}\n", .{ module_count, entry_point_id });
return &[_]u8{};
}
string_builder.cap += @sizeOf(CompiledModuleGraphFile) * output_files.len;
string_builder.cap += trailer.len;
@@ -853,7 +864,9 @@ pub const StandaloneModuleGraph = struct {
windows_hide_console: bool,
windows_icon: ?[]const u8,
) anyerror!void {
std.debug.print("[DEBUG] StandaloneModuleGraph.toExecutable entry - outfile: {s}\n", .{outfile});
const bytes = try toBytes(allocator, module_prefix, output_files, output_format);
std.debug.print("[DEBUG] toBytes returned {d} bytes\n", .{bytes.len});
if (bytes.len == 0) return;
const fd = try inject(
@@ -866,6 +879,7 @@ pub const StandaloneModuleGraph = struct {
target,
);
bun.debugAssert(fd.kind == .system);
std.debug.print("[DEBUG] After inject, about to check Environment.isWindows: {}\n", .{Environment.isWindows});
if (Environment.isWindows) {
var outfile_buf: bun.OSPathBuffer = undefined;
@@ -902,14 +916,27 @@ pub const StandaloneModuleGraph = struct {
var buf: bun.PathBuffer = undefined;
const temp_location = bun.getFdPath(fd, &buf) catch |err| return err;
const dest_basename = std.fs.path.basename(outfile);
std.debug.print("[DEBUG] toExecutable - temp_location: {s}\n", .{temp_location});
std.debug.print("[DEBUG] toExecutable - outfile: {s}\n", .{outfile});
std.debug.print("[DEBUG] toExecutable - dest_basename: {s}\n", .{dest_basename});
// Check the size of the temporary file before moving
if (std.fs.cwd().statFile(temp_location)) |temp_stat| {
std.debug.print("[DEBUG] toExecutable - temp file size: {d} bytes\n", .{temp_stat.size});
} else |err| {
std.debug.print("[DEBUG] toExecutable - failed to stat temp file: {}\n", .{err});
}
bun.sys.moveFileZWithHandle(
fd,
bun.FD.cwd(),
bun.sliceTo(&(try std.posix.toPosixPath(temp_location)), 0),
.fromStdDir(root_dir),
bun.sliceTo(&(try std.posix.toPosixPath(std.fs.path.basename(outfile))), 0),
bun.sliceTo(&(try std.posix.toPosixPath(dest_basename)), 0),
) catch |err| {
std.debug.print("[DEBUG] toExecutable - moveFileZWithHandle failed: {}\n", .{err});
if (err == error.IsDir or err == error.EISDIR) {
Output.prettyErrorln("<r><red>error<r><d>:<r> {} is a directory. Please choose a different --outfile or delete the directory", .{bun.fmt.quote(outfile)});
} else {

View File

@@ -2292,60 +2292,124 @@ pub const BundleV2 = struct {
const output_files_list = try this.linker.generateChunksInParallel(chunks, false);
// ADD COMPILE LOGIC HERE
// Handle compile: true option
if (this.transpiler.options.compile) {
defer {
for (output_files_list.items) |*file| {
file.deinit();
}
output_files_list.deinit();
}
const outfile = if (this.transpiler.options.output_dir.len > 0)
// This logic might need adjustment based on how --outfile is handled.
// For now, assume the first entry point name.
this.transpiler.options.entry_points[0]
else
"a.out";
// The JS task holds the compile target.
const compile_target_from_js = this.completion.?.compile_target;
std.debug.print("[DEBUG] Entering compile mode\n", .{});
// Extract the compile target from the completion task
const compile_target_from_js = if (this.completion) |completion| completion.compile_target else null;
var default_compile_target: CompileTarget = .{};
const target = compile_target_from_js orelse &default_compile_target;
// Determine output file name - use proper basename logic
const entry_point = this.transpiler.options.entry_points[0];
const basename = std.fs.path.basename(entry_point);
const name_without_ext = if (std.mem.lastIndexOfScalar(u8, basename, '.')) |dot_index|
basename[0..dot_index]
else
basename;
// Create output path in the specified output directory
var outfile_buf: bun.PathBuffer = undefined;
const outfile = if (this.transpiler.options.output_dir.len > 0) blk: {
break :blk std.fmt.bufPrint(&outfile_buf, "{s}{c}{s}", .{
this.transpiler.options.output_dir,
std.fs.path.sep,
name_without_ext,
}) catch "a.out";
} else name_without_ext;
std.debug.print("[DEBUG] Output file path: {s}\n", .{outfile});
// For compile mode, we need to load saved files into buffers for embedding
var output_files_for_executable = std.ArrayList(options.OutputFile).init(this.graph.allocator);
defer output_files_for_executable.deinit();
for (output_files_list.items) |*output_file| {
if (output_file.value == .saved) {
// Read the saved file content into a buffer
// Check if dest_path is absolute or relative to output_dir
const file_path = if (std.fs.path.isAbsolute(output_file.dest_path))
output_file.dest_path
else if (this.transpiler.options.output_dir.len > 0) blk: {
var path_buf: bun.PathBuffer = undefined;
break :blk std.fmt.bufPrint(&path_buf, "{s}{c}{s}", .{
this.transpiler.options.output_dir,
std.fs.path.sep,
output_file.dest_path,
}) catch output_file.dest_path;
} else output_file.dest_path;
std.debug.print("[DEBUG] Attempting to read file: {s}\n", .{file_path});
const file_content = std.fs.cwd().readFileAlloc(this.graph.allocator, file_path, 16 * 1024 * 1024) catch |err| {
std.debug.print("[DEBUG] Failed to read saved file {s}: {}\n", .{ file_path, err });
continue;
};
// Create a new output file with buffer content
var new_output_file = output_file.*;
new_output_file.value = .{ .buffer = .{ .allocator = this.graph.allocator, .bytes = file_content } };
try output_files_for_executable.append(new_output_file);
std.debug.print("[DEBUG] Loaded saved file into buffer: {s} ({d} bytes)\n", .{ file_path, file_content.len });
} else {
try output_files_for_executable.append(output_file.*);
}
}
// Generate standalone executable from bundled output
std.debug.print("[DEBUG] Calling toExecutable with {} output files\n", .{output_files_for_executable.items.len});
// For toExecutable to work correctly, we need to pass the directory and filename separately
var output_dir_owned: ?std.fs.Dir = null;
const output_dir = if (std.fs.path.dirname(outfile)) |dirname| blk: {
const dir = std.fs.cwd().openDir(dirname, .{}) catch |err| {
std.debug.print("[DEBUG] Failed to open output directory {s}: {}\n", .{ dirname, err });
return err;
};
output_dir_owned = dir;
break :blk dir;
} else std.fs.cwd();
defer if (output_dir_owned) |*dir| dir.close();
const output_filename = std.fs.path.basename(outfile);
std.debug.print("[DEBUG] Output directory: {s}, filename: {s}\n", .{
if (std.fs.path.dirname(outfile)) |dir| dir else ".",
output_filename
});
bun.StandaloneModuleGraph.toExecutable(
target,
this.graph.allocator,
output_files_list.items,
std.fs.cwd(), // Assumes CWD, might need to use rootdir
output_files_for_executable.items,
output_dir,
"", // module_prefix
outfile,
output_filename,
this.transpiler.env,
this.transpiler.options.output_format,
false, // windows_hide_console, TODO: expose this option
null, // windows_icon, TODO: expose this option
) catch |err| {
// Handle the new errors from StandaloneModuleGraph
std.debug.print("[DEBUG] toExecutable failed with error: {}\n", .{err});
this.transpiler.log.addError(null, .{}, @errorName(err)) catch {};
// Propagate the error to fail the Bun.build() promise
return err;
};
std.debug.print("[DEBUG] toExecutable completed successfully\n", .{});
// Debug: Check if the executable was actually created
if (std.fs.cwd().access(outfile, .{})) {
std.debug.print("[DEBUG] Executable file created successfully at: {s}\n", .{outfile});
} else |err| {
std.debug.print("[DEBUG] Executable file NOT found after toExecutable: {} at path: {s}\n", .{ err, outfile });
}
// Return a BuildArtifact representing the executable
var executable_output = std.ArrayList(options.OutputFile).init(this.graph.allocator);
try executable_output.append(options.OutputFile{
.loader = .file,
.src_path = Fs.Path.init(this.transpiler.options.entry_points[0]),
.dest_path = outfile,
.value = .{ .saved = .{} },
.size = 0, // Will be set correctly by the file system
.output_kind = .@"entry-point",
.side = null,
.entry_point_index = 0,
});
return executable_output;
// Clean up the intermediate output files since we've created an executable
for (output_files_list.items) |*file| {
file.deinit();
}
output_files_list.deinit();
// Return empty output list for compile mode (executable was written to disk)
return std.ArrayList(options.OutputFile).init(this.graph.allocator);
}
// END ADDED BLOCK
return output_files_list;
}

View File

@@ -4942,6 +4942,7 @@ pub fn moveFileZWithHandle(from_handle: bun.FileDescriptor, from_dir: bun.FileDe
if (err.getErrno() == .XDEV) {
try copyFileZSlowWithHandle(from_handle, to_dir, destination).unwrap();
_ = unlinkat(from_dir, filename);
return;
}
return bun.errnoToZigErr(err.errno);
@@ -5008,9 +5009,34 @@ pub fn copyFileZSlowWithHandle(in_handle: bun.FileDescriptor, to_dir: bun.FileDe
_ = std.os.linux.fallocate(out_handle.cast(), 0, 0, @intCast(stat_.size));
}
// Seek to the beginning of the input file
switch (lseek(in_handle, 0, std.posix.SEEK.SET)) {
.result => |pos| {
std.debug.print("[DEBUG] copyFileZSlowWithHandle - seeked to position: {d}\n", .{pos});
},
.err => |err| {
std.debug.print("[DEBUG] copyFileZSlowWithHandle - failed to seek input file: {}\n", .{err});
},
}
std.debug.print("[DEBUG] copyFileZSlowWithHandle - copying from fd {d} to fd {d}, source size: {d}\n", .{ in_handle.cast(), out_handle.cast(), stat_.size });
switch (bun.copyFile(in_handle, out_handle)) {
.err => |e| return .{ .err = e },
.result => {},
.err => |e| {
std.debug.print("[DEBUG] copyFileZSlowWithHandle - copyFile failed: {}\n", .{e});
return .{ .err = e };
},
.result => {
std.debug.print("[DEBUG] copyFileZSlowWithHandle - copyFile succeeded\n", .{});
// Check the size of the output file after copying
switch (fstat(out_handle)) {
.result => |out_stat| {
std.debug.print("[DEBUG] copyFileZSlowWithHandle - output file size after copy: {d}\n", .{out_stat.size});
},
.err => |err| {
std.debug.print("[DEBUG] copyFileZSlowWithHandle - failed to stat output file: {}\n", .{err});
},
}
},
}
if (comptime Environment.isPosix) {