use std::path::{Path, PathBuf};
use std::{env, fs};
fn main() {
if env::var_os("CARGO_FEATURE_EMBEDDED").is_none() {
return;
}
println!("cargo::rerun-if-env-changed=SPIRAL_NATIVE_DIR");
println!("cargo::rerun-if-env-changed=SPIRAL_NATIVE_CACHE_DIR");
println!("cargo::rerun-if-env-changed=XDG_CACHE_HOME");
if env::var_os("CARGO_FEATURE_SOURCE_RUNTIME").is_some() || env::var_os("DOCS_RS").is_some() {
return;
}
let target = env::var("TARGET").expect("Cargo target");
let version = env::var("CARGO_PKG_VERSION").expect("package version");
let directory = match env::var_os("SPIRAL_NATIVE_DIR") {
Some(directory) => PathBuf::from(directory),
None => cache_directory().join(&version).join(&target),
};
match link_requirements(&directory) {
Ok(requirements) => {
println!("cargo::rerun-if-changed={}", directory.display());
println!("cargo::rustc-link-search=native={}", directory.display());
println!("cargo::rustc-link-lib=static=spiral");
for requirement in requirements {
println!("{requirement}");
}
}
Err(problem) => println!(
"cargo::error={problem} for {target} at {}; {}",
directory.display(),
setup_step()
),
}
}
fn link_requirements(directory: &Path) -> Result<Vec<String>, String> {
if !directory.join("libspiral.a").is_file() {
return Err("no prepared libspiral archive".into());
}
let manifest = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("manifest directory"));
let header = fs::read(directory.join("scandal.h")).unwrap_or_default();
if header != fs::read(manifest.join("include/scandal.h")).expect("published header") {
return Err("the libspiral archive was built for a different ABI".into());
}
let requirements = fs::read_to_string(directory.join("link.txt"))
.map_err(|_| "the libspiral archive has no link.txt")?;
requirements
.lines()
.filter(|line| !line.is_empty())
.map(|requirement| {
if requirement.starts_with("cargo::rustc-link-lib=") {
Ok(requirement.to_owned())
} else {
Err(format!("unexpected link requirement {requirement:?}"))
}
})
.collect()
}
fn setup_step() -> String {
let manifest = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("manifest directory"));
let packaged_script = manifest.join("scripts/setup-native-runtime.py");
let script = if packaged_script.is_file() {
packaged_script
} else {
manifest.join("../../../scripts/setup-native-runtime.py")
};
let script = fs::canonicalize(&script).unwrap_or(script);
format!(
"run `python3 {}` to download this release's verified archive (or its \
`setup-native-runtime` action at this release's tag), or set SPIRAL_NATIVE_DIR \
to a prepared archive directory",
script.display()
)
}
fn cache_directory() -> PathBuf {
if let Some(directory) = env::var_os("SPIRAL_NATIVE_CACHE_DIR") {
return PathBuf::from(directory);
}
let base = env::var_os("XDG_CACHE_HOME")
.map(PathBuf::from)
.or_else(|| env::var_os("HOME").map(|home| PathBuf::from(home).join(".cache")))
.expect("HOME or XDG_CACHE_HOME for the libspiral archive cache");
base.join("spiral").join("native")
}