Lines
90.24 %
Functions
75 %
Branches
100 %
//! Tangle the canonical native fn registry from
//! =doc/scripting/native_reference.org= into
//! `src/natives/generated_specs.rs`. The org file is the source of
//! truth — this script is the build-time bridge cargo invokes
//! before compiling the crate.
//!
//! Mirrors the `scripting/format/build.rs` precedent: `emacs
//! --batch` evaluates the named babel block, which writes the Rust
//! file at a fixed path. `cargo build` is the only command needed;
//! editing the org and re-running `cargo build` regenerates the
//! tangled source (`cargo:rerun-if-changed=` on the org file
//! drives the incremental rebuild).
use std::path::Path;
use std::process::Command;
fn main() {
let org_path = "../doc/scripting/native_reference.org";
println!("cargo:rerun-if-changed={org_path}");
let status = Command::new("emacs")
.args([
"-q",
"--batch",
org_path,
"--eval",
"(setq org-confirm-babel-evaluate nil create-lockfiles nil)",
"(org-babel-goto-named-src-block \"emit-rust\")",
"(org-babel-execute-src-block)",
"-f",
"kill-emacs",
])
.status();
match status {
Ok(s) if s.success() => {}
Ok(s) => panic!(
"emacs --batch tangle of native_reference.org exited with {s} \
— regen of rpc/src/natives/generated_specs.rs failed"
),
Err(e) => panic!(
"failed to spawn emacs for native_reference.org tangle: {e}. \
cargo build requires emacs on PATH (same as finance/build.rs)"
}
// The babel block writes raw Rust without consulting rustfmt; run
// rustfmt here so the file's whitespace matches the rest of the
// tree. Without this, `cargo fmt --all --check` flags the tangled
// file and the pre-commit hook refuses every commit.
rustfmt_generated("src/natives/generated_specs.rs");
/// Formats a generated source so it matches `cargo fmt --all --check`.
/// rustfmt is resolved from the active toolchain's sysroot (where
/// `cargo fmt` finds it), not bare `$PATH`: minimal CI images expose
/// rustfmt only in the sysroot bin, so `Command::new("rustfmt")` would
/// spawn-fail there and ship the file unformatted, breaking the later
/// fmt check. A genuine failure is surfaced as a build warning, not
/// silently swallowed.
fn rustfmt_generated(path: &str) {
match rustfmt_command().arg("--edition=2024").arg(path).status() {
Ok(s) => println!("cargo:warning=rustfmt exited {s} on {path}"),
Err(e) => println!("cargo:warning=could not run rustfmt on {path}: {e}"),
fn rustfmt_command() -> Command {
// Probe the compiler Cargo set for this build (`$RUSTC`), not bare
// `rustc`, so the sysroot matches the active toolchain.
let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_owned());
let sysroot_rustfmt = Command::new(rustc)
.args(["--print", "sysroot"])
.output()
.ok()
.filter(|out| out.status.success())
.map(|out| Path::new(String::from_utf8_lossy(&out.stdout).trim()).join("bin/rustfmt"))
.filter(|path| path.exists());
sysroot_rustfmt.map_or_else(|| Command::new("rustfmt"), Command::new)