Compare commits

...

8 Commits

Author SHA1 Message Date
claude[bot]
8e79089c72 Refactor --compile-argv to --compile-exec-argv with AutoCommand-style parsing
- Rename parameter from --compile-argv to --compile-exec-argv
- Change from option() to flag() for AutoCommand-style behavior  
- Parse remaining arguments from ctx.passthrough instead of splitting single string
- Update test harness to pass individual arguments as separate CLI parameters

Usage: bun build --compile --compile-exec-argv --foo bar

Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2025-05-24 20:48:45 +00:00
Jarred-Sumner
c339478739 bun run prettier 2025-05-24 20:40:18 +00:00
Jarred-Sumner
bd729be79a bun run zig-format 2025-05-24 20:39:02 +00:00
Jarred Sumner
388d51e5fb Merge branch 'main' into codex/add-cli-flag-support-to-bun-build 2025-05-24 13:37:17 -07:00
Jarred Sumner
5ee79922dd Merge branch 'main' into codex/add-cli-flag-support-to-bun-build 2025-05-21 12:14:08 -07:00
claude[bot]
cb5aa1b6e9 feat(build): add --compile-argv parameter to fix build error
This change adds proper support for the --compile-argv CLI parameter to bun build,
which allows embedding arguments in compiled executables that will be prepended to
process.argv when the executable is run.

Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2025-05-19 19:15:29 +00:00
claude[bot]
52d7e57263 feat(build): add --compile-argv parameter to fix build error
This commit adds the missing --compile-argv parameter definition to the parameter list in cli.zig. The code to handle the parameter was already there, but the parameter itself wasn't registered, causing a compilation error.

Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2025-05-19 19:07:50 +00:00
Jarred Sumner
b895f798fd feat(compile): support embedded runtime argv 2025-05-19 08:39:38 -07:00
7 changed files with 79 additions and 4 deletions

View File

