use std::error::Error;
mod context;
pub(crate) use context::{LinkerTemplateContext, TemplateContextCfg};
pub fn register_template(path_in: impl AsRef<std::path::Path>, name_out: &str) {
template_impl(path_in.as_ref(), name_out, false);
}
pub fn include_template(path_in: impl AsRef<std::path::Path>, name_out: &str) {
template_impl(path_in.as_ref(), name_out, true);
}
fn template_impl(path_in: &std::path::Path, name_out: &str, add_immediately: bool) {
println!("cargo:rerun-if-changed={}", path_in.display());
let linker_script_in = std::fs::read_to_string(path_in).expect("failed to read input linker script");
let linker_script_out = process(&linker_script_in).expect("failed to process linker script template");
let out_dir = std::env::var("OUT_DIR").expect("target output directory not found");
println!("cargo:rustc-link-search={out_dir}");
if add_immediately {
println!("cargo:rustc-link-lib=dylib:+verbatim={name_out}");
}
let path_out = std::path::Path::new(&out_dir).join(name_out);
std::fs::write(path_out, linker_script_out)
.expect("unable to write process linker script to target output directory");
}
fn process(linker_script_in: &str) -> Result<String, Box<dyn Error>> {
let mut env = minijinja::Environment::new();
env.set_lstrip_blocks(true);
env.set_trim_blocks(true);
env.add_function("contains", custom_functions::contains);
env.template_from_str(linker_script_in)?
.render(LinkerTemplateContext::new())
.map_err(Into::into)
}
mod custom_functions {
use minijinja::value::ViaDeserialize;
use crate::*;
pub(crate) fn contains(maybe_cfg: ViaDeserialize<Option<TemplateContextCfg>>, value: String) -> bool {
maybe_cfg.as_ref().is_some_and(|cfg| match cfg {
TemplateContextCfg::Value(existing_value) => existing_value == &value,
TemplateContextCfg::List(items) => items.contains(&value),
})
}
}