tg-xtask 0.1.0-preview.1

A small xtask helper to build and run rCore tutorial kernels and local kernels with QEMU.
use std::process::Command;
use std::ffi::OsStr;

pub struct CargoBuilder {
    pub program: String,
    pub args: Vec<String>,
    pub envs: Vec<(String, String)>,
}

impl CargoBuilder {
    pub fn build() -> Self {
        CargoBuilder {
            program: "cargo".to_string(),
            args: vec!["build".to_string()],
            envs: vec![],
        }
    }

    pub fn package(&mut self, name: &str) -> &mut Self {
        self.args.push("--package".to_string());
        self.args.push(name.to_string());
        self
    }

    pub fn features<I>(&mut self, _flag: bool, features: I) -> &mut Self
    where
        I: IntoIterator,
        I::Item: AsRef<OsStr>,
    {
        self.args.push("--features".to_string());
        let s = features
            .into_iter()
            .map(|f| f.as_ref().to_string_lossy().into_owned())
            .collect::<Vec<_>>()
            .join(",");
        self.args.push(s);
        self
    }

    pub fn conditional(&mut self, cond: bool, f: impl FnOnce(&mut CargoBuilder)) -> &mut Self {
        if cond {
            f(self);
        }
        self
    }

    pub fn optional<T>(&mut self, opt: &Option<T>, f: impl FnOnce(&mut CargoBuilder, &T)) -> &mut Self {
        if let Some(v) = opt {
            f(self, v);
        }
        self
    }

    pub fn env<S: AsRef<OsStr>>(&mut self, key: &str, value: S) -> &mut Self {
        let v = value.as_ref().to_string_lossy().into_owned();
        self.envs.push((key.to_string(), v));
        self
    }

    pub fn release(&mut self) -> &mut Self {
        self.args.push("--release".to_string());
        self
    }

    pub fn target(&mut self, target: &str) -> &mut Self {
        self.args.push("--target".to_string());
        self.args.push(target.to_string());
        self
    }

    pub fn arg<S: AsRef<OsStr>>(&mut self, a: S) -> &mut Self {
        let s = a.as_ref().to_string_lossy().into_owned();
        self.args.push(s);
        self
    }

    #[allow(dead_code)]
    pub fn args<I, S>(&mut self, iter: I) -> &mut Self
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        for s in iter {
            self.args.push(s.as_ref().to_string());
        }
        self
    }

    pub fn invoke(&self) {
        let mut cmd = Command::new(&self.program);
        for (k, v) in &self.envs {
            cmd.env(k, v);
        }
        cmd.args(&self.args);
        let status = cmd.status().expect("failed to execute cargo");
        if !status.success() {
            panic!("cargo build failed");
        }
    }

    pub fn to_command_string(&self) -> String {
        let mut parts: Vec<String> = vec![];
        for (k, v) in &self.envs {
            parts.push(format!("{}={}", k, v));
        }
        parts.push(self.program.clone());
        parts.extend(self.args.clone());
        parts.join(" ")
    }
}