mod cargo_cross; mod cargo_metadata; mod cargo_toml; mod exec; mod packer; mod upx; use cargo_metadata::CargoMetaData; use cargo_toml::CargoToml; use regex::Regex; use std::{ any::{Any, TypeId}, env, error::Error, fs, path::PathBuf, process::Command, }; trait Param { fn set(&mut self, s: String); } impl Param for String { fn set(&mut self, s: String) { *self = s; } } impl Param for Option { fn set(&mut self, s: String) { *self = match s.as_str() { "true" | "1" | "on" => Some(true), "false" | "0" | "off" => Some(false), _ => *self, }; } } impl Param for bool { fn set(&mut self, _: String) { *self = !*self; } } impl Param for Option { fn set(&mut self, s: String) { *self = Some(s); } } fn get_param( args: &mut Vec, key: &str, default: T, remove: bool, ) -> T { let mut value = default.clone(); match args.iter().position(|x| x == key) { Some(idx) => { if value.type_id() == TypeId::of::() { value.set(String::new()); if remove { args.remove(idx); } } else { if idx + 1 < args.len() { value.set(args[idx + 1].clone()); if remove { args.remove(idx); args.remove(idx); } } else { panic!("Missing value for {key}"); } } } None => (), } value } fn main() -> Result<(), Box> { // params let mut args: Vec = env::args().collect::>()[1..].to_vec(); if args.len() > 0 && args[0] == "zbuild" { args = args[1..].to_vec(); } let (active_channel, active_target, is_cross) = { let line = String::from_utf8_lossy( &Command::new("rustup") .args(["show", "active-toolchain"]) .output()? .stdout, ) .trim() .to_string(); let active_channel = line.split("-").next().unwrap().to_string(); let mut active_default_target = line.split(" ").next().unwrap()[active_channel.len() + 1..].to_string(); let re = Regex::new(r"^\d{4}-\d{2}-\d{2}-").unwrap(); active_default_target = re.replace(&active_default_target, "").to_string(); let active_target = get_param(&mut args, "--target", None, true).unwrap_or(active_default_target.clone()); let is_cross = active_target != active_default_target; (active_channel, active_target, is_cross) }; let is_darwin = active_target.contains("darwin"); let is_musl = active_target.contains("musl"); let upx_param = get_param(&mut args, "--upx", None, true).unwrap_or(true); let current_bin = env::current_exe()?; let exec_dir = current_bin.parent().unwrap(); let upx = upx::prepare(&exec_dir)?; cargo_cross::prepare()?; // get package name let mut cargo_toml = CargoToml::new::(None)?; let dist = cargo_toml.root.join("dist"); // get metadata let pname = get_param(&mut args, "-p", String::new(), false); let bin = get_param(&mut args, "--bin", String::new(), false); let mut metadata = CargoMetaData::new(&dist, &active_target)?; if !pname.is_empty() { metadata.member_filter(&pname); } if !bin.is_empty() { metadata.bin_filter(&bin); } // update release profile cargo_toml.get_bool(&vec!["profile", "release", "strip"], Some(true))?; cargo_toml.get_string(&vec!["profile", "release", "opt-level"], Some("z"))?; cargo_toml.get_integer(&vec!["profile", "release", "codegen-units"], Some(1))?; cargo_toml.get_bool(&vec!["profile", "release", "lto"], Some(true))?; cargo_toml.get_string(&vec!["profile", "release", "panic"], Some("abort"))?; cargo_toml.save(false)?; // cross config let cross_toml = cargo_toml.root.join("Cross.toml"); if !cross_toml.exists() { match tinyget::get("https://pages.ares.ctripcorp.com/rust/cross/Cross.toml").send() { Ok(res) => { if res.status_code == 200 { fs::write(&cross_toml, res.as_bytes())?; } } Err(_) => { fs::write(&cross_toml, include_bytes!("build/Cross.tmpl.toml"))?; } } } // icon metadata.prepare_icon()?; // build release { args.push("--target".to_string()); args.push(active_target); if active_channel == "nightly" { args.push("-Z".to_string()); args.push("build-std=std,panic_abort".to_string()); args.push("-Z".to_string()); args.push("build-std-features=panic_immediate_abort".to_string()); } else { println!("[BUILDER] Stable channel is used, size optimization is disabled"); } if is_cross && is_darwin { args.push(String::from("--config")); args.push(String::from("profile.release.strip=false")); println!("[BUILDER] Strip is disabled for cross build"); } let program = if is_cross { "cross" } else { "cargo" }; let mut command = Command::new(program); command.args(vec!["build", "--release"]).args(args); let mut rust_flags = Vec::::new(); if is_musl { rust_flags.push("-C".to_string()); rust_flags.push("target-feature=-crt-static".to_string()); println!("[BUILDER] Disable crt-static for musl"); } if active_channel == "nightly" { rust_flags.push("-Z".to_string()); rust_flags.push("location-detail=none".to_string()); } if !rust_flags.is_empty() { command.env("RUSTFLAGS", rust_flags.join(" ")); } let mut proc = command.spawn()?; let code = proc.wait()?.code().unwrap_or(-1); if code != 0 { panic!("Build Failed"); } } // copy to dist metadata.copy_to_dist(&upx, upx_param)?; Ok(()) }