#[cfg(any(target_os = "macos", target_os = "windows"))]
use crate::BoxErr;
#[cfg(target_os = "macos")]
use crate::PluginDef;
use crate::Res;
pub(crate) mod stage;
#[cfg(target_os = "macos")]
pub(crate) mod macos;
#[cfg(any(target_os = "macos", target_os = "windows"))]
#[derive(Clone, PartialEq)]
pub(crate) enum PkgFormat {
Clap,
Vst3,
Vst2,
Au2,
Au3,
Aax,
}
#[cfg(any(target_os = "macos", target_os = "windows"))]
impl std::str::FromStr for PkgFormat {
type Err = BoxErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"clap" => Ok(PkgFormat::Clap),
"vst3" => Ok(PkgFormat::Vst3),
"vst2" => Ok(PkgFormat::Vst2),
"au2" => Ok(PkgFormat::Au2),
"au3" => Ok(PkgFormat::Au3),
"aax" => Ok(PkgFormat::Aax),
other => Err(format!("unknown format: {other}").into()),
}
}
}
#[cfg(any(target_os = "macos", target_os = "windows"))]
impl PkgFormat {
pub(crate) fn parse_list(s: &str) -> Result<Vec<PkgFormat>, BoxErr> {
s.split(',').map(|t| t.trim().parse()).collect()
}
pub(crate) fn label(&self) -> &'static str {
match self {
PkgFormat::Clap => "CLAP",
PkgFormat::Vst3 => "VST3",
PkgFormat::Vst2 => "VST2",
PkgFormat::Au2 => "AU2",
PkgFormat::Au3 => "AU3",
PkgFormat::Aax => "AAX",
}
}
}
#[cfg(target_os = "macos")]
impl PkgFormat {
pub(crate) fn extension(&self) -> &'static str {
match self {
PkgFormat::Clap => "clap",
PkgFormat::Vst3 => "vst3",
PkgFormat::Vst2 => "vst",
PkgFormat::Au2 => "component",
PkgFormat::Au3 => "app",
PkgFormat::Aax => "aaxplugin",
}
}
pub(crate) fn install_location(&self) -> &'static str {
match self {
PkgFormat::Clap => "/Library/Audio/Plug-Ins/CLAP/",
PkgFormat::Vst3 => "/Library/Audio/Plug-Ins/VST3/",
PkgFormat::Vst2 => "/Library/Audio/Plug-Ins/VST/",
PkgFormat::Au2 => "/Library/Audio/Plug-Ins/Components/",
PkgFormat::Au3 => "/Applications/",
PkgFormat::Aax => "/Library/Application Support/Avid/Audio/Plug-Ins/",
}
}
pub(crate) fn pkg_id_suffix(&self) -> &'static str {
match self {
PkgFormat::Clap => "clap",
PkgFormat::Vst3 => "vst3",
PkgFormat::Vst2 => "vst2",
PkgFormat::Au2 => "au2",
PkgFormat::Au3 => "au3",
PkgFormat::Aax => "aax",
}
}
pub(crate) fn is_native_bundle(&self) -> bool {
matches!(self, PkgFormat::Vst3 | PkgFormat::Au2 | PkgFormat::Au3)
}
pub(crate) fn bundle_name(&self, plugin: &PluginDef) -> String {
match self {
PkgFormat::Au3 => format!("{}.app", plugin.au3_app_name()),
_ => format!("{}.{}", plugin.name, self.extension()),
}
}
pub(crate) fn choice_description(&self) -> &'static str {
match self {
PkgFormat::Clap => "For Reaper, Bitwig",
PkgFormat::Vst3 => "For Ableton, FL Studio, Reaper, Cubase",
PkgFormat::Vst2 => "Legacy — for hosts without VST3 support",
PkgFormat::Au2 => "For Logic Pro, GarageBand, Ableton",
PkgFormat::Au3 => "Audio Unit v3 (appex)",
PkgFormat::Aax => "For Pro Tools",
}
}
}
#[cfg_attr(
not(any(target_os = "macos", target_os = "windows")),
allow(unused_variables)
)]
pub(crate) fn cmd_package(args: &[String]) -> Res {
if args.iter().any(|a| a == "--help" || a == "-h") {
print_help();
return Ok(());
}
#[cfg(target_os = "windows")]
{
crate::packaging_windows::cmd_package(args)
}
#[cfg(target_os = "macos")]
{
macos::cmd_package_macos(args)
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
Err("`cargo truce package` is not supported on this platform. \
macOS produces signed `.pkg` installers; Windows produces Inno Setup `.exe` installers. \
For Linux distribution, use `cargo truce build` and ship the bundles from \
`target/bundles/` via your distro's native packaging (.deb / .rpm / AppImage / Flatpak)."
.into())
}
fn print_help() {
eprintln!(
"\
Usage: cargo truce package [-p <crate>] [--formats <list>] \
[--user|--system|--ask] [--no-notarize] [--no-sign|--no-pace-sign] \
[--host-only|--universal]
Build, sign, and package plugins into a signed installer:
- macOS: `target/dist/<Plugin>-<version>-<platform>.pkg` (productbuild)
- Windows: `target/dist/<Plugin>-<version>-<platform>.exe` (Inno Setup)
- Linux: not supported. Use `cargo truce build` and ship the bundles
from `target/bundles/` via .deb / .rpm / AppImage / Flatpak.
Selection:
-p <crate> Package only this plugin crate.
--formats <list> Comma-separated subset (clap,vst3,vst2,au2,au3,aax).
Default: every format in the plugin's `[features].default`.
Install scope (where the resulting installer puts files at the end user's machine):
--ask End user picks at install time. Default.
--user User-scope. CLAP/VST3 land in user paths with no
admin prompt. System-only formats (AAX, AU v3, Windows
VST2) stay system-scope; the user sees one admin prompt.
--system Hard-lock to system paths.
Override the default project-wide via `[packaging] preferred_scope` in truce.toml.
Signing / notarization:
--no-notarize Skip macOS notarization (still codesigns).
--no-pace-sign Skip PACE (AAX) signing — useful for non-Pro Tools
sanity checks. Apple codesign always runs on macOS.
--no-sign Synonym for --no-pace-sign on macOS.
Build target (macOS):
--host-only Single-arch build of the host. Default is universal.
--universal Explicit universal (no-op; same as default).
Misc:
-h, --help Show this message."
);
}