cargo-truce 0.60.1

Build tool for truce audio plugins (CLAP, VST3, VST2, LV2, AU v2, AU v3, AAX, and a standalone host binary)
Documentation
//! `cargo-truce` library - engine for `cargo truce <subcommand>`.
//!
//! `main.rs` owns argument parsing, the `cargo truce` arg strip,
//! the user-facing help block, and dispatch for `new` (single +
//! `--workspace` modes, which live in the [`scaffold`] module).
//! Every other subcommand goes through [`run`].

mod commands;
mod config;
pub(crate) mod dirs;
mod error;
mod format;
mod install_scope;
pub(crate) mod preset_codec;
pub mod scaffold;
mod templates;
mod util;

pub use error::CargoTruceError;

// The Windows packager + its DPI/icon manifest helper now live under
// `commands::package::{windows, windows_manifest}` (alongside the
// macOS / Linux packagers), so they no longer need a crate-root `mod`
// or a `PkgFormat` re-export - `windows.rs` reaches `super::PkgFormat`
// like its siblings.

// Crate-root re-export of the AAX template builder, used by the
// Windows packager. Cfg-gated so it isn't dead on macOS / Linux.
#[cfg(target_os = "windows")]
pub(crate) use commands::install::aax::build_aax_template;
#[cfg(target_os = "macos")]
pub(crate) use config::installer_identity;
pub(crate) use config::{
    Config, PluginDef, application_identity, deployment_target, load_config, read_build_env,
    resolve_aax_sdk_path,
};
#[cfg(target_os = "macos")]
pub(crate) use config::{
    ios_appex_provisioning_profile, ios_application_identity, ios_provisioning_profile, ios_team_id,
};
#[cfg(any(target_os = "macos", test))]
pub(crate) use util::tmp_verify;
pub(crate) use util::{
    cargo_build, cargo_build_debug, check_cmd, codesign_bundle, confirm_prompt,
    detect_default_features, find_on_path, is_debug_profile, log_output, log_skip, project_root,
    read_standalone_bin_name, release_lib, set_build_profile, set_debug_profile, set_target_cpu,
    tag_fail, tag_ok, tag_warn, take_outputs, take_skipped, verify_shell_profile_declared,
    vprintln,
};
// `run_sudo` shells out to `/usr/bin/sudo`, which only exists on macOS in
// our supported targets. Windows admin elevation is per-process (UAC, not
// per-command) and Linux installs are always per-user, so the helper has
// no meaningful implementation off macOS - the cfg gate forces callers to
// stay platform-aware rather than silently spawning a missing binary.
#[cfg(target_os = "macos")]
pub(crate) use util::run_sudo;
// `tmp_dir` is the raw escape hatch - used by `reset_au` (macOS only)
// to walk every subdir under `tmp/`. Most callers pick the typed
// helper that matches their purpose (`tmp_manifests`, `tmp_lv2`, …).
#[cfg(target_os = "macos")]
pub(crate) use util::tmp_dir;
// `tmp_manifests` is used on both macOS (codesign / AU / VST plist
// scratch) and Windows (Azure signing metadata). Not used on Linux
// since the tarball pipeline doesn't shell out to platform tools.
#[cfg(any(target_os = "macos", target_os = "windows"))]
pub(crate) use util::tmp_manifests;
// `tmp_lv2` is consumed only by `install_lv2`'s macOS sudo-stage
// path - Windows/Linux installers write straight into the destination
// without staging, since neither needs the per-command privilege
// escalation that drives the macOS detour.
#[cfg(target_os = "macos")]
pub(crate) use util::tmp_lv2;

// `read_workspace_version` is consumed by all three packagers
// (macOS / Windows / Linux tarball), so it stays unconditional.
pub(crate) use util::read_workspace_version;

// `release_lib_for_target` resolves a per-triple build output path; it
// powers macOS universal lipo, Windows x64+arm64, and Linux dual-arch
// `--target` builds. Always available.
pub(crate) use util::release_lib_for_target;
#[cfg(target_os = "macos")]
pub(crate) use util::{release_bundle_bin, release_static_for_target};

