use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
struct Builder {
include: String,
capi: PathBuf,
}
impl Builder {
fn new() -> Self {
let output = Command::new("yosys-config")
.args(["--datdir/include"])
.output()
.expect("failed to get yosys include dir");
let stdout = String::from_utf8_lossy(&output.stdout);
let include = stdout.trim().to_string();
let capi = Path::new(&include).join("backends/cxxrtl/cxxrtl_capi.cc");
Self { include, capi }
}
fn build(&self, source: PathBuf, dest: PathBuf) {
let cc_file = dest.with_extension("cc");
// touch(format!("/tmp/out/{}", tmp.file_name().unwrap().to_string_lossy()).as_str());
Command::new("yosys")
.args([
"-p",
&format!("write_cxxrtl -O0 {}", cc_file.to_string_lossy()),
])
.arg(&source)
.status()
.expect("cargo:waring=failed generate cxxrtl code");
Command::new("clang++")
.args(["-g", "-O3", "-fPIC", "-shared", "-std=c++14"])
.arg(format!("-I{}", &self.include))
.arg(&self.capi)
.arg(cc_file)
.arg("-o")
.arg(dest)
.status()
.expect("cargo:warning=failed generate cxxrtl code");
}
}
fn main() {
let builder = Builder::new();
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
let designs = PathBuf::from("designs");
println!("cargo:rerun-if-changed={}", designs.display());
for file in std::fs::read_dir(designs)
.into_iter()
.flat_map(|r| r.filter_map(|f| f.ok()))
{
if let Ok(metadata) = file.metadata() {
if metadata.is_file() {
let file = file.path();
if file.extension().and_then(|ext| ext.to_str()) == Some("sv") {
let file_name = file.file_stem().unwrap();
let dest = out.join(file_name).with_extension("so");
let modified = metadata.modified().unwrap();
let dest_modified = dest.metadata().and_then(|m| m.modified());
let duration = dest_modified
.ok()
.and_then(|m| m.duration_since(modified).ok());
if !dest.exists() || duration.is_none() {
builder.build(file, dest);
}
}
}
}
}
}