use std::{ env, io::Read, path::{Path, PathBuf}, }; use anyhow::{bail, ensure, Result}; mod config; fn main() -> Result<()> { println!("cargo:rerun-if-changed=build.rs"); if env::var("CARGO_FEATURE_DOWNLOAD").is_err() { return Ok(()); } let workdir = create_workdir()?; let config = create_config()?; let url = config.url(); let data = https_get(&url)?; unzip_to_path(&url, data, &workdir)?; println!("cargo:rustc-link-search=native={}", workdir.display()); for (path, lib) in walkdir::WalkDir::new(&workdir) .into_iter() .filter_map(|entry| entry.ok()) .filter_map(|entry| match entry.file_name().to_str() { Some(name) => Some((name.to_string(), entry.into_path())), None => None, }) .filter_map(|(name, path)| { config .libs() .into_iter() .find(|lib| name == *lib) .map(|lib| (path, lib)) }) { let name = format!("github_com_zzwxh_libwebp2_to_avoid_name_conflicts_{}", lib); std::fs::rename(path, workdir.join(&name))?; println!("cargo:rustc-link-lib=static:+verbatim={}", name); } Ok(()) } fn create_config() -> Result> { let arch = env::var("CARGO_CFG_TARGET_ARCH")?; let os = env::var("CARGO_CFG_TARGET_OS")?; let config: Box = match (&*os, &*arch) { ("linux", "x86_64") => Box::new(config::LinuxX86_64), ("linux", "aarch64") => Box::new(config::LinuxAarch64), ("macos", "x86_64") => Box::new(config::MacosX86_64), ("macos", "aarch64") => Box::new(config::MacosAarch64), ("windows", "x86_64") => Box::new(config::WindowsX86_64), _ => bail!("unsupported os or arch"), }; Ok(config) } fn https_get(url: &str) -> Result> { use reqwest::blocking::Client; let response = Client::builder() .use_rustls_tls() .https_only(true) .timeout(Some(std::time::Duration::from_secs(10))) .build()? .get(url) .send()?; ensure!(response.status().is_success()); Ok(response.bytes()?.to_vec()) } fn create_workdir() -> Result { let root = PathBuf::from(env::var("OUT_DIR")?) .canonicalize()? .join("root"); if root.exists() { std::fs::remove_dir_all(&root)?; } std::fs::create_dir(&root)?; Ok(root) } fn unzip_to_path(url: &str, data: Vec, path: impl AsRef) -> Result<()> { use flate2::read::GzDecoder; use tar::Archive; use zip::ZipArchive; if url.ends_with(".tar.gz") { let mut tar = Vec::new(); GzDecoder::new(&*data).read_to_end(&mut tar)?; Archive::new(&*tar).unpack(path)?; } else if url.ends_with(".zip") { let reader = std::io::Cursor::new(data); ZipArchive::new(reader)?.extract(path)?; } else { bail!("unsupported file"); } Ok(()) }