// `tag_info` formats the doctor/packager status prefix; the `doctor`
// command runs on every platform, so this stays unconditional.
pub(crate) use util::tag_info;

// Re-exports used only by the macOS / Windows installer pipelines.
// Linux ships plugins via distro tooling rather than the bundled
// `package` flow, so these symbols are absent there.
#[cfg(any(target_os = "macos", target_os = "windows"))]
pub(crate) use util::{rustup_has_target, tmp_aax_template};

// macOS-only: codesign / lipo / notary / AAX PACE-sign pipeline. The
// `MacosPackagingConfig` / `copy_dir_recursive` re-exports land here
// too - only `commands::package::{macos, stage}` consume them, and
// both are macOS-gated.
#[cfg(target_os = "macos")]
pub(crate) use config::MacosPackagingConfig;
#[cfg(target_os = "macos")]
pub(crate) use util::{
    CLAP_EXPORTS, MacArch, VST2_EXPORTS, VST3_EXPORTS, cargo_build_for_arch,
    cargo_build_multi_arch, copy_dir_recursive, extract_team_id, is_production_identity,
    link_macos_bundle, lipo_into, locate_wraptool_macos, missing_staticlib_error,
    pace_sign_aax_macos, run_codesign, run_silent, tmp_au_v3,
};

// Windows-only: VS / MSVC / cmake / ninja discovery + Program Files
// path helpers, used by `commands::{doctor, install, install::aax}`
// and `commands::package::windows`.
#[cfg(target_os = "windows")]
pub(crate) use util::{
    cargo_rustc_bin, common_program_files, locate_cmake, locate_msvc_cl, locate_ninja,
    locate_vcvars64, locate_vcvarsall, program_files, tmp_scripts, vs_install_paths, which_exe,
};

use std::process::ExitCode;

pub type Res = std::result::Result<(), CargoTruceError>;

/// Run a command with the given args (e.g. `["install", "--clap"]`).
///
/// Help, scaffold (`new`), and the `cargo truce`
/// arg-stripping live in `main.rs`. Unknown commands here surface
/// back to the caller as an error so `main` can render its own help
/// block.
#[must_use]
pub fn run(args: &[String]) -> ExitCode {
    // Strip global `-v` / `--verbose` from anywhere in the arg list.
    // Setting the static once here means every subcommand picks it up
    // without each having to parse the flag.
    let mut filtered: Vec<String> = Vec::with_capacity(args.len());
    for a in args {
        if a == "-v" || a == "--verbose" {
            util::set_verbose(true);
        } else {
            filtered.push(a.clone());
        }
    }
    let args = &filtered[..];

    let cmd = args.first().map_or("", std::string::String::as_str);

    let result = match cmd {
        "install" => commands::install::cmd_install(&args[1..]),
        "build" => commands::build::cmd_build(&args[1..]),
        "package" => commands::package::cmd_package(&args[1..]),
        "uninstall" => commands::uninstall::cmd_uninstall(&args[1..]),
        "run" => commands::run::cmd_run(&args[1..]),
        "screenshot" => commands::screenshot::cmd_screenshot(&args[1..]),
        "status" => commands::status::cmd_status(&args[1..]),
        "reset-au" => commands::reset_au::cmd_reset_au(&args[1..]),
        "reset-aax" => commands::reset_aax::cmd_reset_aax(&args[1..]),
        "validate" => commands::validate::cmd_validate(&args[1..]),
        "preset" => commands::preset::cmd_preset(&args[1..]),
        "doctor" => commands::doctor::cmd_doctor(&args[1..]),
        "log-stream-au" => commands::log_stream_au::cmd_log_stream_au(&args[1..]),
        other => Err(format!("unknown command: {other:?}").into()),
    };

    match result {
        Ok(()) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("Error: {e}");
            ExitCode::FAILURE
        }
    }
}