Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ channel = "nightly-2026-07-20"
# comes with the toolchain install regardless; they're listed here so a dev
# running `bun run rust:check-all` or `cargo check --target` from any host
# still gets prebuilt std for every Tier 1/2 triple.
# llvm-tools provides llvm-link/opt for the ELF cross-language LTO
# regular-LTO-summary fix-up (scripts/build/rust-lto-fix-cli.ts). CI agents
# that pin via RUSTUP_TOOLCHAIN bypass this list; that script self-heals by
# running `rustup component add llvm-tools` when the tools are missing.
components = ["rust-src", "rustfmt", "clippy", "miri", "llvm-tools"]
components = ["rust-src", "rustfmt", "clippy", "miri"]
targets = [
"aarch64-unknown-linux-gnu",
"x86_64-unknown-linux-gnu",
Expand Down
18 changes: 5 additions & 13 deletions scripts/build/bun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { assert } from "./error.ts";
import { bunIncludes, computeFlags, extraFlagsFor, linkDepends, linkerMapOutputs } from "./flags.ts";
import { writeIfChanged } from "./fs.ts";
import type { BuildNode, Ninja } from "./ninja.ts";
import { emitRust, rustLibPath, rustLtoLinkInputs } from "./rust.ts";
import { emitRust, rustLibPath } from "./rust.ts";
import { quote, slash } from "./shell.ts";
import { emitShims, machoPostlinkCommand, machoPostlinkImplicitInputs } from "./shims.ts";
import { computeDepLibs, resolveDep, type ResolvedDep } from "./source.ts";
Expand Down Expand Up @@ -504,13 +504,7 @@ export function emitBun(n: Ninja, cfg: Config, sources: Sources): BunOutput {
// is needed; if a member ever isn't, `rustLinkFlags()` in rust.ts is the
// wrapping helper.
const shims = emitShims(n, cfg);
// rustLtoLinkInputs(): on ELF cross-language LTO targets the Rust bitcode
// is rewritten with a regular-LTO summary first (identity elsewhere).
const linkObjects = [
...(archive !== undefined ? [archive] : allObjects),
...rustLtoLinkInputs(n, cfg, rustObjects),
...windowsRes,
];
const linkObjects = [...(archive !== undefined ? [archive] : allObjects), ...rustObjects, ...windowsRes];
const ldflags = [...flags.ldflags, ...systemLibs(cfg), ...shims.ldflags];
const exe = link(n, cfg, exeName, linkObjects, {
libs: depLibs,
Expand Down Expand Up @@ -639,10 +633,8 @@ function emitLinkOnly(n: Ninja, cfg: Config): BunOutput {

// libbun_runtime.a from rust-only: same path emitRust writes to. Shared
// helper so both sides of the CI split agree (cargo's
// `<target-dir>/<triple>/<profile>/` layout). rustLtoLinkInputs(): on ELF
// cross-language LTO targets the downloaded archive's bitcode is rewritten
// with a regular-LTO summary on this (link) agent before the link.
const rustObjects = rustLtoLinkInputs(n, cfg, [rustLibPath(cfg)]);
// `<target-dir>/<triple>/<profile>/` layout).
const rustObjects = [rustLibPath(cfg)];

// Only need ldflags + stripflags (no cflags/cxxflags — no compile).
const flags = computeFlags(cfg);
Expand Down Expand Up @@ -733,7 +725,7 @@ function emitRustAndLink(n: Ninja, cfg: Config, sources: Sources): BunOutput {
const windowsRes = cfg.windows ? [emitWindowsResources(n, cfg)] : [];

const shims = emitShims(n, cfg);
const linkObjects = [archive, ...rustLtoLinkInputs(n, cfg, rustObjects), ...windowsRes];
const linkObjects = [archive, ...rustObjects, ...windowsRes];
const ldflags = [...flags.ldflags, ...systemLibs(cfg), ...shims.ldflags];
const exe = link(n, cfg, exeName, linkObjects, {
libs: depLibs,
Expand Down
26 changes: 1 addition & 25 deletions scripts/build/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,6 @@ export interface Host {
arch: Arch;
/** ".exe" on a Windows host, "" elsewhere. Mirrors Config.exeSuffix (target). */
exeSuffix: string;
/**
* Host's Rust target triple — `host:` line from `rustc -vV`. Also the
* `${sysroot}/lib/rustlib/<triple>/` directory name. Stamped at
* `resolveConfig()` from `Toolchain.rustHostTriple` (so the toolchain
* probe is the single source of truth); `undefined` only when no rustc
* is installed.
*/
rustTriple: string | undefined;
}

/**
Expand Down Expand Up @@ -235,14 +227,6 @@ export interface Config {
rustLld: string | undefined;
/** Parsed `LLVM version:` from `rustc -vV`. Captured once; feeds workarounds.ts. */
rustLlvmVersion: string | undefined;
/**
* `rustc --print sysroot`. Used to locate rustc's bundled `llvm-nm` for
* reading LTO bitcode in `libbun_runtime.a` — clang's `llvm-nm` may lag
* rustc's LLVM major and reject the bitcode (#53609, #53656). Unlike
* `rustLld`, this is needed regardless of whether cross-language LTO is
* actually using rust-lld as the linker.
*/
rustSysroot: string | undefined;
strip: string;
/** llvm-nm, for `DirectBuild.forbidUndefined`; undefined skips those checks. */
nm: string | undefined;
Expand Down Expand Up @@ -443,10 +427,6 @@ export interface Toolchain {
rustLld: string | undefined;
/** Parsed `LLVM version:` from `rustc -vV` (X.Y.Z). */
rustLlvmVersion: string | undefined;
/** `rustc --print sysroot` — see `Config.rustSysroot`. */
rustSysroot: string | undefined;
/** `host:` line from `rustc -vV` — stamped onto `Host.rustTriple` at resolveConfig. */
rustHostTriple: string | undefined;
strip: string;
/**
* llvm-strip. On Linux hosts GNU strip is the default (`strip` above) but
Expand Down Expand Up @@ -520,9 +500,7 @@ export function detectHost(): Host {
throw new BuildError(`Unsupported host architecture: ${a}`, { hint: "Bun builds on x64 or arm64" });
})();

// rustTriple is stamped later from Toolchain.rustHostTriple in resolveConfig
// (the rustc probe is authoritative — distinguishes glibc/musl host etc.).
return { os, arch, exeSuffix: os === "windows" ? ".exe" : "", rustTriple: undefined };
return { os, arch, exeSuffix: os === "windows" ? ".exe" : "" };
}

/**
Expand Down Expand Up @@ -722,7 +700,6 @@ function linkNdkRuntimesIntoClang(cc: string, ndk: string, host: Host, triple: s
*/
export function resolveConfig(partial: PartialConfig, toolchain: Toolchain): Config {
const host = detectHost();
host.rustTriple = toolchain.rustHostTriple;

// ─── Target platform ───
const os = partial.os ?? host.os;
Expand Down Expand Up @@ -1233,7 +1210,6 @@ export function resolveConfig(partial: PartialConfig, toolchain: Toolchain): Con
ld: ld64StripSwap?.ld ?? ld,
rustLld: toolchain.rustLld,
rustLlvmVersion: toolchain.rustLlvmVersion,
rustSysroot: toolchain.rustSysroot,
// Cross strips: linux-gnu uses <triple>-strip (GNU, handles -R .eh_frame
// fully; host strip rejects foreign-arch ELF); other cross targets use
// llvm-strip.
Expand Down
173 changes: 0 additions & 173 deletions scripts/build/rust-lto-fix-cli.ts

This file was deleted.

56 changes: 1 addition & 55 deletions scripts/build/rust.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@
*/

import { existsSync, mkdirSync, writeFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { dirname, resolve } from "node:path";
import type { Abi, Arch, Config, OS } from "./config.ts";
import { assert } from "./error.ts";
import { computeCpuTargetFlags } from "./flags.ts";
import type { Ninja } from "./ninja.ts";
import { rustLtoFixCliPath } from "./rust-lto-fix-cli.ts";
import { quote, quoteArgs } from "./shell.ts";
import { streamPath } from "./stream.ts";

Expand Down Expand Up @@ -203,18 +202,6 @@ export function registerRustRules(n: Ninja, cfg: Config): void {
const hostWin = cfg.host.os === "windows";
const q = (p: string) => quote(p, hostWin);

// Regular-LTO summary fix-up for the ELF cross-language LTO link (see
// rustLtoLinkInputs() below). Registered before the cargo gate: the
// link-only CI agents emit this edge too, and it needs rustc's
// llvm-tools, not cargo. Not darwin/windows: their ThinLTO links keep the
// per-CGU summaries (CARGO_PROFILE_RELEASE_LTO=off) and need no fix-up.
if (cfg.crossLangLto && !cfg.darwin && !cfg.windows) {
n.rule("rust_lto_fix", {
command: `${cfg.jsRuntime} ${q(rustLtoFixCliPath)} $in $out $llvm_bin $ar`,
description: "regular-LTO summary → $out",
});
}

if (cfg.cargo === undefined) return; // emitRust() asserts with a hint
const stream = `${cfg.jsRuntime} ${q(streamPath)} rust`;

Expand Down Expand Up @@ -854,47 +841,6 @@ export function emitRust(n: Ninja, cfg: Config, inputs: RustBuildInputs): string
return [lib];
}

/**
* Link inputs for the Rust side of the binary.
*
* On ELF cross-language LTO targets the fat Rust bitcode member can't go
* into the link as-is: it has no per-module summary, so lld reads it as
* EnableSplitLTOUnit=0 while every clang-produced full-LTO object (ours,
* the deps', the WebKit -lto prebuilts') says 1, and the link aborts with
* "inconsistent LTO Unit splitting". rustc has no way to emit a regular-LTO
* summary (clang hardcodes one in shouldEmitRegularLTOSummary()), so a
* build step rewrites the bitcode with rustc's own LLVM tools — see
* rust-lto-fix-cli.ts and the `rustc-no-regular-lto-summary` workaround
* entry.
*
* Returns [fixed bitcode .o, original .a]: the .o defines every Rust symbol
* (so the archive's bitcode member is never pulled), and the archive still
* supplies its native members (compiler_builtins). On every other config
* this is the identity function.
*/
export function rustLtoLinkInputs(n: Ninja, cfg: Config, rustObjects: string[]): string[] {
const rustLib = rustObjects[0];
// All LTO platforms now use ThinLTO with -fno-split-lto-unit and per-CGU
// rust bitcode (CARGO_PROFILE_RELEASE_LTO=off), so the regular-LTO summary
// fix-up below is never needed. Delete this function once confirmed.
if (cfg.lto || !cfg.crossLangLto || cfg.darwin || cfg.windows || rustLib === undefined) return rustObjects;
assert(
cfg.rustSysroot !== undefined && cfg.host.rustTriple !== undefined,
"ELF cross-language LTO needs rustc's sysroot to locate its LLVM tools (llvm-link/opt) for the regular-LTO summary fix-up, but rustc wasn't found",
{ hint: "Install the pinned rust toolchain (rustup show active-toolchain), or build with --lto=off" },
);
const llvmBin = join(cfg.rustSysroot, "lib", "rustlib", cfg.host.rustTriple, "bin");
const out = resolve(cfg.buildDir, "bun_runtime.lto.o");
n.build({
outputs: [out],
rule: "rust_lto_fix",
inputs: [rustLib],
implicitInputs: [rustLtoFixCliPath],
vars: { llvm_bin: llvmBin, ar: cfg.ar },
});
return [out, ...rustObjects];
}

/**
* Linker flags to wrap the Rust staticlib so every `#[no_mangle]` member
* reaches the final image (the dynamic-list / NAPI surface has no inbound
Expand Down
Loading
Loading