use cmake; use cmake::Config; use curl::easy::Easy; use flate2::read::GzDecoder; use lazy_static::lazy_static; use log::*; use std::fs::File; use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; use std::{env, fs}; use tar::Archive; const VERSION: &str = "v3.0.0-alpha.2"; lazy_static! { static ref SOURCE_URL: String = format!( "https://github.com/GMLC-TDC/HELICS/releases/download/{ver}/Helics-{ver}-source.tar.gz", ver = VERSION, ); } #[derive(Clone, Debug, PartialEq)] enum Error { DownloadFailure { response_code: u32, url: String }, UrlFailure, IOError, } impl From for Error { fn from(_: std::io::Error) -> Error { Error::IOError } } impl From for Error { fn from(_: curl::Error) -> Error { Error::UrlFailure } } fn download_and_install() -> Result<(), Error> { let crate_dir = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap()); let target_dir = crate_dir.join("target"); debug!("target_dir = {:?}", &target_dir); if !target_dir.exists() { fs::create_dir(&target_dir).unwrap(); } let download_dir = target_dir.join(format!("helics-{}-source", VERSION)); debug!("download_dir = {:?}", &download_dir); if !download_dir.exists() { fs::create_dir(&download_dir).unwrap(); } // Build destination path let tarball_path = download_dir.join("helics.tar.gz"); debug!("tarball_path = {:?}", &tarball_path); let binary_url = &SOURCE_URL; download_tarball(&tarball_path, &binary_url)?; extract_tarball(tarball_path, &download_dir); // Configure and compile // We shall compile helics in the same mode we build the sys library. This will allow users // to debug the internals of helics more easily. let debug: bool = env::var("DEBUG").unwrap().parse().unwrap(); debug!("debug build? {}", debug); let mut config = Config::new(download_dir); if let Ok(s) = env::var("HELICS_CMAKE_GENERATOR") { config.generator(s); } let build = config.build(); let out_dir = env::var("OUT_DIR").unwrap(); println!("cargo:rustc-link-search=native={}/lib", build.display()); if cfg!(debug_assertions) { println!("cargo:rustc-link-lib=dylib=helicsd"); } else { println!("cargo:rustc-link-lib=dylib=helics"); }; println!("cargo:include={}/include", build.display()); println!("cargo:include={}/include/helics", build.display()); println!( "cargo:include={}/include/helics/shared_api_library", build.display() ); println!("cargo:outdir={}", out_dir); dbg!(build.display()); // https://rust-lang-nursery.github.io/rust-bindgen // https://docs.rs/bindgen let bindings = bindgen::Builder::default() .clang_arg(format!("-I{}/include/", build.display())) .clang_arg(format!("-I{}/include/helics/", build.display())) .clang_arg(format!( "-I{}/include/helics/shared_api_library/", build.display() )) .rustified_enum("*") .header(format!( "{}/include/helics/shared_api_library/helics.h", build.display() )) .generate() .expect("Unable to generate bindings"); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); bindings .write_to_file(out_path.join("bindings.rs")) .expect("Couldn't write bindings"); Ok(()) } /// Download a tarball if it doesn't already exist. fn download_tarball(tarball_path: &Path, binary_url: &str) -> Result<(), Error> { if !tarball_path.exists() { info!("Tarball doesn't exist, downloading..."); let f = File::create(tarball_path).unwrap(); let mut writer = BufWriter::new(f); let mut easy = Easy::new(); easy.follow_location(true)?; easy.url(binary_url).unwrap(); easy.write_function(move |data| Ok(writer.write(data).unwrap())) .unwrap(); easy.perform().unwrap(); let response_code = easy.response_code().unwrap(); if response_code != 200 { return Err(Error::DownloadFailure { response_code, url: binary_url.to_string(), }); } else { info!("Download successful!"); } } Ok(()) } fn extract_tarball + std::fmt::Debug, P2: AsRef + std::fmt::Debug>( archive_path: P, extract_to: P2, ) { info!( "Extracting tarball {:?} to {:?}", &archive_path, &extract_to ); let file = File::open(archive_path).unwrap(); let mut a = Archive::new(GzDecoder::new(file)); a.unpack(extract_to).unwrap(); } fn main() { println!("cargo:rerun-if-changed=build.rs"); download_and_install().unwrap(); }