use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
fn main() {
verify_runtime_version();
println!("cargo:rerun-if-changed=Cargo.toml");
println!("cargo:rerun-if-changed=../runtime/src");
println!("cargo:rerun-if-changed=../runtime/Cargo.toml");
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let target_dir = out_dir
.parent() .and_then(|p| p.parent()) .and_then(|p| p.parent()) .expect("Could not find target directory");
let deps_dir = target_dir.join("deps");
let runtime_lib = newest_runtime_lib(&deps_dir)
.or_else(|| {
let direct = target_dir.join("libplg_runtime.a");
direct.exists().then_some(direct)
})
.unwrap_or_else(|| {
panic!(
"Runtime library not found.\n\
Looked in deps: {}\n\
And: {}\n\
OUT_DIR was: {}",
deps_dir.display(),
target_dir.join("libplg_runtime.a").display(),
out_dir.display()
)
});
println!(
"cargo:rustc-env=PLG_RUNTIME_LIB_PATH={}",
runtime_lib.display()
);
let runtime_bytes = fs::read(&runtime_lib).expect("Failed to read runtime lib for hashing");
println!(
"cargo:rustc-env=PLG_RUNTIME_HASH={:016x}",
fnv1a64(&runtime_bytes)
);
println!("cargo:rerun-if-changed={}", runtime_lib.display());
}
fn fnv1a64(bytes: &[u8]) -> u64 {
let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
for &byte in bytes {
hash ^= u64::from(byte);
hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
}
hash
}
fn newest_runtime_lib(deps_dir: &Path) -> Option<PathBuf> {
let mut best: Option<(SystemTime, PathBuf)> = None;
for entry in fs::read_dir(deps_dir).ok()?.flatten() {
let name = entry.file_name();
let name = name.to_string_lossy();
if !(name.starts_with("libplg_runtime") && name.ends_with(".a")) {
continue;
}
let Ok(mtime) = entry.metadata().and_then(|m| m.modified()) else {
continue;
};
let path = entry.path();
let better = match &best {
None => true,
Some((t, p)) => (mtime, &path) > (*t, p),
};
if better {
best = Some((mtime, path));
}
}
best.map(|(_, p)| p)
}
fn verify_runtime_version() {
let compiler_version = env!("CARGO_PKG_VERSION");
let cargo_toml_content =
fs::read_to_string("Cargo.toml").expect("Failed to read compiler/Cargo.toml");
let cargo_toml: toml::Value =
toml::from_str(&cargo_toml_content).expect("Failed to parse Cargo.toml");
let runtime_version = cargo_toml
.get("build-dependencies")
.and_then(|deps| deps.get("plg-runtime"))
.and_then(|dep| match dep {
toml::Value::Table(t) => t.get("version").and_then(|v| v.as_str()),
toml::Value::String(s) => Some(s.as_str()),
_ => None,
})
.expect("Could not find plg-runtime version in Cargo.toml");
let runtime_version = runtime_version.trim_start_matches('=');
if compiler_version != runtime_version {
panic!(
"\n\nVERSION MISMATCH: plg-compiler is {compiler_version} but \
build-dependencies pin plg-runtime to {runtime_version}.\n\
The embedded runtime MUST match the compiler version.\n\
Update crates/compiler/Cargo.toml to: version = \"={compiler_version}\"\n"
);
}
}