use reqwest::blocking::get;
use std::env;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use tempfile::tempdir;
fn main() {
let dep_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
.parent()
.unwrap()
.join("lib/");
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let lib_subdir = get_target_triplet().unwrap();
let lib_dir = dep_dir.join(&lib_subdir);
let lib_out_dir = out_dir.join("lib/").join(&lib_subdir);
if !lib_dir.exists() {
#[cfg(feature = "download-wavify-core")]
download_and_extract_library(&lib_out_dir).unwrap();
} else {
copy_dir(lib_dir, &lib_out_dir).unwrap();
}
let has_linked = link_library("wavify_core", &lib_out_dir);
if !has_linked {
println!("cargo:warning=Linking wavify core failed")
}
let has_linked_tflitec = link_library("tensorflowlite_c", &lib_out_dir);
if !has_linked_tflitec {
println!("cargo:warning=Linking tflitec failed")
}
}
#[cfg(feature = "download-wavify-core")]
fn download_and_extract_library(target: &Path) -> io::Result<()> {
let base_url = "https://nlkytncvwapfujviszuq.supabase.co/storage/v1/object/public/lib/";
let target_triplet = get_target_triplet().unwrap();
let filename = target_triplet.to_owned() + ".tar.gz";
let url = base_url.to_owned() + &filename;
let temp_dir = tempdir()?;
let mut response = get(url).unwrap();
let archive_path = temp_dir.path().join("archive");
let mut archive_file = fs::File::create(&archive_path)?;
response.copy_to(&mut archive_file).unwrap();
let mut ar = tar::Archive::new(flate2::read::GzDecoder::new(fs::File::open(&archive_path)?));
ar.unpack(temp_dir.path())?;
let extracted_lib_dir = temp_dir.path().join("lib").join(target_triplet);
copy_dir(extracted_lib_dir, target)
}
fn link_library<T: std::fmt::Display>(name: T, search_path: &PathBuf) -> bool {
let libname = if cfg!(target_os = "windows") {
format!("{name}.dll")
} else if cfg!(target_os = "macos") {
format!("lib{name}.dylib")
} else {
format!("lib{name}.so")
};
if let Some(p) = find_library(libname, search_path) {
println!("cargo:rustc-link-search={}", p.display());
println!("cargo:rustc-link-lib={name}");
return true;
}
false
}
fn find_library<T: AsRef<Path>>(name: T, search_path: &PathBuf) -> Option<PathBuf> {
let maybe_lib_path = PathBuf::from(search_path);
if maybe_lib_path.exists() {
return Some(maybe_lib_path);
}
if let Ok(p) = env::var("LD_LIBRARY_PATH") {
if let Some(path) = p
.split(':')
.map(Path::new)
.find(|p| p.join(name.as_ref()).exists())
{
return Some(PathBuf::from(path));
}
}
None
}
fn copy_dir<U: AsRef<Path>, V: AsRef<Path>>(from: U, to: V) -> Result<(), io::Error> {
let mut stack = vec![PathBuf::from(from.as_ref())];
let output_root = PathBuf::from(to.as_ref());
let input_root = PathBuf::from(from.as_ref()).components().count();
while let Some(working_path) = stack.pop() {
let src: PathBuf = working_path.components().skip(input_root).collect();
let dest = if src.components().count() == 0 {
output_root.clone()
} else {
output_root.join(&src)
};
if fs::metadata(&dest).is_err() {
fs::create_dir_all(&dest)?;
}
for entry in fs::read_dir(working_path)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
stack.push(path);
} else if let Some(filename) = path.file_name() {
let dest_path = dest.join(filename);
fs::copy(&path, &dest_path)?;
}
}
}
Ok(())
}
fn get_target_triplet() -> anyhow::Result<String> {
let target_os = env::var("CARGO_CFG_TARGET_OS")?;
let target_arch = env::var("CARGO_CFG_TARGET_ARCH")?;
let triplet = match (target_os.as_str(), target_arch.as_str()) {
("linux", "x86_64") => "x86_64-unknown-linux-gnu",
("linux", "aarch64") => "aarch64-unknown-linux-gnu",
("android", "aarch64") => "aarch64-linux-android",
("windows", "x86_64") => "x86_64-pc-windows-gnu",
("macos", "aarch64") => "aarch64-apple-darwin",
_ => anyhow::bail!(
"Unsupported platform or architecture: {}, {}",
target_os,
target_arch
),
};
Ok(triplet.to_string())
}