-
Notifications
You must be signed in to change notification settings - Fork 5k
build: bun-standalone — reduced-footprint --compile runtime #32262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
bda4775
97fd0e9
de8eb91
535eeea
5c34fe8
dd34b28
eb087fc
cb8c88c
4907b67
87dbc85
14fe94c
f1a9925
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -125,7 +125,7 @@ function download_buildkite_artifact() { | |
| # (build-bun unsigned, windows-sign signed). Pin to the sign step to | ||
| # guarantee we get the signed one. | ||
| local step_args=() | ||
| if [[ -n "$WINDOWS_ARTIFACT_STEP" && "$name" == bun-windows-* ]]; then | ||
| if [[ -n "$WINDOWS_ARTIFACT_STEP" && ( "$name" == bun-windows-* || "$name" == bun-standalone-windows-* ) ]]; then | ||
| step_args=(--step "$WINDOWS_ARTIFACT_STEP") | ||
| fi | ||
| run_command buildkite-agent artifact download "$name" "$dir" "${step_args[@]}" | ||
|
|
@@ -238,6 +238,36 @@ function create_release() { | |
| bun-windows-aarch64-profile.zip | ||
| ) | ||
|
|
||
| # Reduced-footprint --compile runtime. Same triplets minus android/freebsd | ||
| # (see shouldBuildStandalone in .buildkite/ci.mjs). buildkite-agent artifact | ||
| # download without --step searches the whole build, so these are picked up | ||
| # from the *-build-bun-standalone steps. | ||
| local standalone_artifacts=( | ||
| bun-standalone-darwin-aarch64.zip | ||
| bun-standalone-darwin-aarch64-profile.zip | ||
| bun-standalone-darwin-x64.zip | ||
| bun-standalone-darwin-x64-profile.zip | ||
| bun-standalone-linux-aarch64.zip | ||
| bun-standalone-linux-aarch64-profile.zip | ||
| bun-standalone-linux-x64.zip | ||
| bun-standalone-linux-x64-profile.zip | ||
| bun-standalone-linux-x64-baseline.zip | ||
| bun-standalone-linux-x64-baseline-profile.zip | ||
| bun-standalone-linux-aarch64-musl.zip | ||
| bun-standalone-linux-aarch64-musl-profile.zip | ||
| bun-standalone-linux-x64-musl.zip | ||
| bun-standalone-linux-x64-musl-profile.zip | ||
| bun-standalone-linux-x64-musl-baseline.zip | ||
| bun-standalone-linux-x64-musl-baseline-profile.zip | ||
| bun-standalone-windows-x64.zip | ||
| bun-standalone-windows-x64-profile.zip | ||
| bun-standalone-windows-x64-baseline.zip | ||
| bun-standalone-windows-x64-baseline-profile.zip | ||
| bun-standalone-windows-aarch64.zip | ||
| bun-standalone-windows-aarch64-profile.zip | ||
| ) | ||
| artifacts+=("${standalone_artifacts[@]}") | ||
|
|
||
|
Comment on lines
+269
to
+270
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix contradictory standalone upload flow (mandatory + duplicate best-effort pass). Line 269 makes standalone artifacts required by appending them to Suggested fix- artifacts+=("${standalone_artifacts[@]}")
@@
- for artifact in "${artifacts[@]}"; do
- local standalone="${artifact/bun-/bun-standalone-}"
- ( upload_artifact "$standalone" ) || echo "warn: skipping missing standalone artifact: $standalone"
- done
+ for standalone in "${standalone_artifacts[@]}"; do
+ ( upload_artifact "$standalone" ) || echo "warn: skipping missing standalone artifact: $standalone"
+ doneAlso applies to: 292-295 🤖 Prompt for AI Agents |
||
| function upload_artifact() { | ||
| local artifact="$1" | ||
| download_buildkite_artifact "$artifact" | ||
|
|
@@ -255,6 +285,15 @@ function create_release() { | |
| upload_artifact "$artifact" | ||
| done | ||
|
|
||
| # bun-standalone-* zips ship alongside the regular zips. Derived from the | ||
| # main artifact list so a new platform can't be forgotten here. Best-effort: | ||
| # a missing standalone artifact warns but doesn't abort the release | ||
| # (download_buildkite_artifact's `exit 1` only kills the subshell). | ||
| for artifact in "${artifacts[@]}"; do | ||
| local standalone="${artifact/bun-/bun-standalone-}" | ||
| ( upload_artifact "$standalone" ) || echo "warn: skipping missing standalone artifact: $standalone" | ||
| done | ||
|
|
||
|
Comment on lines
+288
to
+296
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Two mutually-exclusive approaches for uploading standalone artifacts are both active: line 269 appends Extended reasoning...What the bug is
The comment on the second loop says "Derived from the main artifact list so a new platform can't be forgotten here" — but the main artifact list is no longer just the 30 regular zips; it's the 52-element combined array. Step-by-step traceAfter line 269, First loop (284-286) —
Second loop (292-295) — iterates the same 52 entries and applies
Net per release: 22 redundant download+upload cycles (~hundreds of MB to S3/GitHub) and 30 spurious "warn: skipping missing standalone artifact" lines. Why existing code doesn't prevent it
Impact
How to fixPick one approach: Option A (keep the explicit list): delete the second loop (lines 288-295) entirely. The first loop already handles everything. If best-effort semantics are desired for standalone, wrap those entries in a subshell or iterate Option B (keep derivation): delete Option A is simpler given the explicit list already exists and matches |
||
| update_github_release "$tag" | ||
| create_sentry_release "$tag" | ||
| send_discord_announcement "$tag" | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| # `bun-standalone` — the `--compile` runtime binary | ||
|
|
||
| `bun-standalone` is a second build of the `bun` executable with the toolkit | ||
| subcommands compiled out. It exists so that `bun build --compile` can produce | ||
| smaller single-file executables: the embedded runtime only needs to _run_ | ||
| JavaScript, not bundle it, install packages, or run a test suite. | ||
|
|
||
| The binary name is `bun-standalone` (`bun-standalone.exe` on Windows). Debug | ||
| and instrumented variants follow the same suffix scheme as the full binary | ||
| (`bun-standalone-debug`, `bun-standalone-asan`, …). | ||
|
|
||
| ## What's removed | ||
|
|
||
| The CLI dispatch for every subcommand other than the run path is replaced | ||
| with an error message pointing at the full Bun install: | ||
|
|
||
| - `bun build` | ||
| - `bun test` | ||
| - `bun install` / `add` / `remove` / `update` / `link` / `unlink` / `pm` / | ||
| `outdated` / `publish` / `audit` / `why` / `info` / `patch` | ||
| - `bun init` / `create` / `x` / `upgrade` | ||
|
|
||
| `bun <file>`, `bun run`, `bun --eval/--print`, `bun exec`, `bun repl`, and | ||
| the `node`-shim entry remain. | ||
|
|
||
| The dispatch sever is the load-bearing change: with the per-tag `exec_*` | ||
| bodies gone, `--gc-sections` (driven by `.llvm_addrsig`, which both rustc and | ||
| clang emit) drops the now-unreferenced `bundle_v2` / `PackageManager` / | ||
| `TestCommand` machinery from the final image. The C++ object set is unchanged | ||
| — `build-cpp` produces one archive that both `bun` and `bun-standalone` link | ||
| against. | ||
|
|
||
| ## How it's built | ||
|
|
||
| `cfg.standalone` (a boolean on the build `Config`) drives three things: | ||
|
|
||
| - `cargo build -p bun_bin --features standalone` with | ||
| `RUSTFLAGS="… --cfg=bun_standalone"` into a separate `--target-dir` | ||
| (`rust-target-standalone/`), so the full and standalone staticlibs can | ||
| coexist in one build directory. | ||
| - the linked executable is named `bun-standalone[-profile]` and the | ||
| stripped output `bun-standalone`. | ||
| - `bun_core::build_options::STANDALONE_BUILD` is `true`. | ||
|
|
||
| Gating in Rust is on `cfg(bun_standalone)` (the global RUSTFLAG), not | ||
| `cfg(feature = "standalone")`, so any crate can branch on it without | ||
| threading a cargo feature through the workspace graph. The cargo feature on | ||
| `bun_bin` → `bun_runtime` exists so `cargo check -p bun_bin --features | ||
| standalone` is a valid invocation. | ||
|
|
||
| Locally: | ||
|
|
||
| ```sh | ||
| bun run build:standalone # release → build/release-standalone/bun-standalone | ||
| bun run build:standalone:debug # debug → build/debug-standalone/bun-standalone-debug | ||
| ``` | ||
|
|
||
| In CI, each release platform gets two extra steps that reuse the existing | ||
| `build-cpp` artifact: | ||
|
|
||
| ``` | ||
| <target>-build-cpp (shared) | ||
| <target>-build-rust ────────► <target>-build-bun | ||
| <target>-build-rust-standalone ────────► <target>-build-bun-standalone | ||
| ``` | ||
|
Comment on lines
+61
to
+65
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a language tag to this fenced block. Markdownlint flags the bare code fence here; Suggested fix-```
+```text🧰 Tools🪛 markdownlint-cli2 (0.22.1)[warning] 61-61: Fenced code blocks should have a language specified (MD040, fenced-code-language) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| `scripts/build/ci.ts::downloadArtifacts` derives the rust sibling from the | ||
| step-key suffix; the cpp sibling is always `<target>-build-cpp`. Packaged | ||
| artifacts are `bun-standalone-<os>-<arch>[-musl][-baseline].zip`. | ||
|
|
||
| ## Size | ||
|
|
||
| Linux-x64 release, non-LTO, measured on this branch: | ||
|
|
||
| | | bytes | MB | | ||
| | ------------------------- | -------------: | --------: | | ||
| | stripped `bun` | 70,389,048 | 67.13 | | ||
| | stripped `bun-standalone` | 62,392,896 | 59.50 | | ||
| | **delta** | **−7,996,152** | **−7.63** | | ||
|
|
||
| `bloaty` section diff: `.text` −6.80 MB, `.rodata` −849 KB. | ||
|
|
||
| Per-crate VM size from `bloaty -d compileunits` (full → standalone): | ||
|
|
||
| | crate | full MB | standalone MB | Δ | | ||
| | ----------------- | ------: | ------------: | ----: | | ||
| | `bun_runtime` | 6.45 | 4.75 | −1.70 | | ||
| | `bun_install` | 2.03 | 0.03 | −2.00 | | ||
| | `bun_css` | 1.77 | 0 | −1.77 | | ||
| | `bun_bundler` | 1.61 | 0.44 | −1.17 | | ||
| | `bun_css_jsc` | 0.10 | 0 | −0.10 | | ||
| | `bun_install_jsc` | 0.05 | 0 | −0.05 | | ||
|
|
||
| The remaining `bun_bundler` 0.44 MB is the `Transpiler` half (single-file | ||
| TS→JS, options/defines/cache, `analyze_transpiled_module`) which is | ||
| structurally embedded in `VirtualMachine` and required by the module loader. | ||
|
|
||
| The < 35 MB target additionally requires shipping a reduced ICU data file | ||
| (small-icu ≈ 5 MB instead of 24 MB) — a WebKit-prebuilt change. The hard | ||
| floor with full ICU is JSC 22.9 MB + ICU 23.7 MB + bindings/crypto/codecs | ||
| ≈ 57 MB. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Centralize standalone triplet derivation.
The standalone artifact prefix is derived twice with
triplet.replace(/^bun-/, "bun-standalone-"). Please route both call sites through a helper such asgetTargetTriplet(platform, { standalone: true })so signing, size metadata, and packaging stay on one naming contract. As per coding guidelines, “One source of truth; update every consumer atomically.”Also applies to: 1020-1027
🤖 Prompt for AI Agents
Source: Coding guidelines