use std::{env, io::Read, path::PathBuf};
use anyhow::{bail, ensure, Result};
use flate2::read::GzDecoder;
use reqwest::{Client, IntoUrl};
use tar::Archive;
#[tokio::main]
async fn main() -> Result<()> {
println!("cargo:rerun-if-changed=build.rs");
if env::var("CARGO_FEATURE_DOWNLOAD").is_err() {
return Ok(());
}
let root = prep_workdir().await?;
let config = config()?;
let targz = get(config.url()).await?;
let tar = gz_unzip(&*targz)?;
for entry in Archive::new(&*tar).entries()? {
let mut entry = entry?;
let path = entry.path()?.to_path_buf();
for lib in config.libs() {
if path.ends_with(&lib) {
let libpath = root.join(&lib);
entry.unpack(&libpath)?;
println!("cargo:rustc-link-arg={}", libpath.display());
}
}
}
if env::var("CARGO_CFG_TARGET_OS")? == "linux" {
println!("cargo:rustc-link-arg=-lm");
}
Ok(())
}
trait Config {
fn url(&self) -> String;
fn libs(&self) -> Vec<String>;
}
struct LinuxX64;
impl Config for LinuxX64 {
fn url(&self) -> String {
"https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-linux-x86-64.tar.gz".to_string()
}
fn libs(&self) -> Vec<String> {
vec!["libwebp.a".to_string(), "libsharpyuv.a".to_string()]
}
}
struct LinuxAarch64;
impl Config for LinuxAarch64 {
fn url(&self) -> String {
"https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-linux-aarch64.tar.gz".to_string()
}
fn libs(&self) -> Vec<String> {
vec!["libwebp.a".to_string(), "libsharpyuv.a".to_string()]
}
}
struct MacosX64;
impl Config for MacosX64 {
fn url(&self) -> String {
"https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-mac-x86-64.tar.gz".to_string()
}
fn libs(&self) -> Vec<String> {
vec!["libwebp.a".to_string(), "libsharpyuv.a".to_string()]
}
}
struct MacosAarch64;
impl Config for MacosAarch64 {
fn url(&self) -> String {
"https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-mac-arm64.tar.gz".to_string()
}
fn libs(&self) -> Vec<String> {
vec!["libwebp.a".to_string(), "libsharpyuv.a".to_string()]
}
}
fn config() -> Result<Box<dyn Config>> {
let arch = env::var("CARGO_CFG_TARGET_ARCH")?;
let os = env::var("CARGO_CFG_TARGET_OS")?;
let config = match (&*os, &*arch) {
("linux", "x86_64") => Box::new(LinuxX64),
_ => bail!("unsupported os or arch"),
};
Ok(config)
}
async fn get(url: impl IntoUrl) -> Result<Vec<u8>> {
let response = Client::builder()
.use_rustls_tls()
.https_only(true)
.build()?
.get(url)
.send()
.await?;
ensure!(response.status().is_success());
Ok(response.bytes().await?.to_vec())
}
async fn prep_workdir() -> Result<PathBuf> {
let root = PathBuf::from(env::var("OUT_DIR")?)
.canonicalize()?
.join("root");
if root.exists() {
tokio::fs::remove_dir_all(&root).await?;
}
tokio::fs::create_dir(&root).await?;
Ok(root)
}
fn gz_unzip(r: impl Read) -> Result<Vec<u8>> {
let mut out = Vec::new();
GzDecoder::new(r).read_to_end(&mut out)?;
Ok(out)
}