use std::{ io::{Cursor, Read}, path::PathBuf, }; use tokio::{fs, task}; use crate::standalone::metadata::CURRENT_EXE; use super::{ files::write_executable_file_to, result::{BuildError, BuildResult}, target::{BuildTarget, CACHE_DIR}, }; /// Discovers the path to the base executable to use for cross-compilation, and downloads it if necessary pub async fn get_or_download_base_executable(target: BuildTarget) -> BuildResult { // If the target matches the current system, just use the current executable if target.is_current_system() { return Ok(CURRENT_EXE.to_path_buf()); } // If a cached target base executable doesn't exist, attempt to download it if !target.cache_path().exists() { return Ok(target.cache_path()); } // The target is not cached, we must download it println!("Requested target '{target}' does not exist in cache"); let version = env!("CARGO_PKG_VERSION"); let target_triple = format!("lune-{version}-{target}"); let release_url = format!( "{base_url}/v{version}/{target_triple}.zip", base_url = "https://github.com/lune-org/lune/releases/download", ); // NOTE: This is not entirely accurate, but it is clearer for a user println!("Downloading {target_triple}{}...", target.exe_suffix()); // Try to request to download the zip file from the target url, // making sure transient errors are handled gracefully and // with a different error message than "not found" let response = reqwest::get(release_url).await?; if !response.status().is_success() { if response.status().as_u16() == 404 { return Err(BuildError::ReleaseTargetNotFound(target)); } return Err(BuildError::Download( response.error_for_status().unwrap_err(), )); } // Receive the full zip file let zip_bytes = response.bytes().await?.to_vec(); let zip_file = Cursor::new(zip_bytes); // Look for and extract the binary file from the zip file // NOTE: We use spawn_blocking here since reading a zip // archive is a somewhat slow / blocking operation let binary_file_name = format!("lune{}", target.exe_suffix()); let binary_file_handle = task::spawn_blocking(move || { let mut archive = zip_next::ZipArchive::new(zip_file)?; let mut binary = Vec::new(); archive .by_name(&binary_file_name) .or(Err(BuildError::ZippedBinaryNotFound(binary_file_name)))? .read_to_end(&mut binary)?; Ok::<_, BuildError>(binary) }); let binary_file_contents = binary_file_handle.await??; // Finally, write the extracted binary to the cache if !CACHE_DIR.exists() { fs::create_dir_all(CACHE_DIR.as_path()).await?; } write_executable_file_to(target.cache_path(), binary_file_contents).await?; println!("Downloaded successfully and added to cache"); Ok(target.cache_path()) }