From 11512e92ea414ac98d0e1835062871483fdc08a1 Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Sun, 18 Aug 2024 13:44:49 +1000 Subject: [PATCH] xtask: handle triples better --- Cargo.lock | 14 +++- xtask/Cargo.toml | 1 + xtask/src/args.rs | 8 +++ xtask/src/main.rs | 164 ++++++++++++++++++++++++++++++++++++--------- xtask/src/types.rs | 54 +++++++++++++-- 5 files changed, 201 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c2f9b11..9d1f8a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -740,6 +740,15 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cfg-expr" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345c78335be0624ed29012dc10c49102196c6882c12dde65d9f35b02da2aada8" +dependencies = [ + "smallvec", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -4558,7 +4567,7 @@ version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ - "cfg-expr", + "cfg-expr 0.15.8", "heck", "pkg-config", "toml 0.8.15", @@ -4571,7 +4580,7 @@ version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c81f13d9a334a6c242465140bd262fae382b752ff2011c4f7419919a9c97922" dependencies = [ - "cfg-expr", + "cfg-expr 0.15.8", "heck", "pkg-config", "toml 0.8.15", @@ -5875,6 +5884,7 @@ name = "xtask" version = "0.1.0" dependencies = [ "cargo_metadata", + "cfg-expr 0.16.0", "clap", "env_logger", "log", diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 6520fa2..1335f9d 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -10,6 +10,7 @@ clap = { version = "4.5.15", features = ["derive"] } strum = { version = "0.26.3", features = ["derive"] } xshell = "0.2.6" cargo_metadata = "0.18.1" +cfg-expr = "0.16.0" nih_plug_xtask = { workspace = true } log = { workspace = true } env_logger = { workspace = true } diff --git a/xtask/src/args.rs b/xtask/src/args.rs index 4496ab7..1c07cb2 100644 --- a/xtask/src/args.rs +++ b/xtask/src/args.rs @@ -31,12 +31,18 @@ pub enum Commands { #[derive(clap::Args, Debug)] #[command(group(clap::ArgGroup::new("platforms").args(["platform","all_platforms"]).required(true)))] #[command(group(clap::ArgGroup::new("binaries").args(["binary","all_binaries"]).required(true)))] +#[command(group(clap::ArgGroup::new("architectures").args(["architecture","all_architectures"]).required(true)))] pub struct BuildArgs { #[arg(long, short)] pub platform: Vec, #[arg(long)] pub all_platforms: bool, + #[arg(long, short)] + pub architecture: Vec, + #[arg(long)] + pub all_architectures: bool, + #[arg(long, short)] pub binary: Vec, #[arg(long)] @@ -51,6 +57,8 @@ pub struct RunArgs { #[arg(long, short)] pub platform: Option, #[arg(long, short)] + pub architecture: Option, + #[arg(long, short)] pub binary: Option, #[arg(long, short)] pub renderer: Option, diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 6fe094a..6863da7 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,3 +1,5 @@ +use std::io::BufRead; + use clap::Parser; use strum::IntoEnumIterator; use xshell::Shell; @@ -10,18 +12,20 @@ mod types; static METADATA: std::sync::OnceLock = std::sync::OnceLock::new(); static OUTPUT_DIR: std::sync::OnceLock = std::sync::OnceLock::new(); -fn executable_dir(binary: Binary, platform: Platform, renderer: Renderer) -> String { - format!( - "{}_{}_{}", - binary.name(), - platform.as_target(), - renderer.as_feature() - ) +fn executable_dir( + binary: Binary, + triple: &cfg_expr::targets::Triple, + renderer: Renderer, +) -> String { + format!("{}_{}_{}", binary.name(), triple, renderer.as_feature()) } fn main() -> Result<(), Box> { if std::env::var("RUST_LOG").is_err() { - std::env::set_var("RUST_LOG", "info"); + std::env::set_var( + "RUST_LOG", + "cli=info,frontend_common=info,gb_emu_lib=info,xtask=info", + ); } env_logger::init(); let args = Args::parse(); @@ -55,27 +59,28 @@ fn build(args: BuildArgs, debug: bool) -> Result<(), Box> } else { args.platform }; - log::info!("platforms: {platforms:?}"); + + let architectures = if args.all_architectures { + Architecture::iter().collect() + } else { + args.architecture + }; let binaries = if args.all_binaries { Binary::iter().collect() } else { args.binary }; - log::info!("binaries: {binaries:?}"); let renderers = if args.renderer.is_empty() { vec![Renderer::Wgpu] } else { args.renderer }; - log::info!("renderers: {renderers:?}"); for binary in binaries { - for platform in &platforms { - for renderer in &renderers { - build_binary(binary, *platform, *renderer, debug)?; - } + for renderer in &renderers { + build_binary(binary, &platforms, &architectures, *renderer, debug)?; } } @@ -84,11 +89,16 @@ fn build(args: BuildArgs, debug: bool) -> Result<(), Box> fn run(args: RunArgs, debug: bool) -> Result<(), Box> { let platform = args.platform.unwrap_or(Platform::host_platform()); + let architecture = args + .architecture + .unwrap_or(Architecture::host_architecture()); + let triple = get_triple(platform, architecture).unwrap().triple; let binary = args.binary.unwrap_or(Binary::get_default()); let renderer = args.renderer.unwrap_or(Renderer::Wgpu); + match binary { - Binary::Gui => run_gui(platform, renderer, debug), - Binary::Cli => run_cli(platform, renderer, debug), + Binary::Gui => run_gui(platform, &triple, renderer, debug), + Binary::Cli => run_cli(platform, &triple, renderer, debug), Binary::Vst => { eprintln!("twinc_emu_vst doesn't support standalone usage!"); std::process::exit(1); @@ -98,26 +108,81 @@ fn run(args: RunArgs, debug: bool) -> Result<(), Box> { fn build_binary( binary: Binary, - platform: Platform, + platforms: &[Platform], + architectures: &[Architecture], renderer: Renderer, debug: bool, ) -> Result<(), Box> { - let output_dir = OUTPUT_DIR - .get() - .unwrap() - .join(executable_dir(binary, platform, renderer)); - - std::fs::create_dir_all(&output_dir)?; match binary { - Binary::Gui => build_gui(output_dir, platform, renderer, debug), - Binary::Cli => build_cli(output_dir, platform, renderer, debug), - Binary::Vst => build_vst(platform, renderer, debug), + Binary::Gui => { + for (platform, triple) in platforms_and_triples(platforms, architectures) { + let output_dir = OUTPUT_DIR + .get() + .unwrap() + .join(executable_dir(binary, &triple, renderer)); + + std::fs::create_dir_all(&output_dir)?; + build_gui(output_dir, platform, &triple, renderer, debug)?; + } + } + Binary::Cli => { + for (platform, triple) in platforms_and_triples(platforms, architectures) { + let output_dir = OUTPUT_DIR + .get() + .unwrap() + .join(executable_dir(binary, &triple, renderer)); + + std::fs::create_dir_all(&output_dir)?; + build_cli(output_dir, platform, &triple, renderer, debug)?; + } + } + Binary::Vst => { + log::warn!("TODO: handle vst builds more smarter"); + for platform in platforms { + if *platform == Platform::Mac { + build_vst(*platform, None, renderer, debug)?; + } else { + for triple in platform_triples(platform, architectures) { + build_vst(*platform, Some(&triple), renderer, debug)?; + } + } + } + } } + Ok(()) +} + +fn platform_triples<'a>( + platform: &'a Platform, + architectures: &'a [Architecture], +) -> impl Iterator + 'a { + architectures + .iter() + .filter_map(|architecture| get_triple(*platform, *architecture).map(|v| v.triple)) + .filter(|triple| { + if is_toolchain_installed(triple) { + true + } else { + log::warn!("toolchain not installed for {triple} - skipping"); + false + } + }) +} + +fn platforms_and_triples( + platforms: &[Platform], + architectures: &[Architecture], +) -> Vec<(Platform, cfg_expr::targets::Triple)> { + platforms + .iter() + .flat_map(|platform| platform_triples(platform, architectures).map(|v| (*platform, v))) + .collect() } fn build_gui( output_dir: std::path::PathBuf, platform: Platform, + triple: &cfg_expr::targets::Triple, renderer: Renderer, debug: bool, ) -> Result<(), Box> { @@ -126,6 +191,7 @@ fn build_gui( run_build( "gui", platform, + triple, renderer, debug, Some(["-F", ui].into_iter().map(String::from).collect()), @@ -135,6 +201,7 @@ fn build_gui( fn run_gui( platform: Platform, + triple: &cfg_expr::targets::Triple, renderer: Renderer, debug: bool, ) -> Result<(), Box> { @@ -144,6 +211,7 @@ fn run_gui( "run", "gui", platform, + triple, renderer, debug, Some(["-F", ui].into_iter().map(String::from).collect()), @@ -154,14 +222,16 @@ fn run_gui( fn build_cli( output_dir: std::path::PathBuf, platform: Platform, + triple: &cfg_expr::targets::Triple, renderer: Renderer, debug: bool, ) -> Result<(), Box> { - run_build("cli", platform, renderer, debug, None, output_dir) + run_build("cli", platform, triple, renderer, debug, None, output_dir) } fn run_cli( platform: Platform, + triple: &cfg_expr::targets::Triple, renderer: Renderer, debug: bool, ) -> Result<(), Box> { @@ -171,13 +241,22 @@ fn run_cli( .position(|arg| arg == "--") .map(|extra_args_index| args[extra_args_index..].to_vec()); - let _ = cargo_exec("run", "cli", platform, renderer, debug, additional_flags)?; + let _ = cargo_exec( + "run", + "cli", + platform, + triple, + renderer, + debug, + additional_flags, + )?; Ok(()) } fn run_build( package: &str, platform: Platform, + triple: &cfg_expr::targets::Triple, renderer: Renderer, debug: bool, additional_flags: Option>, @@ -187,6 +266,7 @@ fn run_build( "build", package, platform, + triple, renderer, debug, additional_flags, @@ -200,10 +280,26 @@ fn run_build( Ok(()) } +static INSTALLED_TRIPLES: std::sync::LazyLock> = std::sync::LazyLock::new(|| { + let sh = Shell::new().expect("failed to open shell"); + match xshell::cmd!(sh, "rustup target list --installed").output() { + Ok(output) => output.stdout.lines().map_while(Result::ok).collect(), + Err(e) => { + log::error!("Error listing installed targets: {e:?}"); + std::process::exit(0); + } + } +}); + +fn is_toolchain_installed(triple: &cfg_expr::targets::Triple) -> bool { + INSTALLED_TRIPLES.contains(&triple.to_string()) +} + fn cargo_exec( verb: &str, package: &str, - platform: Platform, + _platform: Platform, + triple: &cfg_expr::targets::Triple, renderer: Renderer, debug: bool, additional_flags: Option>, @@ -212,11 +308,11 @@ fn cargo_exec( Box, > { let sh = Shell::new()?; - let target = platform.as_target(); + let release = if debug { "" } else { "--release" }; let renderer = renderer.as_feature(); - let args=format!("{verb} -p {package} --target {target} {release} --no-default-features -F {renderer} --message-format=json"); + let args=format!("{verb} -q -p {package} --target {triple} {release} --no-default-features -F {renderer} --message-format=json"); let args = args.split_whitespace().map(|s| s.to_string()); let args = if let Some(additional_flags) = additional_flags { args.chain(additional_flags).collect::>() @@ -241,13 +337,15 @@ fn as_artifact(message: cargo_metadata::Message) -> Option, renderer: Renderer, debug: bool, ) -> Result<(), Box> { + let triple_str = triple.map(|t| t.to_string()); let mut args = if platform == Platform::Mac { vec!["bundle-universal"] } else { - vec!["bundle", "--target", platform.as_target()] + vec!["bundle", "--target", triple_str.as_ref().unwrap().as_str()] }; if !debug { args.push("--release"); diff --git a/xtask/src/types.rs b/xtask/src/types.rs index 4304ae2..01c8344 100644 --- a/xtask/src/types.rs +++ b/xtask/src/types.rs @@ -1,3 +1,5 @@ +use cfg_expr::expr::TargetMatcher; + #[derive(clap::ValueEnum, Clone, Copy, Debug, strum::EnumIter, PartialEq, Eq)] pub enum Platform { Windows, @@ -6,11 +8,11 @@ pub enum Platform { } impl Platform { - pub fn as_target(&self) -> &str { + pub fn as_cargo(&self) -> cfg_expr::targets::Os { match self { - Platform::Windows => "x86_64-pc-windows-gnu", - Platform::Linux => "x86_64-unknown-linux-gnu", - Platform::Mac => "aarch64-apple-darwin", + Platform::Windows => cfg_expr::targets::Os::windows, + Platform::Linux => cfg_expr::targets::Os::linux, + Platform::Mac => cfg_expr::targets::Os::macos, } } @@ -29,7 +31,49 @@ impl Platform { } else if cfg!(target_os = "linux") { Self::Linux } else { - todo!() + unimplemented!() + } + } +} + +pub fn get_triple( + platform: Platform, + architecture: Architecture, +) -> Option { + cfg_expr::targets::ALL_BUILTINS + .iter() + .find(|b| { + b.matches(&cfg_expr::TargetPredicate::Arch(architecture.as_cargo())) + && b.matches(&cfg_expr::TargetPredicate::Os(platform.as_cargo())) + }) + .cloned() +} + +#[derive(clap::ValueEnum, Clone, Copy, Debug, strum::EnumIter, PartialEq, Eq)] +pub enum Architecture { + Aarch64, + X86, + X86_64, +} + +impl Architecture { + pub fn as_cargo(&self) -> cfg_expr::targets::Arch { + match self { + Architecture::Aarch64 => cfg_expr::targets::Arch::aarch64, + Architecture::X86_64 => cfg_expr::targets::Arch::x86_64, + Architecture::X86 => cfg_expr::targets::Arch::x86, + } + } + + pub fn host_architecture() -> Self { + if cfg!(target_arch = "x86_64") { + Self::X86_64 + } else if cfg!(target_arch = "aarch64") { + Self::Aarch64 + } else if cfg!(target_arch = "x86") { + Self::X86 + } else { + unimplemented!() } } }