@@ -19,6 +19,7 @@ pub const StandaloneModuleGraph = struct {
bytes: []const u8 = "",
files: bun.StringArrayHashMap(File),
entry_point_id: u32 = 0,
argv: []const [:0]const u8 = &.{},
// We never want to hit the filesystem for these files
// We use the `/$bunfs/` prefix to indicate that it's a virtual path
@@ -267,6 +268,8 @@ pub const StandaloneModuleGraph = struct {
byte_count: usize = 0,
modules_ptr: bun.StringPointer = .{},
entry_point_id: u32 = 0,
argv_ptr: bun.StringPointer = .{},
argv_count: u32 = 0,
};
const trailer = "\n---- Bun! ----\n";
@@ -306,10 +309,21 @@ pub const StandaloneModuleGraph = struct {
modules.lockPointers(); // make the pointers stable forever
var argv_list = try allocator.alloc([:0]const u8, offsets.argv_count);
if (offsets.argv_count > 0) {
const argv_bytes = sliceTo(raw_bytes, offsets.argv_ptr);
const argv_ptrs: []align(1) const StringPointer = std.mem.bytesAsSlice(StringPointer, argv_bytes);
var i: usize = 0;
while (i < argv_ptrs.len) : (i += 1) {
argv_list[i] = sliceToZ(raw_bytes, argv_ptrs[i]);
}
}
return StandaloneModuleGraph{
.bytes = raw_bytes[0..offsets.byte_count],
.files = modules,
.entry_point_id = offsets.entry_point_id,
.argv = argv_list,
};
}
@@ -325,7 +339,13 @@ pub const StandaloneModuleGraph = struct {
return bytes[ptr.offset..][0..ptr.length :0];
}
pub fn toBytes(allocator: std.mem.Allocator, prefix: []const u8, output_files: []const bun.options.OutputFile, output_format: bun.options.Format) ![]u8 {
pub fn toBytes(
allocator: std.mem.Allocator,
prefix: []const u8,
output_files: []const bun.options.OutputFile,
output_format: bun.options.Format,
argv: []const [:0]const u8,
) ![]u8 {
var serialize_trace = bun.perf.trace("StandaloneModuleGraph.serialize");
defer serialize_trace.end();
@@ -358,9 +378,14 @@ pub const StandaloneModuleGraph = struct {
}
}
for (argv) |arg| {
string_builder.countZ(arg);
}
if (module_count == 0 or entry_point_id == null) return &[_]u8{};
string_builder.cap += @sizeOf(CompiledModuleGraphFile) * output_files.len;
string_builder.cap += @sizeOf(StringPointer) * argv.len;
string_builder.cap += trailer.len;
string_builder.cap += 16;
string_builder.cap += @sizeOf(Offsets);
@@ -441,9 +466,16 @@ pub const StandaloneModuleGraph = struct {
modules.appendAssumeCapacity(module);
}
var argv_ptrs = try std.ArrayList(StringPointer).initCapacity(allocator, argv.len);
for (argv) |arg| {
argv_ptrs.appendAssumeCapacity(string_builder.appendCountZ(arg));
}
const offsets = Offsets{
.entry_point_id = @as(u32, @truncate(entry_point_id.?)),
.modules_ptr = string_builder.appendCount(std.mem.sliceAsBytes(modules.items)),
.argv_ptr = string_builder.appendCount(std.mem.sliceAsBytes(argv_ptrs.items)),
.argv_count = @as(u32, @truncate(argv_ptrs.items.len)),
.byte_count = string_builder.len,
};
@@ -772,8 +804,9 @@ pub const StandaloneModuleGraph = struct {
output_format: bun.options.Format,
windows_hide_console: bool,
windows_icon: ?[]const u8,
argv: []const [:0]const u8,
) !void {
const bytes = try toBytes(allocator, module_prefix, output_files, output_format);
const bytes = try toBytes(allocator, module_prefix, output_files, output_format, argv);
if (bytes.len == 0) return;
const fd = inject(

View File

@@ -267,6 +267,7 @@ pub const Arguments = struct {
const build_only_params = [_]ParamType{
clap.parseParam("--production Set NODE_ENV=production and enable minification") catch unreachable,
clap.parseParam("--compile Generate a standalone Bun executable containing your bundled code. Implies --production") catch unreachable,
clap.parseParam("--compile-argv <STR> Arguments to embed in the compiled executable that will be prepended to process.argv") catch unreachable,
clap.parseParam("--bytecode Use a bytecode cache") catch unreachable,
clap.parseParam("--watch Automatically restart the process on file change") catch unreachable,
clap.parseParam("--no-clear-screen Disable clearing the terminal screen on reload when --watch is enabled") catch unreachable,
@@ -1022,6 +1023,23 @@ pub const Arguments = struct {
}
ctx.bundler_options.windows_icon = path;
}
if (args.flag("--compile-exec-argv")) {
if (!ctx.bundler_options.compile) {
Output.errGeneric("--compile-exec-argv requires --compile", .{});
Global.crash();
}
// Parse remaining arguments for compile execution
const remaining_args = ctx.passthrough;
if (remaining_args.len > 0) {
var arr = try allocator.alloc([:0]const u8, remaining_args.len);
for (remaining_args, 0..) |arg, i| {
arr[i] = try allocator.dupeZ(u8, arg);
}
ctx.bundler_options.compile_argv = arr;
// Clear passthrough args since we consumed them
ctx.passthrough = &.{};
}
}
if (args.option("--outdir")) |outdir| {
if (outdir.len > 0) {
@@ -1610,6 +1628,7 @@ pub const Command = struct {
compile_target: Cli.CompileTarget = .{},
windows_hide_console: bool = false,
windows_icon: ?[]const u8 = null,
compile_argv: []const [:0]const u8 = &.{},
};
pub fn create(allocator: std.mem.Allocator, log: *logger.Log, comptime command: Command.Tag) anyerror!Context {
@@ -1814,6 +1833,17 @@ pub const Command = struct {
// bun build --compile entry point
if (try bun.StandaloneModuleGraph.fromExecutable(bun.default_allocator)) |graph| {
if (graph.argv.len > 0) {
var new_argv = try bun.default_allocator.alloc([:0]const u8, bun.argv.len + graph.argv.len);
new_argv[0] = bun.argv[0];
for (graph.argv, 0..) |a, i| {
new_argv[1 + i] = a;
}
for (bun.argv[1..], 0..) |a, i| {
new_argv[1 + graph.argv.len + i] = a;
}
bun.argv = new_argv;
}
context_data = .{
.args = std.mem.zeroes(Api.TransformOptions),
.log = log,

View File

@@ -10,7 +10,6 @@ const HeaderBuilder = http.HeaderBuilder;
const MutableString = bun.MutableString;
const URL = @import("../url.zig").URL;
const logger = bun.logger;
const semver = @import("../semver.zig");
const libdeflate = @import("../deps/libdeflate.zig");
const VulnerabilityInfo = struct {

View File

@@ -431,6 +431,7 @@ pub const BuildCommand = struct {
this_transpiler.options.output_format,
ctx.bundler_options.windows_hide_console,
ctx.bundler_options.windows_icon,
ctx.bundler_options.compile_argv,
);
const compiled_elapsed = @divTrunc(@as(i64, @truncate(std.time.nanoTimestamp() - bundled_end)), @as(i64, std.time.ns_per_ms));
const compiled_elapsed_digit_count: isize = switch (compiled_elapsed) {

View File

@@ -607,4 +607,13 @@ error: Hello World`,
},
},
});
itBundled("compile/CompileArgv", {
compile: true,
compileArgv: ["--foo", "bar"],
files: {
"/entry.ts": `console.log(process.argv.slice(2).join(" "))`,
},
run: { args: ["baz"], stdout: "--foo bar baz" },
});
});

View File

@@ -139,6 +139,8 @@ export interface BundlerTestInput {
outputPaths?: string[];
/** Use --compile */
compile?: boolean;
/** Arguments to embed with --compile */
compileArgv?: string[];
/** force using cli or js api. defaults to api if possible, then cli otherwise */
backend?: "cli" | "api";
@@ -420,6 +422,7 @@ function expectBundled(
chunkNaming,
cjs2esm,
compile,
compileArgv,
conditions,
dce,
dceKeepMarkerCount,
@@ -683,6 +686,7 @@ function expectBundled(
...(entryPointsRaw ?? []),
bundling === false ? "--no-bundle" : [],
compile ? "--compile" : [],
compileArgv && compileArgv.length ? ["--compile-exec-argv", ...compileArgv] : [],
outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`,
define && Object.entries(define).map(([k, v]) => ["--define", `${k}=${v}`]),
`--target=${target}`,

View File

@@ -16,7 +16,6 @@ test("18547", async () => {
expect(request.cookies.get("sessionToken")).toEqual("123456");
expect(clone.cookies.get("sessionToken")).toEqual("654321");
return new Response("OK");
},
},