cargo-mkdist 0.1.1

A Cargo subcommand to build native distribution packages (apt, pacman, etc) directly from Rust, without external tools.
#![warn(missing_docs)]
#![doc = include_str!("../DOCS.md")]

mod meta;

mod build;

mod stage;

mod pkg;

mod cfg;

mod cli;

#[cfg(test)]
mod tests;

use clap::Parser as _;

use anyhow::{Context as _, Result};

fn main() -> Result<()>
{
    let cli = cli::Cli::parse();

    if cli.list
    {
        let config_path = cfg::DistConfig::find()?;
        let config = cfg::DistConfig::load(&config_path)?;
        println!("Available targets:");
        for name in config.targets.keys()
        {
            println!("  {}", name);
        }
        return Ok(());
    }

    let config_path = cfg::DistConfig::find()?;
    let raw_config = cfg::DistConfig::load(&config_path)?;

    let resolved_targets = cfg::resolve_targets(raw_config.targets)?;

    let targets_to_build: Vec<String> = if cli.all
    {
        resolved_targets.keys().cloned().collect()
    }
    else if let Some(target_name) = &cli.target
    {
        if !resolved_targets.contains_key(target_name)
        {
            anyhow::bail!("'{}' target not found.", target_name);
        }
        vec![target_name.clone()]
    }
    else
    {
        if let Some(default) = resolved_targets.get("default")
        {
            vec![default.name.clone()]
        }
        else
        {
            anyhow::bail!(
                "No target is specified, and there is no 'default' target in \
                 configuration. Use --list to view the targets or --all to \
                 build all."
            )
        }
    };

    let metadata = meta::get()?;

    for name in targets_to_build
    {
        let target = resolved_targets
            .get(&name)
            .context("Can't get resolved target.")?;
        println!(
            "   Compiling '{}' ({}-{})",
            name, target.format, target.target
        );

        let mut cargo_flags: Vec<String> = Vec::new();
        if let Some(flags_str) = &target.flags
        {
            cargo_flags.extend(flags_str.split_whitespace().map(String::from));
        }
        cargo_flags.extend(cli.cargo_args.clone());

        build::build_project(
            &target.target,
            &cargo_flags,
            cli.debug,
            target.package.as_deref(),
        )?;

        let staging = stage::Staging::new()?;

        let binary_names = if let Some(ref bins) = target.binaries
        {
            bins.clone()
        }
        else
        {
            let pkg_name = target.package.as_deref().map(|s| s.to_string());
            meta::get_binary_names(pkg_name)?
        };

        for name in &binary_names
        {
            staging.copy_binary(name, &target.target, cli.debug)?;
        }

        let files = if let Some(ref files) = target.files
        {
            files.clone()
        }
        else
        {
            vec![]
        };

        staging.copy_extra_files(&files)?;

        let packager = pkg::packager(&target.format);
        let out_dir = std::path::Path::new(&cli.out_dir).join(&name);
        let package_meta =
            meta::find_package(&metadata, target.package.clone())?;
        let output_path = packager.package(
            &staging,
            target,
            &out_dir,
            &package_meta.name,
            &package_meta.version.to_string(),
        )?;
        println!("    Finished {}.", output_path.display());
    }

    Ok(())
}