Files
bun-src/scripts/build/buildOptionsRs.ts
2026-08-27 21:09:14 +00:00

85 lines
3.8 KiB
TypeScript

/**
* Generates `${codegenDir}/build_options.rs` — Rust constants derived from
* `Config`, `include!()`'d by `bun_core::build_options`.
*
* Replaces the `option_env!("BUN_*")` handshake (a dozen env vars exported
* by `rust.ts`, read back in `bun_core`). `Config` is now the single source
* of truth: literal `pub const`s land on disk, so a bare `cargo check` /
* rust-analyzer sees the real version/sha/paths instead of placeholder
* defaults, and the env-var name list isn't maintained in two places.
*
* `bun_core/build.rs` emits `rerun-if-changed` on the file; `writeIfChanged`
* keeps the mtime stable so a reconfigure with the same sha doesn't
* recompile `bun_core` and its dependents.
*
* Target-dependent constants (`ENABLE_TINYCC`, `ENABLE_ASAN`, `ENABLE_LOGS`)
* stay as `cfg!()` expressions inside the generated file rather than literals
* so a `cargo check --target <other-triple>` against the same generated file
* still evaluates them per-target.
*
* Written at configure time alongside `depVersionsHeader.ts` /
* `cargo-config.ts` — it's a constant manifest, not a build edge.
*/
import { mkdirSync } from "node:fs";
import { resolve } from "node:path";
import type { Config } from "./config.ts";
import { writeIfChanged } from "./fs.ts";
/** Rust string literal for `s`. JSON escaping is a strict subset of Rust's. */
const rstr = (s: string): string => JSON.stringify(s);
/**
* Rust `&[u8]` literal for `s`. Goes via a UTF-8 string literal +
* `.as_bytes()` rather than `b"..."` so non-ASCII paths (e.g. a Windows
* checkout under `C:\Users\Müller\`) survive — Rust byte-string literals are
* ASCII-only and `JSON.stringify` would pass the `ü` through verbatim.
*/
const rbstr = (s: string): string => `${JSON.stringify(s)}.as_bytes()`;
export function generateBuildOptionsRs(cfg: Config): string {
const outPath = resolve(cfg.codegenDir, "build_options.rs");
const [major, minor, patch] = cfg.version.split(".");
const lines: string[] = [
"// Generated by scripts/build/buildOptionsRs.ts from `Config` at configure",
"// time. Do not edit. Regenerate with `bun bd`.",
"",
`pub const SHA: &str = ${rstr(cfg.revision)};`,
`pub const REPORTED_NODEJS_VERSION: &str = ${rstr(cfg.nodejsVersion)};`,
`pub const RELEASE_SAFE: bool = ${cfg.assertions};`,
`pub const IS_CANARY: bool = ${cfg.canary};`,
`pub const CANARY_REVISION: &str = ${rstr(cfg.canaryRevision)};`,
`pub const ENABLE_FUZZILLI: bool = ${cfg.fuzzilli};`,
`pub const FALLBACK_HTML_VERSION: &str = "0000000000000000";`,
"pub const VERSION: crate::Version = crate::Version {",
` major: ${Number(major)},`,
` minor: ${Number(minor)},`,
` patch: ${Number(patch)},`,
"};",
`pub const BASE_PATH: &[u8] = ${rbstr(cfg.cwd)};`,
`pub const CODEGEN_PATH: &[u8] = ${rbstr(cfg.codegenDir)};`,
"",
"// Target/profile-derived — kept as `cfg!()` so cross-target",
"// `cargo check` evaluates per-triple. Values agree with `Config`:",
"// rust.ts sets `--cfg=bun_debug` ⇔ `cfg.debug`, `--cfg=bun_asan` ⇔",
"// `cfg.asan`, and `cfg.tinycc`'s default (config.ts) is the negation",
"// of this predicate.",
"pub const ENABLE_LOGS: bool = cfg!(bun_debug);",
"pub const ENABLE_ASAN: bool = cfg!(bun_asan);",
"pub const ENABLE_TINYCC: bool = !cfg!(any(",
` target_os = "android",`,
` target_os = "freebsd",`,
"));",
"",
];
// Generated file self-opts-out of the workspace's denied unused lints. It is
// `include!`d, where inner attributes are rejected, so tag each item.
const allow = "#[allow(dead_code, unreachable_pub, unused)]";
const withAllow = lines.flatMap(l => (l.startsWith("pub const ") ? [allow, l] : [l]));
mkdirSync(cfg.codegenDir, { recursive: true });
writeIfChanged(outPath, withAllow.join("\n"));
return outPath;
}