extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Literal;
use syn::{parse_macro_input, LitStr};
use quote::quote;
use std::{
env,
env::consts,
fs::{self, File},
io::{Write, Cursor},
path::{Path, PathBuf},
};
mod file_extraction;
use file_extraction::{
FileType,
extract_tgz,
extract_zip,
zip_dir,
DylibName
};
fn get_onnxruntime_url(onnx_version: &str) -> (String, String, String, DylibName, FileType) {
let base_url = format!(
"https://github.com/microsoft/onnxruntime/releases/download/v{}/",
onnx_version
);
match (consts::OS, consts::ARCH) {
("linux", "x86_64") => (
format!("{}onnxruntime-linux-x64-{}.tgz", base_url, onnx_version),
format!("onnxruntime-linux-x64-{}", onnx_version),
"tgz".to_string(),
DylibName::So,
FileType::Tgz
),
("linux", "aarch64") => (
format!("{}onnxruntime-linux-aarch64-{}.tgz", base_url, onnx_version),
format!("onnxruntime-linux-aarch64-{}", onnx_version),
"tgz".to_string(),
DylibName::So,
FileType::Tgz
),
("macos", "x86_64") => (
format!("{}onnxruntime-osx-x86_64-{}.tgz", base_url, onnx_version),
format!("onnxruntime-osx-x86_64-{}", onnx_version),
"tgz".to_string(),
DylibName::Dylib,
FileType::Tgz
),
("macos", "aarch64") => (
format!("{}onnxruntime-osx-arm64-{}.tgz", base_url, onnx_version),
format!("onnxruntime-osx-arm64-{}", onnx_version),
"tgz".to_string(),
DylibName::Dylib,
FileType::Tgz
),
("windows", "x86_64") => (
format!("{}onnxruntime-win-x64-{}.zip", base_url, onnx_version),
format!("onnxruntime-win-x64-{}", onnx_version),
"zip".to_string(),
DylibName::Dll,
FileType::Zip
),
("windows", "aarch64") => (
format!("{}onnxruntime-win-arm64-{}.zip", base_url, onnx_version),
format!("onnxruntime-win-arm64-{}", onnx_version),
"zip".to_string(),
DylibName::Dll,
FileType::Zip
),
_ => panic!(
"Unsupported platform or architecture: {} {}",
consts::OS,
consts::ARCH
),
}
}
#[proc_macro]
pub fn embed_onnx(attr: TokenStream) -> TokenStream {
let input = parse_macro_input!(attr as LitStr);
let supported_versions = ["1.20.0"];
let onnx_version = match input.value().as_str() {
"1.20.0" => "1.20.0",
_ => panic!(
"{} passed in as version, only the following versions are supported: {:?}",
input.value(), supported_versions
)
};
let (url, package_name, ext, dylib_name, file_type) = get_onnxruntime_url(
onnx_version
);
let target_root = std::env::var("CARGO_TARGET_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| {
Path::new(&env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("crate not in a workspace?")
.join("target")
});
let cache = target_root.join("onnxruntime_cache").join(onnx_version);
fs::create_dir_all(&cache).expect("failed to create cache dir");
let filename = format!("{}.{}", package_name, ext);
let download_path = cache.join(&filename);
let extract_target = cache.join(&package_name);
let lib_path = extract_target.join("lib");
let dylib_name_str: &str = dylib_name.clone().into();
let dylib_path = lib_path.join(dylib_name_str);
let lock_path = cache.join("onnx_download.lock");
let mut lock = fslock::LockFile::open(&lock_path).expect("Failed to open lock file");
lock.lock().expect("Failed to acquire download lock");
if !download_path.exists() {
println!("Downloading ONNX Runtime from {}", url);
let response = reqwest::blocking::get(&url)
.expect("Failed to download ONNX Runtime")
.bytes()
.expect("Failed to read ONNX Runtime response");
let mut file = File::create(&download_path).expect("Failed to create ONNX file");
file.write_all(&response).expect("Failed to write ONNX file");
println!("Saved to {}", download_path.display());
}
if !dylib_path.exists() {
match file_type {
FileType::Tgz => extract_tgz(&download_path, &cache).expect("Failed to extract ONNX archive for tgz"),
FileType::Zip => extract_zip(&download_path, &cache).expect("Failed to extract ONNX archive for zip")
};
}
let mut buffer = Cursor::new(Vec::new());
zip_dir(&lib_path, &mut buffer).expect("Failed to zip directory");
let raw_bytes = buffer.into_inner();
lock.unlock().expect("Failed to release download lock");
let byte_string = Literal::byte_string(&raw_bytes);
let tokens = quote! {
#byte_string
};
TokenStream::from(tokens)
}