From f299efe09669e1b301ebcf80160757e428d5f82c Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Mon, 21 Jul 2025 21:41:32 +0000 Subject: [PATCH] wip --- src/StandaloneModuleGraph.zig | 35 ++++++++- src/bundler/bundle_v2.zig | 136 +++++++++++++++++++++++++--------- src/sys.zig | 30 +++++++- 3 files changed, 159 insertions(+), 42 deletions(-) diff --git a/src/StandaloneModuleGraph.zig b/src/StandaloneModuleGraph.zig index 7cd281e521..df793b4a9d 100644 --- a/src/StandaloneModuleGraph.zig +++ b/src/StandaloneModuleGraph.zig @@ -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("error: {} is a directory. Please choose a different --outfile or delete the directory", .{bun.fmt.quote(outfile)}); } else { diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index cf547fda32..6c350df73b 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -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; } diff --git a/src/sys.zig b/src/sys.zig index 9222227a0e..ae58d4b99e 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -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) {