use std::env;
use std::path::PathBuf;
use std::process::Command;
const FIXTURE_TEMPLATE: &str = include_str!("tests/fixtures/test_kernel.rs");
fn main() {
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_STATIC_TEST_KERNEL");
println!("cargo:rustc-link-arg-bins=-rdynamic");
let has_tests = has_integration_tests();
if has_tests {
println!("cargo:rustc-link-arg-tests=-rdynamic");
}
if env::var("CARGO_FEATURE_STATIC_TEST_KERNEL").is_err() {
return;
}
println!("cargo:rerun-if-changed=tests/fixtures/test_kernel.rs");
let out_dir = PathBuf::from(
env::var("OUT_DIR").expect("OUT_DIR is always set for a crate with a build.rs"),
);
let source = FIXTURE_TEMPLATE
.replace("__ABI_VERSION__", &vj_abi::VJ_ABI_VERSION.to_string())
.replace("__ABORT__", "");
let src_path = out_dir.join("static_test_kernel.rs");
std::fs::write(&src_path, &source)
.unwrap_or_else(|e| panic!("failed to write {src_path:?}: {e}"));
let archive_path = out_dir.join("libkernel_prologue.a");
let output = Command::new("rustc")
.arg("--crate-type=staticlib")
.arg("--crate-name=static_test_kernel")
.arg("--edition=2024")
.arg("-o")
.arg(&archive_path)
.arg(&src_path)
.output()
.unwrap_or_else(|e| {
panic!("failed to spawn rustc to compile the static fixture kernel: {e}")
});
assert!(
output.status.success(),
"rustc failed compiling the AOT-static fixture kernel; stderr:\n{}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
archive_path.exists(),
"rustc reported success but {archive_path:?} does not exist"
);
let archive_arg = archive_path.display().to_string();
println!("cargo:rustc-link-arg-bins={archive_arg}");
if has_tests {
println!("cargo:rustc-link-arg-tests={archive_arg}");
}
let shim = r#"// GENERATED by crates/vj/build.rs (Task 11, AB-006). Declares the
// statically-linked fixture kernel's ABI surface as ordinary `extern "C"`
// symbols -- no dynamic-loading crate, no dlopen. `include!`d, never a module (so each
// including crate gets its own private copy of these type/fn declarations,
// exactly mirroring the raw #[repr(C)] shapes
// crates/vj/tests/fixtures/test_kernel.rs's own doc comment
// documents for a dlopen'd kernel -- only the binary LAYOUT crosses this
// link boundary, never Rust nominal-type identity).
// #[allow(dead_code)] throughout: each consumer of this generated file
// (src/main.rs's smoke path, tests/static_link.rs) only actually calls
// sim_abi_version/sim_init -- the remaining seven entry points are declared
// for AB-006's "the exact 9 entry points" completeness (mirroring
// vj_abi::entry's own full set) even though no test here exercises them
// beyond proving they link (Task 10's dynamic-loader tests already cover
// their runtime behavior against the same fixture template).
#[allow(dead_code)]
#[repr(C)]
pub struct EvalResultRaw {
pub outcome: u32,
pub reschedule: [u8; 32],
}
#[allow(dead_code)]
#[repr(C)]
pub struct SimTimeRaw {
pub sim_units: i64,
pub delta: u32,
}
#[allow(dead_code)]
unsafe extern "C" {
pub fn sim_abi_version() -> u32;
pub fn sim_init(design_id: u32) -> *mut core::ffi::c_void;
pub fn sim_settle(h: *mut core::ffi::c_void) -> EvalResultRaw;
pub fn sim_eval_region(h: *mut core::ffi::c_void, region: u32, t: SimTimeRaw) -> EvalResultRaw;
pub fn sim_commit_nba(h: *mut core::ffi::c_void, t: SimTimeRaw) -> EvalResultRaw;
pub fn sim_save(h: *mut core::ffi::c_void) -> *mut core::ffi::c_void;
pub fn sim_restore(h: *mut core::ffi::c_void, checkpoint: *mut core::ffi::c_void);
pub fn sim_step_time(h: *mut core::ffi::c_void, t: SimTimeRaw) -> EvalResultRaw;
pub fn sim_final(h: *mut core::ffi::c_void);
pub fn sim_process_count(h: *mut core::ffi::c_void) -> u32;
// Task 16 (AB-009): the two narrow-scalar accessors, added to this
// generated shim so `tests/cross_mode_differential.rs`'s AOT-static leg
// can read/write the fixture's CELL/REQUEST_CELL slots the same way
// Task 10's dynamic loader already does -- additive only, the original
// 9 entry-point declarations above are unchanged.
pub fn sv_get_scalar(h: *mut core::ffi::c_void, slot: u32) -> u128;
pub fn sv_set_scalar(h: *mut core::ffi::c_void, slot: u32, v: u128);
}
"#;
let shim_path = out_dir.join("kernel_shim.rs");
std::fs::write(&shim_path, shim)
.unwrap_or_else(|e| panic!("failed to write {shim_path:?}: {e}"));
}
fn has_integration_tests() -> bool {
std::fs::read_dir("tests").is_ok_and(|dir| {
dir.flatten()
.any(|e| e.path().extension().is_some_and(|x| x == "rs"))
})
}