use std::{ process::{exit, Command}, str, }; #[cfg(feature = "bindings")] use std::{ fs, io::{self, prelude::*}, path, }; static CARGOENV: &str = "cargo:rustc-env="; #[cfg(feature = "bindings")] #[allow(clippy::trivial_regex)] fn prepreprocess(path: &path::Path) -> io::Result { use lazy_static::lazy_static; use regex::{Captures, Regex, RegexBuilder, Replacer}; println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); lazy_static! { static ref INCLUDE_RE: Regex = RegexBuilder::new("^( *)#include \"([^\"]+)\".*\n") .multi_line(true) .build() .unwrap(); static ref SOL_RE: Regex = RegexBuilder::new("^").multi_line(true).build().unwrap(); static ref TRIM_EOL: Regex = RegexBuilder::new(" +$").multi_line(true).build().unwrap(); } struct PrePreprocessReplacer<'t> { base: &'t path::Path, } impl<'t> Replacer for PrePreprocessReplacer<'t> { fn replace_append(&mut self, caps: &Captures, dst: &mut String) { let indent = caps.get(1).unwrap().as_str(); let fname = path::Path::new(caps.get(2).unwrap().as_str()); let text = prepreprocess(&self.base.join(fname)) .unwrap_or_else(|_| prepreprocess(fname).unwrap()); let text = SOL_RE.replace_all(&text, indent); let text = TRIM_EOL.replace_all(&text, ""); dst.push_str(&text); } } let mut contents = String::new(); io::BufReader::new(fs::File::open(path)?).read_to_string(&mut contents)?; Ok(INCLUDE_RE .replace_all( &contents, PrePreprocessReplacer { base: path.parent().unwrap_or_else(|| path::Path::new(".")), }, ) .to_string()) } #[cfg(feature = "bindings")] fn cbindgen() { use std::env; let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let out_dir = format!( "{}/../target/include", env::var("CARGO_MANIFEST_DIR").unwrap() ); let trailer = include_str!("../src/bindings/trailer.inc"); let header_c = include_str!("../src/bindings/header_c.inc"); let header_cpp = include_str!("../src/bindings/header_cpp.inc"); // Generate C headers. cbindgen::Builder::new() .with_crate(crate_dir.clone()) .with_language(cbindgen::Language::C) .with_no_includes() .with_header(header_c) .with_trailer(trailer) .generate() .expect("Unable to generate bindings") .write_to_file(format!("{}/dqcsim.h", out_dir)); // Generate C++ minimal API headers. cbindgen::Builder::new() .with_crate(crate_dir.clone()) .with_language(cbindgen::Language::Cxx) .with_no_includes() .with_header(header_cpp) .with_namespaces(&["dqcsim", "raw"]) .with_trailer(trailer) .generate() .expect("Unable to generate bindings") .write_to_file(format!("{}/cdqcsim", out_dir)); // Generate C++ extended API headers. fs::write( format!("{}/dqcsim", out_dir), prepreprocess(path::Path::new("../cpp/include/dqcsim")).unwrap(), ) .unwrap(); // Generate SWIG header. cbindgen::Builder::new() .with_crate(crate_dir) .with_language(cbindgen::Language::C) .with_line_length(100_000) .with_documentation(false) .generate() .expect("Unable to generate bindings") .write_to_file(format!("{}/dqcsim-py.h", out_dir)); } fn main() { let time_c = Command::new("date").args(&["+%F %T"]).output(); match time_c { Ok(t) => { let time; unsafe { time = str::from_utf8_unchecked(&t.stdout); } println!("{}COMPILED_AT={}", CARGOENV, time); } Err(_) => exit(1), } #[cfg(feature = "bindings")] cbindgen(); }