use anyhow::{Context, Result};
use llzk_sys_build_support::{
config_traits::{bindgen::BindgenConfig as _, cc::CCConfig as _},
default::DefaultConfig,
link_llzk,
wrap_static_fns::WrapStaticFns,
};
use std::{
env, fs,
path::{Path, PathBuf},
};
const LLZK_MAJOR_VERSION: u8 = 1;
fn create_default_cfg() -> DefaultConfig<'static> {
let mut passes = vec![
"ArrayToScalar",
"InlineIncludes",
"Flattening",
"RedundantOperationElimination",
"RedundantReadAndWriteElimination",
"UnusedDeclarationElimination",
"MemberWriteValidator",
];
let pcl_enabled = feature_is_enabled("pcl-backend");
if pcl_enabled {
passes.push("PCLLowering");
}
DefaultConfig::new(
pcl_enabled,
passes,
&[
"GetDialectHandle__llzk__.*",
"Operation.*",
"OpBuilder.*",
"RegisterLLZK.*Passes",
"RegisterPCL.*Passes",
],
&[
"OpBuilder",
"OpBuilderListener",
"Notify(Operation|Block)Inserted",
"(Op|Block)BuilderInsertPoint",
"ValueRange",
],
)
}
fn run() -> Result<()> {
let out_dir = env::var("OUT_DIR").context("OUT_DIR environment variable")?;
let llzk_prefix = format!("LLZK_SYS_{LLZK_MAJOR_VERSION}0_PREFIX");
let llzk_dir = env::var(&llzk_prefix).context(format!("{llzk_prefix} environment variable"))?;
let default_cfg = create_default_cfg();
default_cfg.emit_cargo_commands()?;
let cfg = (
&default_cfg,
WrapStaticFns::new(Path::new(&out_dir)),
link_llzk(PathBuf::from(llzk_dir))?,
);
let bindings_path = Path::new(&out_dir).join("bindings.rs");
let source = cfg.generate()?.to_string();
fs::write(bindings_path, suppress_missing_docs(&source))?;
cfg.try_compile("llzk-sys-cc")
}
fn suppress_missing_docs(source: &str) -> String {
let mut result = String::with_capacity(source.len());
for line in source.lines() {
let trimmed = line.trim_start();
let indent = &line[..line.len() - trimmed.len()];
let needs_allow = trimmed.starts_with("pub fn mlir")
|| trimmed
.strip_prefix("pub type ")
.and_then(|rest| rest.split_once('='))
.is_some_and(|(name, ty)| {
let name = name.trim();
let ty = ty.trim().trim_end_matches(';');
name.starts_with("Llzk")
&& matches!(
ty,
"::std::os::raw::c_uint"
| "::std::os::raw::c_int"
| "::std::os::raw::c_uchar"
| "::std::os::raw::c_ushort"
| "::std::os::raw::c_ulong"
| "::std::os::raw::c_ulonglong"
| "::std::os::raw::c_schar"
| "::std::os::raw::c_short"
| "::std::os::raw::c_long"
| "::std::os::raw::c_longlong"
)
})
|| (trimmed.starts_with("impl") && trimmed.to_ascii_lowercase().contains("bindgen"))
|| trimmed
.strip_prefix("pub struct ")
.and_then(|rest| rest.split_whitespace().next())
.is_some_and(|name| name.to_ascii_lowercase().contains("bindgen"))
|| trimmed
.strip_prefix("pub ")
.and_then(|rest| {
let colon = rest.find(':')?;
let ident = &rest[..colon];
if ident.contains(' ') || ident.contains('(') {
return None;
}
Some(ident.to_ascii_lowercase().contains("bindgen"))
})
.unwrap_or(false);
if needs_allow {
result.push_str(indent);
result.push_str("#[allow(missing_docs)]\n");
}
result.push_str(line);
result.push('\n');
}
result
}
fn feature_is_enabled(feature: &str) -> bool {
env::var_os(format!(
"CARGO_FEATURE_{}",
feature.to_uppercase().replace("-", "_")
))
.is_some()
}
fn main() {
if let Err(err) = run() {
println!("cargo:error={err:#}");
std::process::exit(1);
}
}