use anyhow::{anyhow, bail, ensure, Result};
use common::{
emit_link_info, SimicsBindings, VersionDeclaration, AUTO_BINDINGS_FILENAME,
AUTO_BINDINGS_VERSION_FILENAME, OUT_DIR_ENV, SIMICS_BASE_ENV,
};
use ispm_wrapper::ispm::{self, GlobalOptions};
use std::{env::var, path::PathBuf};
pub mod common {
use anyhow::{anyhow, bail, ensure, Result};
use bindgen::{
callbacks::{MacroParsingBehavior, ParseCallbacks},
AliasVariation, Bindings, Builder, EnumVariation, FieldVisibilityKind, MacroTypeVariation,
NonCopyUnionStyle,
};
use ispm_wrapper::ispm::{self, GlobalOptions};
use scraper::{Html, Selector};
use std::{
collections::HashSet,
env::var,
ffi::OsStr,
fmt::{self, Display, Formatter, Write},
fs::{read, read_dir, write},
path::{Path, PathBuf},
};
use walkdir::WalkDir;
pub const AUTO_BINDINGS_FILENAME: &str = "bindings-auto.rs";
pub const AUTO_BINDINGS_VERSION_FILENAME: &str = "version-auto.rs";
pub const OUT_DIR_ENV: &str = "OUT_DIR";
pub const SIMICS_BASE_ENV: &str = "SIMICS_BASE";
pub const PYTHON3_INCLUDE_ENV: &str = "PYTHON3_INCLUDE";
pub const INCLUDE_PATHS_ENV: &str = "INCLUDE_PATHS";
pub const PYTHON3_LDFLAGS_ENV: &str = "PYTHON3_LDFLAGS";
pub const LDFLAGS_ENV: &str = "LDFLAGS";
pub const LIBS_ENV: &str = "LIBS";
#[cfg(not(windows))]
pub const HOST_DIRNAME: &str = "linux64";
#[cfg(windows)]
pub const HOST_DIRNAME: &str = "win64";
pub const HAP_DOC_PATH: &str = "doc/html/rm-base/rm-haps.html";
pub const IGNORE_MACROS: [&str; 20] = [
"FE_DIVBYZERO",
"FE_DOWNWARD",
"FE_INEXACT",
"FE_INVALID",
"FE_OVERFLOW",
"FE_TONEAREST",
"FE_TOWARDZERO",
"FE_UNDERFLOW",
"FE_UPWARD",
"FP_INFINITE",
"FP_INT_DOWNWARD",
"FP_INT_TONEAREST",
"FP_INT_TONEARESTFROMZERO",
"FP_INT_TOWARDZERO",
"FP_INT_UPWARD",
"FP_NAN",
"FP_NORMAL",
"FP_SUBNORMAL",
"FP_ZERO",
"IPPORT_RESERVED",
];
pub const HDR_BLOCKLIST: [&str; 4] = [
"libfollower.h",
"data-structs.h",
"global.h",
"vtutils.h",
];
#[derive(Debug)]
struct IgnoreMacros(HashSet<String>);
impl ParseCallbacks for IgnoreMacros {
fn will_parse_macro(&self, name: &str) -> MacroParsingBehavior {
if self.0.contains(name) {
MacroParsingBehavior::Ignore
} else {
MacroParsingBehavior::Default
}
}
}
impl IgnoreMacros {
fn new() -> Self {
Self(IGNORE_MACROS.into_iter().map(|s| s.to_string()).collect())
}
}
pub struct IncludeWrapper {
include_path: PathBuf,
code: String,
}
impl IncludeWrapper {
pub fn include_code_from_includes<P>(include_path: P) -> Result<String>
where
P: AsRef<Path>,
{
let mut include_paths = WalkDir::new(&include_path)
.into_iter()
.filter_map(|p| p.ok())
.filter(|p| p.path().is_file())
.filter_map(|p| match p.path().extension() {
Some(e) => {
if e == "h" {
match p.path().canonicalize() {
Ok(p) => p.strip_prefix(&include_path).map_or_else(
|e| {
println!(
"cargo:warning=Failed to strip prefix {} from {}: {}",
include_path.as_ref().display(),
p.display(),
e
);
None::<PathBuf>
},
|p| Some(p.to_path_buf()),
),
Err(e) => {
println!(
"cargo:warning=Failed to canonicalize path {}: {}",
p.path().display(),
e
);
None
}
}
} else {
println!(
"cargo:warning=Ignoring path {}, no '.h' extension",
p.path().display()
);
None
}
}
None => {
println!(
"cargo:warning=Ignoring path {}, no extension",
p.path().display()
);
None
}
})
.collect::<Vec<_>>();
let Some(python_hdr_pos) = include_paths
.iter()
.position(|p| p.file_name() == Some(OsStr::new("python-header.h")))
else {
bail!("No python-header.h in include file list.");
};
include_paths.swap(0, python_hdr_pos);
HDR_BLOCKLIST.iter().for_each(|le| {
if let Some(pos) = include_paths
.iter()
.position(|p| p.file_name() == Some(OsStr::new(le)))
{
include_paths.remove(pos);
}
});
let include_stmts = include_paths
.iter()
.map(|p| format!("#include <{}>", p.display()))
.collect::<Vec<_>>();
let code = include_stmts.join("\n") + "\n";
Ok(code)
}
pub fn hap_code_from_doc<P>(hap_doc_path: P) -> Result<String>
where
P: AsRef<Path>,
{
let hap_document =
Html::parse_document(&String::from_utf8(read(&hap_doc_path).map_err(|e| {
anyhow!(
"Error reading document path {} to extract HAP definitions: {e}",
hap_doc_path.as_ref().display()
)
})?)?);
let haps_selector = Selector::parse(r#"article"#).unwrap();
let haps_id_selector = Selector::parse(r#"h2"#).unwrap();
let section_selector = Selector::parse(r#"section"#).unwrap();
let hap_code_selector = Selector::parse(r#"pre"#).unwrap();
let hap_description_selector = Selector::parse(r#"h3"#).unwrap();
let hap_index_selector = Selector::parse(r#"code"#).unwrap();
let haps_article = hap_document.select(&haps_selector).next().unwrap();
let haps_names = haps_article
.select(&haps_id_selector)
.filter_map(|h| h.value().id())
.collect::<Vec<_>>();
let haps_sections = haps_article.select(§ion_selector).collect::<Vec<_>>();
let haps_code_indices_descriptions = haps_sections
.iter()
.map(|s| {
let code = s
.select(&hap_code_selector)
.next()
.unwrap()
.inner_html()
.trim()
.to_string();
let maybe_index = s
.select(&hap_index_selector)
.next()
.map(|i| i.inner_html().trim().to_string());
let maybe_description = s
.select(&hap_description_selector)
.last()
.and_then(|i| i.next_sibling())
.and_then(|n| n.value().as_text().map(|t| t.trim().to_string()));
(code, maybe_index, maybe_description)
})
.collect::<Vec<_>>();
let hap_code = haps_names
.iter()
.zip(haps_code_indices_descriptions.iter())
.try_fold(
String::default(),
|mut s, (name, (code, maybe_index, maybe_description))| {
let mut hap_name_name = name.to_ascii_uppercase();
hap_name_name += "_HAP_NAME";
let mut hap_callback_name = name.to_ascii_lowercase();
hap_callback_name += "_hap_callback";
let code = code
.replace("(*)", &format!("(*{})", hap_callback_name))
.replace(['/', '-'], "_");
let comment = format!(
"/**\n * Index: {}\n * Description: {}\n */",
maybe_index
.as_ref()
.unwrap_or(&"Indices not supported".to_string()),
maybe_description
.as_ref()
.unwrap_or(&"No description".to_string())
);
write!(
&mut s,
"#define {} \"{}\"\n{}\ntypedef {}\n",
hap_name_name, name, comment, code,
)
.and_then(|_| Ok(s))
},
)?;
Ok(hap_code)
}
pub fn new<P>(simics_base_directory: P) -> Result<Self>
where
P: AsRef<Path>,
{
let hap_doc_path = simics_base_directory
.as_ref()
.join(HOST_DIRNAME)
.join(HAP_DOC_PATH);
let include_paths_env = simics_base_directory
.as_ref()
.join("src")
.join("include")
.to_str()
.map(|s| s.to_string())
.ok_or_else(|| anyhow!("Could not convert path to string"))?;
let include_path = PathBuf::from(&include_paths_env)
.canonicalize()
.map_err(|e| {
anyhow!(
"Include path from include path env {:?}: {}",
include_paths_env,
e
)
})?;
let include_code = Self::include_code_from_includes(&include_path)?;
let hap_code = Self::hap_code_from_doc(hap_doc_path)?;
Ok(Self {
code: include_code + &hap_code,
include_path,
})
}
pub fn write<P>(&self, path: P) -> Result<()>
where
P: AsRef<Path>,
{
write(path.as_ref(), &self.code).map_err(|e| {
anyhow!(
"Failed to write include wrapper to path {:?}: {}",
path.as_ref(),
e
)
})?;
Ok(())
}
}
impl Display for IncludeWrapper {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.code)
}
}
pub struct VersionDeclaration(String);
impl VersionDeclaration {
pub fn new<P>(simics_base_directory: P) -> Result<Self>
where
P: AsRef<Path>,
{
let simics_base_version = simics_base_directory
.as_ref()
.file_name()
.ok_or_else(|| anyhow!("No file name found in SIMICS base path"))?
.to_str()
.ok_or_else(|| anyhow!("Could not convert file name to string"))?
.split('-')
.last()
.ok_or_else(|| anyhow!("Could not split to obtain version: SIMICS base directory may not be in the format simics-X.X.XXX"))?
.to_string();
Ok(Self(format!(
r#"pub const SIMICS_VERSION: &str = "{}";"#,
simics_base_version
)))
}
pub fn write<P>(&self, path: P) -> Result<()>
where
P: AsRef<Path>,
{
write(path.as_ref(), &self.0).map_err(|e| {
anyhow!(
"Failed to write version declaration to path {:?}: {}",
path.as_ref(),
e
)
})?;
Ok(())
}
}
pub struct SimicsBindings {
pub bindings: Bindings,
}
impl SimicsBindings {
pub fn new<P>(base_dir_path: P, blocklist: &[&str], allowlist: &[&str]) -> Result<Self>
where
P: AsRef<Path>,
{
let wrapper = IncludeWrapper::new(&base_dir_path)?;
#[cfg(unix)]
let wrapper_include_path = wrapper.include_path.display().to_string();
#[cfg(windows)]
let wrapper_include_path = {
let path = wrapper.include_path.display().to_string();
if path.starts_with(r#"\\?\"#) {
path[4..].to_string()
} else {
path
}
};
let bindings = Builder::default()
.clang_arg(
subdir(base_dir_path.as_ref().join(HOST_DIRNAME).join("include")).and_then(
|p| {
p.to_str()
.map(|s| format!("-I{}", s))
.ok_or_else(|| anyhow!("Could not convert path to string"))
},
)?,
)
.clang_arg(format!("-I{}", &wrapper_include_path))
.clang_arg("-fretain-comments-from-system-headers")
.clang_arg("-fparse-all-comments")
.clang_arg("-Wno-everything")
.default_visibility(FieldVisibilityKind::Public)
.default_alias_style(AliasVariation::TypeAlias)
.default_enum_style(EnumVariation::Rust {
non_exhaustive: false,
})
.default_macro_constant_type(MacroTypeVariation::Unsigned)
.default_non_copy_union_style(NonCopyUnionStyle::BindgenWrapper)
.derive_default(true)
.derive_hash(true)
.derive_partialord(true)
.derive_ord(true)
.derive_eq(true)
.derive_partialeq(true)
.generate_comments(true)
.header_contents("wrapper.h", &wrapper.to_string())
.parse_callbacks(Box::new(IgnoreMacros::new()))
.blocklist_items(blocklist)
.allowlist_items(allowlist)
.blocklist_type("__mingw_ldbl_type_t")
.bitfield_enum("event_class_flag_t")
.bitfield_enum("micro_checkpoint_flags_t")
.bitfield_enum("access_t")
.bitfield_enum("breakpoint_flag")
.bitfield_enum("save_flags_t")
.blocklist_function("__acoshl")
.blocklist_function("acoshl")
.blocklist_function("__acosl")
.blocklist_function("acosl")
.blocklist_function("__asinhl")
.blocklist_function("asinhl")
.blocklist_function("__asinl")
.blocklist_function("asinl")
.blocklist_function("__atan2l")
.blocklist_function("atan2l")
.blocklist_function("__atanhl")
.blocklist_function("atanhl")
.blocklist_function("__atanl")
.blocklist_function("atanl")
.blocklist_function("__cbrtl")
.blocklist_function("cbrtl")
.blocklist_function("__ceill")
.blocklist_function("ceill")
.blocklist_function("__copysignl")
.blocklist_function("copysignl")
.blocklist_function("__coshl")
.blocklist_function("coshl")
.blocklist_function("__cosl")
.blocklist_function("cosl")
.blocklist_function("__dreml")
.blocklist_function("dreml")
.blocklist_function("__erfcl")
.blocklist_function("erfcl")
.blocklist_function("__erfl")
.blocklist_function("erfl")
.blocklist_function("__exp2l")
.blocklist_function("exp2l")
.blocklist_function("__expl")
.blocklist_function("expl")
.blocklist_function("__expm1l")
.blocklist_function("expm1l")
.blocklist_function("__fabsl")
.blocklist_function("fabsl")
.blocklist_function("__fdiml")
.blocklist_function("fdiml")
.blocklist_function("__finitel")
.blocklist_function("finitel")
.blocklist_function("__floorl")
.blocklist_function("floorl")
.blocklist_function("__fmal")
.blocklist_function("fmal")
.blocklist_function("__fmaxl")
.blocklist_function("fmaxl")
.blocklist_function("__fminl")
.blocklist_function("fminl")
.blocklist_function("__fmodl")
.blocklist_function("fmodl")
.blocklist_function("__fpclassifyl")
.blocklist_function("__frexpl")
.blocklist_function("frexpl")
.blocklist_function("__gammal")
.blocklist_function("gammal")
.blocklist_function("__hypotl")
.blocklist_function("hypotl")
.blocklist_function("__ilogbl")
.blocklist_function("ilogbl")
.blocklist_function("__iseqsigl")
.blocklist_function("__isinfl")
.blocklist_function("isinfl")
.blocklist_function("__isnanl")
.blocklist_function("isnanl")
.blocklist_function("__issignalingl")
.blocklist_function("__j0l")
.blocklist_function("j0l")
.blocklist_function("__j1l")
.blocklist_function("j1l")
.blocklist_function("__jnl")
.blocklist_function("jnl")
.blocklist_function("__ldexpl")
.blocklist_function("ldexpl")
.blocklist_function("__lgammal")
.blocklist_function("lgammal")
.blocklist_function("__lgammal_r")
.blocklist_function("lgammal_r")
.blocklist_function("__llrintl")
.blocklist_function("llrintl")
.blocklist_function("__llroundl")
.blocklist_function("llroundl")
.blocklist_function("__log10l")
.blocklist_function("log10l")
.blocklist_function("__log1pl")
.blocklist_function("log1pl")
.blocklist_function("__log2l")
.blocklist_function("log2l")
.blocklist_function("__logbl")
.blocklist_function("logbl")
.blocklist_function("__logl")
.blocklist_function("logl")
.blocklist_function("__lrintl")
.blocklist_function("lrintl")
.blocklist_function("__lroundl")
.blocklist_function("lroundl")
.blocklist_function("__modfl")
.blocklist_function("modfl")
.blocklist_function("__nanl")
.blocklist_function("nanl")
.blocklist_function("__nearbyintl")
.blocklist_function("nearbyintl")
.blocklist_function("__nextafterl")
.blocklist_function("nextafterl")
.blocklist_function("__nexttoward")
.blocklist_function("nexttoward")
.blocklist_function("__nexttowardf")
.blocklist_function("nexttowardf")
.blocklist_function("__nexttowardl")
.blocklist_function("nexttowardl")
.blocklist_function("__powl")
.blocklist_function("powl")
.blocklist_function("__remainderl")
.blocklist_function("remainderl")
.blocklist_function("__remquol")
.blocklist_function("remquol")
.blocklist_function("__rintl")
.blocklist_function("rintl")
.blocklist_function("__roundl")
.blocklist_function("roundl")
.blocklist_function("__scalbl")
.blocklist_function("scalbl")
.blocklist_function("__scalblnl")
.blocklist_function("scalblnl")
.blocklist_function("__scalbnl")
.blocklist_function("scalbnl")
.blocklist_function("__signbitl")
.blocklist_function("__significandl")
.blocklist_function("significandl")
.blocklist_function("__sinhl")
.blocklist_function("sinhl")
.blocklist_function("__sinl")
.blocklist_function("sinl")
.blocklist_function("__sqrtl")
.blocklist_function("sqrtl")
.blocklist_function("__tanhl")
.blocklist_function("tanhl")
.blocklist_function("__tanl")
.blocklist_function("tanl")
.blocklist_function("__tgammal")
.blocklist_function("tgammal")
.blocklist_function("__truncl")
.blocklist_function("truncl")
.blocklist_function("wcstold")
.blocklist_function("__y0l")
.blocklist_function("y0l")
.blocklist_function("__y1l")
.blocklist_function("y1l")
.blocklist_function("__ynl")
.blocklist_function("ynl")
.blocklist_function("strtold")
.blocklist_function("qecvt")
.blocklist_function("qfcvt")
.blocklist_function("qgcvt")
.blocklist_function("qecvt_r")
.blocklist_function("qfcvt_r")
.blocklist_function("__mingw_strtold")
.blocklist_function("__mingw_wcstold")
.blocklist_function("sincosl")
.blocklist_function("_chgsignl")
.blocklist_item("M_E")
.blocklist_item("M_LOG2E")
.blocklist_item("M_LOG10E")
.blocklist_item("M_LN2")
.blocklist_item("M_LN10")
.blocklist_item("M_PI")
.blocklist_item("M_PI_2")
.blocklist_item("M_PI_4")
.blocklist_item("M_1_PI")
.blocklist_item("M_2_PI")
.blocklist_item("M_2_SQRTPI")
.blocklist_item("M_SQRT2")
.blocklist_item("M_SQRT1_2")
.blocklist_item("Py_MATH_PIl")
.blocklist_item("Py_MATH_PI")
.blocklist_item("Py_MATH_El")
.blocklist_item("Py_MATH_E")
.blocklist_item("Py_MATH_TAU")
.generate()
.map_err(|e| {
println!("cargo:warning=Failed to generate bindings: {e}");
println!("cargo:warning=Include path: {}", &wrapper_include_path);
let wrapper_string = wrapper.to_string();
for (i, line) in wrapper_string.lines().enumerate() {
println!("cargo:warning={:04}: {}", i, line);
}
e
})?;
Ok(Self { bindings })
}
pub fn write<P>(&self, path: P) -> Result<()>
where
P: AsRef<Path>,
{
self.bindings.write_to_file(path)?;
Ok(())
}
}
impl Display for SimicsBindings {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.bindings.to_string())
}
}
pub fn subdir<P>(dir: P) -> Result<PathBuf>
where
P: AsRef<Path>,
{
let subdirs = read_dir(dir)?
.filter_map(|p| p.ok())
.map(|p| p.path())
.filter(|p| p.is_dir())
.collect::<Vec<_>>();
ensure!(
subdirs.len() == 1,
"Expected exactly 1 sub-directory, found {}",
subdirs.len()
);
subdirs
.first()
.cloned()
.ok_or_else(|| anyhow!("No sub-directories found"))
}
pub fn emit_link_info() -> Result<()> {
#[cfg(unix)]
const HOST_DIRNAME: &str = "linux64";
#[cfg(not(unix))]
const HOST_DIRNAME: &'static str = "win64";
let base_dir_path = if let Ok(simics_base) = var(SIMICS_BASE_ENV) {
PathBuf::from(simics_base)
} else {
println!("cargo:warning=No SIMICS_BASE environment variable found, using ispm to find installed packages and using latest base version");
let mut packages = ispm::packages::list(&GlobalOptions::default())?;
packages.sort();
let Some(installed) = packages.installed_packages.as_ref() else {
bail!("No SIMICS_BASE variable set and did not get any installed packages");
};
let Some(base) = installed.iter().find(|p| p.package_number == 1000) else {
bail!(
"No SIMICS_BASE variable set and did not find a package with package number 1000"
);
};
println!("cargo:warning=Using Simics base version {}", base.version);
base.paths
.first()
.ok_or_else(|| anyhow!("No paths found for package with package number 1000"))?
.clone()
};
#[cfg(unix)]
{
let bin_dir = base_dir_path
.join(HOST_DIRNAME)
.join("bin")
.canonicalize()?;
let libsimics_common = bin_dir.join("libsimics-common.so").canonicalize()?;
let libvtutils = bin_dir.join("libvtutils.so").canonicalize()?;
let sys_lib_dir = base_dir_path
.join(HOST_DIRNAME)
.join("sys")
.join("lib")
.canonicalize()?;
let libpython = sys_lib_dir.join(
read_dir(&sys_lib_dir)?
.filter_map(|p| p.ok())
.filter(|p| p.path().is_file())
.filter(|p| {
let path = p.path();
let Some(file_name) = path.file_name() else {
return false;
};
let Some(file_name) = file_name.to_str() else {
return false;
};
file_name.starts_with("libpython")
&& file_name.contains(".so")
&& file_name != "libpython3.so"
})
.map(|p| p.path())
.next()
.ok_or_else(|| {
anyhow!("No libpythonX.XX.so.X.X found in {}", sys_lib_dir.display())
})?,
);
println!(
"cargo:rustc-link-lib=dylib:+verbatim={}",
libsimics_common
.file_name()
.ok_or_else(|| anyhow!(
"No file name found for {}",
libsimics_common.display()
))?
.to_str()
.ok_or_else(|| anyhow!("Could not convert path to string"))?
);
println!(
"cargo:rustc-link-lib=dylib:+verbatim={}",
libvtutils
.file_name()
.ok_or_else(|| anyhow!("No file name found for {}", libvtutils.display()))?
.to_str()
.ok_or_else(|| anyhow!("Could not convert path to string"))?
);
println!(
"cargo:rustc-link-lib=dylib:+verbatim={}",
libpython
.file_name()
.ok_or_else(|| anyhow!("No file name found for {}", libpython.display()))?
.to_str()
.ok_or_else(|| anyhow!("Could not convert path to string"))?
);
println!(
"cargo:rustc-link-search=native={}",
bin_dir
.to_str()
.ok_or_else(|| anyhow!("Could not convert path to string"))?
);
println!(
"cargo:rustc-link-search=native={}",
sys_lib_dir
.to_str()
.ok_or_else(|| anyhow!("Could not convert path to string"))?
);
let ld_library_path = [
bin_dir
.to_str()
.ok_or_else(|| anyhow!("Could not convert path to string"))?,
sys_lib_dir
.to_str()
.ok_or_else(|| anyhow!("Could not convert path to string"))?,
]
.join(":");
println!("cargo:rustc-env=LD_LIBRARY_PATH={}", ld_library_path);
}
Ok(())
}
trait WithLists {
fn blocklist_items(self, blocklist: &[&str]) -> Self;
fn allowlist_items(self, allowlist: &[&str]) -> Self;
}
impl WithLists for Builder {
fn blocklist_items(self, blocklist: &[&str]) -> Self {
blocklist.iter().fold(self, |b, f| b.blocklist_item(f))
}
fn allowlist_items(self, allowlist: &[&str]) -> Self {
allowlist.iter().fold(self, |b, f| b.allowlist_item(f))
}
}
}
fn main() -> Result<()> {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-env-changed={SIMICS_BASE_ENV}");
let base_dir_path = if let Ok(simics_base) = var(SIMICS_BASE_ENV) {
PathBuf::from(simics_base)
} else {
println!("cargo:warning=No SIMICS_BASE environment variable found, using ispm to find installed packages and using latest base version");
let mut packages = ispm::packages::list(&GlobalOptions::default())?;
packages.sort();
let Some(installed) = packages.installed_packages.as_ref() else {
bail!("No SIMICS_BASE variable set and did not get any installed packages");
};
let Some(base) = installed.iter().find(|p| p.package_number == 1000) else {
bail!(
"No SIMICS_BASE variable set and did not find a package with package number 1000"
);
};
println!("cargo:warning=Using Simics base version {}", base.version);
base.paths
.first()
.ok_or_else(|| anyhow!("No paths found for package with package number 1000"))?
.clone()
};
ensure!(
base_dir_path.is_dir(),
"{} is not a directory",
base_dir_path.display()
);
let out_dir_path = PathBuf::from(
var(OUT_DIR_ENV)
.map_err(|e| anyhow!("No environment variable {OUT_DIR_ENV} found: {e}"))?,
);
let bindings_file_path = out_dir_path.join(AUTO_BINDINGS_FILENAME);
let version_file_path = out_dir_path.join(AUTO_BINDINGS_VERSION_FILENAME);
let version_declaration = VersionDeclaration::new(&base_dir_path).map_err(|e| {
anyhow!(
"Failed to create version declaration from path {:?}: {}",
base_dir_path,
e
)
})?;
let bindings = if var("SIMICS_BINDINGS_NOCLEAN").is_ok() {
let allowlist = &[
".*_HAP_NAME",
".*_hap_callback",
".*_INTERFACE",
".*_interface_t",
".*_interface",
"SIM_.*",
"VT_.*",
"CORE_.*",
];
SimicsBindings::new(&base_dir_path, &[], allowlist)?
} else {
let mut allowlist = SIMICS_API_ITEMS.to_vec();
allowlist.push(".*_HAP_NAME");
allowlist.push(".*_hap_callback");
allowlist.push(".*_INTERFACE");
allowlist.push(".*_interface_t");
allowlist.push(".*_interface");
allowlist.push("SIM_.*");
allowlist.push("VT_.*");
allowlist.push("CORE_.*");
SimicsBindings::new(&base_dir_path, &[], &allowlist)?
};
version_declaration.write(&version_file_path).map_err(|e| {
anyhow!(
"Failed to write version declaration to file {:?}: {}",
version_file_path,
e
)
})?;
bindings.write(&bindings_file_path).map_err(|e| {
anyhow!(
"Failed to write bindings to file {:?}: {}",
bindings_file_path,
e
)
})?;
if cfg!(feature = "link") {
emit_link_info()?;
}
Ok(())
}
include!("simics_api_items.rs");