drone 0.14.0

CLI utility for Drone, an Embedded Operating System.
#![warn(clippy::pedantic)]

use anyhow::Result;
use drone::{
    color::Color,
    templates::Registry,
    utils::{
        block_with_signals, crate_root, register_signals, run_command, run_wrapper,
        search_rust_tool,
    },
};
use drone_config::Config;
use std::{
    collections::HashMap,
    env,
    ffi::{OsStr, OsString},
    fs::{create_dir_all, File},
    path::Path,
    process::Command,
};

fn main() {
    run_wrapper(Color::Never, run);
}

fn run() -> Result<()> {
    let args = env::args_os().skip(1).collect::<Vec<_>>();
    let config = Config::read_from_current_dir()?;
    let registry = Registry::new()?;
    let mut signals = register_signals()?;

    let crate_root = crate_root()?;
    let target = block_with_signals(&mut signals, true, run_drone_print_target)?;
    let target = crate_root.join("target").join(target);
    create_dir_all(&target)?;
    let stage_one = target.join("layout.ld.1");
    let stage_two = target.join("layout.ld.2");
    {
        let stage_one_file = File::create(&stage_one)?;
        let stage_two_file = File::create(&stage_two)?;
        registry.layout_ld(&config, false, &stage_one_file)?;
        registry.layout_ld(&config, true, &stage_two_file)?;
    }

    if let Some(output_position) = args.iter().position(|arg| arg == "-o") {
        let linker = linker_command(stage_one.as_ref(), &args, &[])?;
        block_with_signals(&mut signals, true, || run_command(linker))?;

        let size = size_command(&args[output_position + 1])?;
        let syms = block_with_signals(&mut signals, true, || run_size(size))?
            .into_iter()
            .map(|(name, size)| format!("--defsym=_{}_section_size={}", name, size))
            .collect::<Vec<_>>();

        let linker = linker_command(stage_two.as_ref(), &args, &syms)?;
        block_with_signals(&mut signals, true, || run_command(linker))?;
    }

    Ok(())
}

fn linker_command(script: &Path, args: &[OsString], syms: &[String]) -> Result<Command> {
    let mut rust_lld = Command::new(search_rust_tool("rust-lld")?);
    rust_lld.arg("-flavor").arg("gnu");
    rust_lld.arg("-T").arg(script);
    rust_lld.args(args);
    rust_lld.args(syms);
    Ok(rust_lld)
}

fn size_command(output: &OsStr) -> Result<Command> {
    let mut command = Command::new(search_rust_tool("llvm-size")?);
    command.arg("-A").arg(output);
    Ok(command)
}

fn run_size(mut command: Command) -> Result<HashMap<String, u32>> {
    let stdout = String::from_utf8(command.output()?.stdout)?;
    let mut map = HashMap::new();
    for line in stdout.lines() {
        if line.starts_with('.') {
            if let [name, size, ..] = line.split_whitespace().collect::<Vec<_>>().as_slice() {
                map.insert(name[1..].to_string(), size.parse()?);
            }
        }
    }
    Ok(map)
}

fn run_drone_print_target() -> Result<String> {
    let mut command = Command::new("drone");
    command.arg("print").arg("target");
    let stdout = String::from_utf8(command.output()?.stdout)?;
    Ok(stdout.trim().to_string())
}