use std::fs; use crate::commands::link::link; use crate::utils::parser::{self, env::get_install_env}; use crate::utils::shell::self; use super::{download, patch}; pub fn build(repo: &String, pkgname: &String) { let base = crate::commands::get_aeropkg_base(); let var = crate::commands::get_aeropkg_var(); let pkg_dir = base.join(repo).join(pkgname); let src_dir = pkg_dir.join("src"); fs::create_dir_all(&pkg_dir).ok(); let md_path = var.join(format!("{}/{}.md", repo, pkgname)); let built_md_path = pkg_dir.join("build-script.md"); let build_script = parser::get_build_script(&md_path) .unwrap_or_else(|e| panic!("Failed to parse build script: {}", e)); let full_env = get_install_env(repo, pkgname, &md_path, "build"); if is_build_up_to_date(&built_md_path, &build_script, &full_env, repo, pkgname) { update_built_link(&md_path, &built_md_path); return; } let applied_build = src_dir.join(".aeropkg.applied-build"); let applied_env = src_dir.join(".aeropkg.install-env"); if is_source_up_to_date(&applied_build, &applied_env, &build_script, &full_env) { update_built_link(&md_path, &built_md_path); return; } else if applied_build.exists() { fs::remove_dir_all(&src_dir).unwrap(); download::download(repo, pkgname); patch::patch(repo, pkgname); } save_env(&applied_env, &full_env); shell::run_install_script(&build_script, &src_dir, &full_env); shell::run_install_repo_stage_hook_script(repo, "build", &full_env); shell::run_install_stage_hook_script("download", &full_env); fs::create_dir_all(&pkg_dir).ok(); fs::write(&applied_build, &build_script).unwrap(); update_built_link(&md_path, &built_md_path); if full_env.get("save_source").is_none() { fs::remove_dir_all(&src_dir).ok(); } hook(&pkg_dir); if full_env.get("disable").is_some() { link(repo, pkgname); } else { let _ = fs::File::create(pkg_dir.join("disabled")); } } fn update_built_link(src: &std::path::Path, dest: &std::path::Path) { let _ = fs::remove_file(dest); if let Err(e) = fs::hard_link(src, dest) { eprintln!("Warning: Failed to update build-script.md link: {}", e); } } fn hook(pkg_dir: &std::path::Path) { let lib = pkg_dir.join("lib"); for d in ["lib64", "lib32"] { let dir = pkg_dir.join(d); crate::utils::fs::mv::mv(&dir, &lib).ok(); fs::remove_dir_all(&dir).ok(); } } fn is_build_up_to_date( md_path: &std::path::Path, script: &str, env: &std::collections::HashMap, repo: &String, pkgname: &String ) -> bool { if !md_path.exists() { return false; } let old_script = match parser::get_build_script(md_path) { Ok(s) => s, Err(_) => return false, }; script == old_script && env == &get_install_env(repo, pkgname, md_path, "build") } fn is_source_up_to_date( build_path: &std::path::Path, env_path: &std::path::Path, script: &str, env: &std::collections::HashMap ) -> bool { if !build_path.exists() { return false; } let old_script = fs::read_to_string(build_path).unwrap_or_default(); let old_env = load_env(env_path); script == old_script && env == &old_env } fn save_env(path: &std::path::Path, env: &std::collections::HashMap) { let mut lines: Vec<_> = env.iter().map(|(k, v)| format!("{}={}", k, v)).collect(); lines.sort(); fs::write(path, lines.join("\n")).ok(); } fn load_env(path: &std::path::Path) -> std::collections::HashMap { let content = fs::read_to_string(path).unwrap_or_default(); content.lines() .filter_map(|l| l.split_once('=').map(|(k, v)| (k.to_string(), v.to_string()))) .collect() }