// build.rs - Download and merge gradle-wrapper jars at build time use std::path::PathBuf; use std::fs; use std::io::{Read, Write, Cursor}; fn main() { println!("cargo:rerun-if-changed=build.rs"); let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); let gradle_wrapper_path = out_dir.join("gradle-wrapper.jar"); // Check if gradle-wrapper.jar already exists in resources/ (manually prepared) if let Ok(content) = fs::read("resources/gradle-wrapper.jar") { eprintln!("Using existing resources/gradle-wrapper.jar"); fs::write(&gradle_wrapper_path, content) .expect("Failed to copy gradle-wrapper.jar"); return; } // Otherwise, download and merge at build time eprintln!("Downloading and merging gradle-wrapper jars..."); if let Err(e) = download_and_merge_wrapper(&gradle_wrapper_path) { eprintln!("\nError downloading gradle-wrapper: {}\n", e); eprintln!("You can manually create resources/gradle-wrapper.jar and rebuild."); std::process::exit(1); } eprintln!("✓ gradle-wrapper.jar ready"); } fn download_and_merge_wrapper(output_path: &PathBuf) -> Result<(), Box> { use std::io::Cursor; let url = "https://services.gradle.org/distributions/gradle-8.9-bin.zip"; // Download the distribution let response = ureq::get(url) .timeout(std::time::Duration::from_secs(120)) .call()?; let mut bytes = Vec::new(); response.into_reader().read_to_end(&mut bytes)?; // Open the zip let cursor = Cursor::new(bytes); let mut archive = zip::ZipArchive::new(cursor)?; // Extract ALL jars from lib/ (except sources) let mut all_jar_bytes = Vec::new(); for i in 0..archive.len() { let file = archive.by_index(i)?; let name = file.name().to_string(); // Include all .jar files from lib/ or lib/plugins/ (but not sources) if (name.starts_with("gradle-8.9/lib/") || name.starts_with("gradle-8.9/lib/plugins/")) && name.ends_with(".jar") && !name.contains("-sources") && !name.contains("-src") { drop(file); let jar_bytes = extract_file(&mut archive, &name)?; all_jar_bytes.push(jar_bytes); } } if all_jar_bytes.is_empty() { return Err("No gradle jars found".into()); } eprintln!("Merging {} gradle jars into wrapper...", all_jar_bytes.len()); // Merge all jars merge_multiple_jars(all_jar_bytes, output_path)?; Ok(()) } fn extract_file(archive: &mut zip::ZipArchive>>, path: &str) -> Result, Box> { let mut file = archive.by_name(path)?; let mut bytes = Vec::new(); file.read_to_end(&mut bytes)?; Ok(bytes) } fn merge_multiple_jars(jar_bytes_list: Vec>, output_path: &PathBuf) -> Result<(), Box> { use std::io::Cursor; use std::collections::HashSet; let output_file = fs::File::create(output_path)?; let mut zip_writer = zip::ZipWriter::new(output_file); let options = zip::write::FileOptions::<()>::default() .compression_method(zip::CompressionMethod::Deflated); let mut added_files = HashSet::new(); // Process each jar for jar_bytes in jar_bytes_list { let cursor = Cursor::new(jar_bytes); let mut archive = zip::ZipArchive::new(cursor)?; for i in 0..archive.len() { let mut file = archive.by_index(i)?; let name = file.name().to_string(); // Skip META-INF, directories, and duplicates if !name.starts_with("META-INF/") && !name.ends_with('/') && !added_files.contains(&name) { let mut contents = Vec::new(); file.read_to_end(&mut contents)?; zip_writer.start_file(&name, options)?; zip_writer.write_all(&contents)?; added_files.insert(name); } } } zip_writer.finish()?; Ok(()) }