diff --git a/Cargo.lock b/Cargo.lock index 758bffd1..7c60b05a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -563,6 +563,17 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "nih_plug_xtask" +version = "0.1.0" +dependencies = [ + "anyhow", + "goblin", + "reflink", + "serde", + "toml", +] + [[package]] name = "nix" version = "0.22.3" @@ -1153,9 +1164,5 @@ checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" name = "xtask" version = "0.1.0" dependencies = [ - "anyhow", - "goblin", - "reflink", - "serde", - "toml", + "nih_plug_xtask", ] diff --git a/Cargo.toml b/Cargo.toml index 11c25453..a8642215 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ repository = "https://github.com/robbert-vdh/nih-plugs" members = [ "nih_plug_derive", "nih_plug_egui", + "nih_plug_xtask", "xtask", "plugins/examples/gain", diff --git a/nih_plug_xtask/Cargo.toml b/nih_plug_xtask/Cargo.toml new file mode 100644 index 00000000..5f331bd0 --- /dev/null +++ b/nih_plug_xtask/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "nih_plug_xtask" +version = "0.1.0" +edition = "2021" +authors = ["Robbert van der Helm "] +description = "NIH-plug's cargo xtask command, as a library" +license = "ISC" + +[dependencies] +anyhow = "1.0" +goblin = "0.5" +# Version 0.1.3 from crates.io assumes a 64-bit toolchain +reflink = { git = "https://github.com/nicokoch/reflink", rev = "e8d93b465f5d9ad340cd052b64bbc77b8ee107e2" } +serde = { version = "1.0", features = ["derive"] } +toml = "0.5" diff --git a/nih_plug_xtask/README.md b/nih_plug_xtask/README.md new file mode 100644 index 00000000..46b14efa --- /dev/null +++ b/nih_plug_xtask/README.md @@ -0,0 +1,45 @@ +# NIH-plug: bundler and other utilities + +This is NIH-plug's `cargo xtask` command, but as a library. This way you can use +it in your own projects without forking this repo or copying the binary into +your own repo. This is necessary until Cargo supports [running binaries from +dependencies directly](https://github.com/rust-lang/rfcs/pull/3168). + +To use this, add an `xtask` binary to your project using `cargo new --bin xtask` +and add it to the Cargo workspace in your repository's main `Cargo.toml` file: + +```toml +# Cargo.toml + +[workspace] +members = ["xtask"] +``` + +Then add `nih_plug_xtask` to your new xtask package's dependencies: + +```toml +# xtask/Cargo.toml + +[dependencies] +nih_plug_xtask = { git = "https://github.com/robbert-vdh/nih-plug" } +``` + +Call `nih_plug_xtask`'s main function own xtask binary: + +```rust +# xtask/src/main.rs + +fn main() -> nih_plug_xtask::Result<()> { + nih_plug_xtask::main() +} +``` + +And finally create a `.cargo/config` file in your repository and add a Cargo +alias so you can invoke the binary with `cargo xtask`: + +```toml +# .cargo/config + +[alias] +xtask = "run --package xtask --release --" +``` diff --git a/nih_plug_xtask/src/lib.rs b/nih_plug_xtask/src/lib.rs new file mode 100644 index 00000000..6fb03033 --- /dev/null +++ b/nih_plug_xtask/src/lib.rs @@ -0,0 +1,362 @@ +use anyhow::{bail, Context}; +use serde::Deserialize; +use std::collections::HashMap; +use std::fs; +use std::path::Path; +use std::process::Command; + +mod symbols; + +/// Re-export for the main function. +pub use anyhow::Result; + +const USAGE_STRING: &str = "Usage: + cargo xtask bundle [--release] [--target ] + cargo xtask bundle -p -p ... [--release] [--target ]"; + +/// The base birectory for the bundler's output. +const BUNDLE_HOME: &str = "target/bundled"; + +/// Any additional configuration that might be useful for creating plugin bundles, stored as +/// `bundler.toml` alongside the workspace's main `Cargo.toml` file. +type BundlerConfig = HashMap; + +#[derive(Debug, Clone, Deserialize)] +struct PackageConfig { + name: Option, +} + +/// The target we're generating a plugin for. This can be either the native target or a cross +/// compilation target, so to reduce redundancy when determining the correct bundle paths we'll use +/// an enum for this. +/// +/// TODO: Right now we don't consider ARM targets at all +#[allow(dead_code)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CompilationTarget { + Linux64, + Linux32, + Mac64, + Windows64, + Windows32, +} + +/// The main xtask entry point function. See the readme for instructions on how to use this. +pub fn main() -> Result<()> { + chdir_workspace_root()?; + + let mut args = std::env::args().skip(1); + let command = args + .next() + .context(format!("Missing command name\n\n{USAGE_STRING}"))?; + match command.as_str() { + "bundle" => { + // For convenience's sake we'll allow building multiple packages with -p just like carg + // obuild, but you can also build a single package without specifying -p + let mut args = args.peekable(); + let mut packages = Vec::new(); + if args.peek().map(|s| s.as_str()) == Some("-p") { + while args.peek().map(|s| s.as_str()) == Some("-p") { + packages.push( + args.nth(1) + .context(format!("Missing package name after -p\n\n{USAGE_STRING}"))?, + ); + } + } else { + packages.push( + args.next() + .context(format!("Missing package name\n\n{USAGE_STRING}"))?, + ); + }; + let other_args: Vec<_> = args.collect(); + + bundle(&packages[0], &other_args)?; + for package in packages.into_iter().skip(1) { + eprintln!(); + bundle(&package, &other_args)?; + } + + Ok(()) + } + // This is only meant to be used by the CI, since using awk for this can be a bit spotty on + // macOS + "known-packages" => list_known_packages(), + _ => bail!("Unknown command '{command}'\n\n{USAGE_STRING}"), + } +} + +/// Change the current directory into the Cargo workspace's root. +pub fn chdir_workspace_root() -> Result<()> { + let project_root = Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .context("Could not find project root")?; + std::env::set_current_dir(project_root).context("Could not change to project root directory") +} + +/// Bundle a package using the provided `cargo build` arguments. Options from the `bundler.toml` +/// file in the workspace's root are respected (see +/// ). This requires the current +/// working directory to have been set to the workspace's root using [chdir_workspace_root]. +pub fn bundle(package: &str, args: &[String]) -> Result<()> { + let bundle_name = match load_bundler_config()?.and_then(|c| c.get(package).cloned()) { + Some(PackageConfig { name: Some(name) }) => name, + _ => package.to_string(), + }; + + let mut is_release_build = false; + let mut cross_compile_target: Option = None; + for arg_idx in (0..args.len()).rev() { + let arg = &args[arg_idx]; + match arg.as_str() { + "--release" => is_release_build = true, + "--target" => { + // When cross compiling we should generate the correct bundle type + cross_compile_target = Some( + args.get(arg_idx + 1) + .context("Missing cross-compile target")? + .to_owned(), + ); + } + arg if arg.starts_with("--target=") => { + cross_compile_target = Some( + arg.strip_prefix("--target=") + .context("Missing cross-compile target")? + .to_owned(), + ); + } + _ => (), + } + } + + let status = Command::new("cargo") + .arg("build") + .arg("-p") + .arg(package) + .args(args) + .status() + .context(format!("Could not call cargo to build {package}"))?; + if !status.success() { + bail!("Could not build {}", package); + } + + let compilation_target = compilation_target(cross_compile_target.as_deref())?; + let lib_path = Path::new(target_base(cross_compile_target.as_deref())?) + .join(if is_release_build { "release" } else { "debug" }) + .join(library_basename(package, compilation_target)); + if !lib_path.exists() { + bail!("Could not find built library at '{}'", lib_path.display()); + } + + // We'll detect the pugin formats supported by the plugin binary and create bundled accordingly + // TODO: Support VST2 at some point + let bundle_clap = symbols::exported(&lib_path, "clap_entry") + .with_context(|| format!("Could not parse '{}'", lib_path.display()))?; + let bundle_vst3 = symbols::exported(&lib_path, "GetPluginFactory") + .with_context(|| format!("Could not parse '{}'", lib_path.display()))?; + let bundled_plugin = bundle_clap || bundle_vst3; + + eprintln!(); + if bundle_clap { + let clap_bundle_library_name = clap_bundle_library_name(&bundle_name, compilation_target); + let clap_lib_path = Path::new(BUNDLE_HOME).join(&clap_bundle_library_name); + + fs::create_dir_all(clap_lib_path.parent().unwrap()) + .context("Could not create CLAP bundle directory")?; + reflink::reflink_or_copy(&lib_path, &clap_lib_path) + .context("Could not copy library to CLAP bundle")?; + + // In contrast to VST3, CLAP only uses bundles on macOS, so we'll just take the first + // component of the library name instead + let clap_bundle_home = Path::new(BUNDLE_HOME).join( + Path::new(&clap_bundle_library_name) + .components() + .next() + .expect("Malformed CLAP library path"), + ); + maybe_create_macos_bundle_metadata(package, &clap_bundle_home, compilation_target)?; + + eprintln!("Created a CLAP bundle at '{}'", clap_bundle_home.display()); + } + if bundle_vst3 { + let vst3_lib_path = + Path::new(BUNDLE_HOME).join(vst3_bundle_library_name(&bundle_name, compilation_target)); + + fs::create_dir_all(vst3_lib_path.parent().unwrap()) + .context("Could not create VST3 bundle directory")?; + reflink::reflink_or_copy(&lib_path, &vst3_lib_path) + .context("Could not copy library to VST3 bundle")?; + + let vst3_bundle_home = vst3_lib_path + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap(); + maybe_create_macos_bundle_metadata(package, vst3_bundle_home, compilation_target)?; + + eprintln!("Created a VST3 bundle at '{}'", vst3_bundle_home.display()); + } + if !bundled_plugin { + eprintln!("Not creating any plugin bundles because the package does not export any plugins") + } + + Ok(()) +} + +/// This lists the packages configured in `bundler.toml`. This is only used as part of the CI when +/// bundling plugins. +pub fn list_known_packages() -> Result<()> { + if let Some(config) = load_bundler_config()? { + for package in config.keys() { + println!("{package}"); + } + } + + Ok(()) +} + +/// Load the `bundler.toml` file, if it exists. If it does exist but it cannot be parsed, then this +/// will return an error. +fn load_bundler_config() -> Result> { + // We're already in the project root + let bundler_config_path = Path::new("bundler.toml"); + if !bundler_config_path.exists() { + return Ok(None); + } + + let result = toml::from_str( + &fs::read_to_string(&bundler_config_path) + .with_context(|| format!("Could not read '{}'", bundler_config_path.display()))?, + ) + .with_context(|| format!("Could not parse '{}'", bundler_config_path.display()))?; + + Ok(Some(result)) +} + +/// The target we're compiling for. This is used to determine the paths and options for creating +/// plugin bundles. +fn compilation_target(cross_compile_target: Option<&str>) -> Result { + match cross_compile_target { + Some("x86_64-unknown-linux-gnu") => Ok(CompilationTarget::Linux64), + Some("x86_64-apple-darwin") => Ok(CompilationTarget::Mac64), + Some("x86_64-pc-windows-gnu") => Ok(CompilationTarget::Windows64), + Some(target) => bail!("Unhandled cross-compilation target: {}", target), + None => { + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + return Ok(CompilationTarget::Linux64); + #[cfg(all(target_os = "linux", target_arch = "x86"))] + return Ok(CompilationTarget::Linux32); + #[cfg(all(target_os = "macos", target_arch = "x86_64"))] + return Ok(CompilationTarget::Mac64); + #[cfg(all(target_os = "windows", target_arch = "x86_64"))] + return Ok(CompilationTarget::Windows64); + #[cfg(all(target_os = "windows", target_arch = "x86"))] + return Ok(CompilationTarget::Windows32); + } + } +} + +/// The base directory for the compiled binaries. This does not use [CompilationTarget] as we need +/// to be able to differentiate between native and cross-compilation. +fn target_base(cross_compile_target: Option<&str>) -> Result<&'static str> { + match cross_compile_target { + Some("x86_64-unknown-linux-gnu") => Ok("target/x86_64-unknown-linux-gnu"), + Some("x86_64-pc-windows-gnu") => Ok("target/x86_64-pc-windows-gnu"), + Some("x86_64-apple-darwin") => Ok("target/x86_64-apple-darwin"), + Some(target) => bail!("Unhandled cross-compilation target: {}", target), + None => Ok("target"), + } +} + +/// The file name of the compiled library for a `cdylib` crate. +fn library_basename(package: &str, target: CompilationTarget) -> String { + match target { + CompilationTarget::Linux64 | CompilationTarget::Linux32 => format!("lib{package}.so"), + CompilationTarget::Mac64 => format!("lib{package}.dylib"), + CompilationTarget::Windows64 | CompilationTarget::Windows32 => format!("{package}.dll"), + } +} + +/// The filename of the CLAP plugin for Linux and Windows, or the full path to the library file +/// inside of a CLAP bundle on macOS +fn clap_bundle_library_name(package: &str, target: CompilationTarget) -> String { + match target { + CompilationTarget::Linux64 + | CompilationTarget::Linux32 + | CompilationTarget::Windows64 + | CompilationTarget::Windows32 => format!("{package}.clap"), + CompilationTarget::Mac64 => format!("{package}.clap/Contents/MacOS/{package}"), + } +} + +/// The full path to the library file inside of a VST3 bundle, including the leading `.vst3` +/// directory. +/// +/// See . +fn vst3_bundle_library_name(package: &str, target: CompilationTarget) -> String { + match target { + CompilationTarget::Linux64 => format!("{package}.vst3/Contents/x86_64-linux/{package}.so"), + CompilationTarget::Linux32 => format!("{package}.vst3/Contents/i386-linux/{package}.so"), + CompilationTarget::Mac64 => format!("{package}.vst3/Contents/MacOS/{package}"), + CompilationTarget::Windows64 => { + format!("{package}.vst3/Contents/x86_64-win/{package}.vst3") + } + CompilationTarget::Windows32 => format!("{package}.vst3/Contents/x86-win/{package}.vst3"), + } +} + +/// If compiling for macOS, create all of the bundl-y stuff Steinberg and Apple require you to have. +/// +/// This still requires you to move the dylib file to `{bundle_home}/Contents/macOS/{package}` +/// yourself first. +pub fn maybe_create_macos_bundle_metadata( + package: &str, + bundle_home: &Path, + target: CompilationTarget, +) -> Result<()> { + if target != CompilationTarget::Mac64 { + return Ok(()); + } + + // TODO: May want to add bundler.toml fields for the identifier, version and signature at some + // point. + fs::write(bundle_home.join("Contents").join("PkgInfo"), "BNDL????") + .context("Could not create PkgInfo file")?; + fs::write( + bundle_home.join("Contents").join("Info.plist"), + format!(r#" + + + + + CFBundleExecutable + {package} + CFBundleIconFile + + CFBundleIdentifier + com.nih-plug.{package} + CFBundleName + {package} + CFBundleDisplayName + {package} + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0.0 + CFBundleVersion + 1.0.0 + NSHumanReadableCopyright + + NSHighResolutionCapable + + + +"#), + ) + .context("Could not create Info.plist file")?; + + Ok(()) +} diff --git a/xtask/src/symbols.rs b/nih_plug_xtask/src/symbols.rs similarity index 100% rename from xtask/src/symbols.rs rename to nih_plug_xtask/src/symbols.rs diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index aa43b1b8..9f798646 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -6,9 +6,4 @@ authors = ["Robbert van der Helm "] license = "ISC" [dependencies] -anyhow = "1.0" -goblin = "0.5" -# Version 0.1.3 from crates.io assumes a 64-bit toolchain -reflink = { git = "https://github.com/nicokoch/reflink", rev = "e8d93b465f5d9ad340cd052b64bbc77b8ee107e2" } -serde = { version = "1.0", features = ["derive"] } -toml = "0.5" +nih_plug_xtask = { path = "../nih_plug_xtask" } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 94c918b1..11fecb62 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,348 +1,3 @@ -use anyhow::{bail, Context, Result}; -use serde::Deserialize; -use std::collections::HashMap; -use std::fs; -use std::path::Path; -use std::process::Command; - -mod symbols; - -const USAGE_STRING: &str = "Usage: - cargo xtask bundle [--release] [--target ] - cargo xtask bundle -p -p ... [--release] [--target ]"; - -/// The base birectory for the bundler's output. -const BUNDLE_HOME: &str = "target/bundled"; - -/// Any additional configuration that might be useful for creating plugin bundles, stored as -/// `bundler.toml` alongside the workspace's main `Cargo.toml` file. -type BundlerConfig = HashMap; - -#[derive(Debug, Clone, Deserialize)] -struct PackageConfig { - name: Option, -} - -/// The target we're generating a plugin for. This can be either the native target or a cross -/// compilation target, so to reduce redundancy when determining the correct bundle paths we'll use -/// an enum for this. -/// -/// TODO: Right now we don't consider ARM targets at all -#[allow(dead_code)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum CompilationTarget { - Linux64, - Linux32, - Mac64, - Windows64, - Windows32, -} - -fn main() -> Result<()> { - let project_root = Path::new(env!("CARGO_MANIFEST_DIR")) - .parent() - .context("Could not find project root")?; - std::env::set_current_dir(project_root) - .context("Could not change to project root directory")?; - - let mut args = std::env::args().skip(1); - let command = args - .next() - .context(format!("Missing command name\n\n{USAGE_STRING}"))?; - match command.as_str() { - "bundle" => { - // For convenience's sake we'll allow building multiple packages with -p just like carg - // obuild, but you can also build a single package without specifying -p - let mut args = args.peekable(); - let mut packages = Vec::new(); - if args.peek().map(|s| s.as_str()) == Some("-p") { - while args.peek().map(|s| s.as_str()) == Some("-p") { - packages.push( - args.nth(1) - .context(format!("Missing package name after -p\n\n{USAGE_STRING}"))?, - ); - } - } else { - packages.push( - args.next() - .context(format!("Missing package name\n\n{USAGE_STRING}"))?, - ); - }; - let other_args: Vec<_> = args.collect(); - - bundle(&packages[0], &other_args)?; - for package in packages.into_iter().skip(1) { - eprintln!(); - bundle(&package, &other_args)?; - } - - Ok(()) - } - // This is only meant to be used by the CI, since using awk for this can be a bit spotty on - // macOS - "known-packages" => list_known_packages(), - _ => bail!("Unknown command '{command}'\n\n{USAGE_STRING}"), - } -} - -// TODO: The macOS version has not been tested -fn bundle(package: &str, args: &[String]) -> Result<()> { - let bundle_name = match load_bundler_config()?.and_then(|c| c.get(package).cloned()) { - Some(PackageConfig { name: Some(name) }) => name, - _ => package.to_string(), - }; - - let mut is_release_build = false; - let mut cross_compile_target: Option = None; - for arg_idx in (0..args.len()).rev() { - let arg = &args[arg_idx]; - match arg.as_str() { - "--release" => is_release_build = true, - "--target" => { - // When cross compiling we should generate the correct bundle type - cross_compile_target = Some( - args.get(arg_idx + 1) - .context("Missing cross-compile target")? - .to_owned(), - ); - } - arg if arg.starts_with("--target=") => { - cross_compile_target = Some( - arg.strip_prefix("--target=") - .context("Missing cross-compile target")? - .to_owned(), - ); - } - _ => (), - } - } - - let status = Command::new("cargo") - .arg("build") - .arg("-p") - .arg(package) - .args(args) - .status() - .context(format!("Could not call cargo to build {package}"))?; - if !status.success() { - bail!("Could not build {}", package); - } - - let compilation_target = compilation_target(cross_compile_target.as_deref())?; - let lib_path = Path::new(target_base(cross_compile_target.as_deref())?) - .join(if is_release_build { "release" } else { "debug" }) - .join(library_basename(package, compilation_target)); - if !lib_path.exists() { - bail!("Could not find built library at '{}'", lib_path.display()); - } - - // We'll detect the pugin formats supported by the plugin binary and create bundled accordingly - // TODO: Support VST2 at some point - let bundle_clap = symbols::exported(&lib_path, "clap_entry") - .with_context(|| format!("Could not parse '{}'", lib_path.display()))?; - let bundle_vst3 = symbols::exported(&lib_path, "GetPluginFactory") - .with_context(|| format!("Could not parse '{}'", lib_path.display()))?; - let bundled_plugin = bundle_clap || bundle_vst3; - - eprintln!(); - if bundle_clap { - let clap_bundle_library_name = clap_bundle_library_name(&bundle_name, compilation_target); - let clap_lib_path = Path::new(BUNDLE_HOME).join(&clap_bundle_library_name); - - fs::create_dir_all(clap_lib_path.parent().unwrap()) - .context("Could not create CLAP bundle directory")?; - reflink::reflink_or_copy(&lib_path, &clap_lib_path) - .context("Could not copy library to CLAP bundle")?; - - // In contrast to VST3, CLAP only uses bundles on macOS, so we'll just take the first - // component of the library name instead - let clap_bundle_home = Path::new(BUNDLE_HOME).join( - Path::new(&clap_bundle_library_name) - .components() - .next() - .expect("Malformed CLAP library path"), - ); - maybe_create_macos_bundle(package, &clap_bundle_home, compilation_target)?; - - eprintln!("Created a CLAP bundle at '{}'", clap_bundle_home.display()); - } - if bundle_vst3 { - let vst3_lib_path = - Path::new(BUNDLE_HOME).join(vst3_bundle_library_name(&bundle_name, compilation_target)); - - fs::create_dir_all(vst3_lib_path.parent().unwrap()) - .context("Could not create VST3 bundle directory")?; - reflink::reflink_or_copy(&lib_path, &vst3_lib_path) - .context("Could not copy library to VST3 bundle")?; - - let vst3_bundle_home = vst3_lib_path - .parent() - .unwrap() - .parent() - .unwrap() - .parent() - .unwrap(); - maybe_create_macos_bundle(package, vst3_bundle_home, compilation_target)?; - - eprintln!("Created a VST3 bundle at '{}'", vst3_bundle_home.display()); - } - if !bundled_plugin { - eprintln!("Not creating any plugin bundles because the package does not export any plugins") - } - - Ok(()) -} - -/// This lists the packages configured in `bundler.toml`. This is only used as part of the CI when -/// bundling plugins. -fn list_known_packages() -> Result<()> { - if let Some(config) = load_bundler_config()? { - for package in config.keys() { - println!("{package}"); - } - } - - Ok(()) -} - -/// Load the `bundler.toml` file, if it exists. If it does exist but it cannot be parsed, then this -/// will return an error. -fn load_bundler_config() -> Result> { - // We're already in the project root - let bundler_config_path = Path::new("bundler.toml"); - if !bundler_config_path.exists() { - return Ok(None); - } - - let result = toml::from_str( - &fs::read_to_string(&bundler_config_path) - .with_context(|| format!("Could not read '{}'", bundler_config_path.display()))?, - ) - .with_context(|| format!("Could not parse '{}'", bundler_config_path.display()))?; - - Ok(Some(result)) -} - -/// The target we're compiling for. This is used to determine the paths and options for creating -/// plugin bundles. -fn compilation_target(cross_compile_target: Option<&str>) -> Result { - match cross_compile_target { - Some("x86_64-unknown-linux-gnu") => Ok(CompilationTarget::Linux64), - Some("x86_64-apple-darwin") => Ok(CompilationTarget::Mac64), - Some("x86_64-pc-windows-gnu") => Ok(CompilationTarget::Windows64), - Some(target) => bail!("Unhandled cross-compilation target: {}", target), - None => { - #[cfg(all(target_os = "linux", target_arch = "x86_64"))] - return Ok(CompilationTarget::Linux64); - #[cfg(all(target_os = "linux", target_arch = "x86"))] - return Ok(CompilationTarget::Linux32); - #[cfg(all(target_os = "macos", target_arch = "x86_64"))] - return Ok(CompilationTarget::Mac64); - #[cfg(all(target_os = "windows", target_arch = "x86_64"))] - return Ok(CompilationTarget::Windows64); - #[cfg(all(target_os = "windows", target_arch = "x86"))] - return Ok(CompilationTarget::Windows32); - } - } -} - -/// The base directory for the compiled binaries. This does not use [CompilationTarget] as we need -/// to be able to differentiate between native and cross-compilation. -fn target_base(cross_compile_target: Option<&str>) -> Result<&'static str> { - match cross_compile_target { - Some("x86_64-unknown-linux-gnu") => Ok("target/x86_64-unknown-linux-gnu"), - Some("x86_64-pc-windows-gnu") => Ok("target/x86_64-pc-windows-gnu"), - Some("x86_64-apple-darwin") => Ok("target/x86_64-apple-darwin"), - Some(target) => bail!("Unhandled cross-compilation target: {}", target), - None => Ok("target"), - } -} - -/// The file name of the compiled library for a `cdylib` crate. -fn library_basename(package: &str, target: CompilationTarget) -> String { - match target { - CompilationTarget::Linux64 | CompilationTarget::Linux32 => format!("lib{package}.so"), - CompilationTarget::Mac64 => format!("lib{package}.dylib"), - CompilationTarget::Windows64 | CompilationTarget::Windows32 => format!("{package}.dll"), - } -} - -/// The filename of the CLAP plugin for Linux and Windows, or the full path to the library file -/// inside of a CLAP bundle on macOS -fn clap_bundle_library_name(package: &str, target: CompilationTarget) -> String { - match target { - CompilationTarget::Linux64 - | CompilationTarget::Linux32 - | CompilationTarget::Windows64 - | CompilationTarget::Windows32 => format!("{package}.clap"), - CompilationTarget::Mac64 => format!("{package}.clap/Contents/MacOS/{package}"), - } -} - -/// The full path to the library file inside of a VST3 bundle, including the leading `.vst3` -/// directory. -/// -/// See . -fn vst3_bundle_library_name(package: &str, target: CompilationTarget) -> String { - match target { - CompilationTarget::Linux64 => format!("{package}.vst3/Contents/x86_64-linux/{package}.so"), - CompilationTarget::Linux32 => format!("{package}.vst3/Contents/i386-linux/{package}.so"), - CompilationTarget::Mac64 => format!("{package}.vst3/Contents/MacOS/{package}"), - CompilationTarget::Windows64 => { - format!("{package}.vst3/Contents/x86_64-win/{package}.vst3") - } - CompilationTarget::Windows32 => format!("{package}.vst3/Contents/x86-win/{package}.vst3"), - } -} - -/// If compiling for macOS, create all of the bundl-y stuff Steinberg and Apple require you to have. -fn maybe_create_macos_bundle( - package: &str, - bundle_home: &Path, - target: CompilationTarget, -) -> Result<()> { - if target != CompilationTarget::Mac64 { - return Ok(()); - } - - // TODO: May want to add bundler.toml fields for the identifier, version and signature at some - // point. - fs::write(bundle_home.join("Contents").join("PkgInfo"), "BNDL????") - .context("Could not create PkgInfo file")?; - fs::write( - bundle_home.join("Contents").join("Info.plist"), - format!(r#" - - - - - CFBundleExecutable - {package} - CFBundleIconFile - - CFBundleIdentifier - com.nih-plug.{package} - CFBundleName - {package} - CFBundleDisplayName - {package} - CFBundlePackageType - BNDL - CFBundleSignature - ???? - CFBundleShortVersionString - 1.0.0 - CFBundleVersion - 1.0.0 - NSHumanReadableCopyright - - NSHighResolutionCapable - - - -"#), - ) - .context("Could not create Info.plist file")?; - - Ok(()) +fn main() -> nih_plug_xtask::Result<()> { + nih_plug_xtask::main() }