/** * 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 ` 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; }