use std::collections::HashMap; use std::error::Error; use std::io::Write; use std::io; use std::path::PathBuf; use std::process::{Command, Stdio}; fn escape_path(word: &str) -> String { word.replace("$ ", "$$ ").replace(' ', "$ ").replace(':', "$:") } pub struct NinjaWriter { output: T } impl NinjaWriter { pub fn new(output: T) -> Self { Self { output } } pub fn newline(&mut self) -> Result<(), io::Error> { writeln!(self.output) } pub fn comment(&mut self, text: &str) -> Result<(), io::Error> { writeln!(self.output, "# {}", text) } pub fn variable(&mut self, key: &str, value: &str, indent: usize) -> Result<(), io::Error> { if value.is_empty() { return Ok(()) } writeln!(self.output, "{}{} = {}", self._indent(indent), key, value) } pub fn pool(&mut self, name: &str, depth: usize) -> Result<(), io::Error> { writeln!(self.output, "pool {}", name)?; self.variable("depth", &depth.to_string(), 1) } pub fn rule(&mut self, name: &str, command: &str, description: Option<&str>, depfile: Option<&str>, generator: Option, pool: Option<&str>, restat: Option, rspfile: Option<&str>, rspfile_content: Option<&str>, deps: Option<&str>) -> Result<(), io::Error> { writeln!(self.output, "rule {}", name)?; self.variable("command", command, 1)?; if let Some(desc) = description { self.variable("description", desc, 1)?; } if let Some(depfile) = depfile { self.variable("depfile", depfile, 1)?; } if let Some(gen) = generator { if gen { self.variable("generator", "1", 1)?; } } if let Some(pool) = pool { self.variable("pool", pool, 1)?; } if let Some(restat) = restat { if restat { self.variable("restat", "1", 1)?; } } if let Some(rspfile) = rspfile { self.variable("rspfile", rspfile, 1)?; } if let Some(rspfile_content) = rspfile_content { self.variable("rspfile_content", rspfile_content, 1)?; } if let Some(deps) = deps { self.variable("deps", deps, 1)?; } Ok(()) } pub fn build(&mut self, outputs: Vec, rule: String, inputs: Vec, mut implicit: Vec, mut order_only: Vec, variables: HashMap, mut implicit_outputs: Vec, pool: Option, dyndep: Option) -> Result<(), io::Error> { let mut out_outputs: Vec = outputs.iter().map(|u| escape_path(u)).collect(); let mut all_inputs: Vec = inputs.iter().map(|u| escape_path(u)).collect(); if !implicit.is_empty() { all_inputs.push("|".to_string()); all_inputs.append(&mut implicit.iter_mut().map(|u| escape_path(u)).collect()); } if !order_only.is_empty() { all_inputs.push("||".to_string()); all_inputs.append(&mut order_only.iter_mut().map(|u| escape_path(u)).collect()); } if !implicit_outputs.is_empty() { out_outputs.push("|".to_string()); out_outputs.append(&mut implicit_outputs.iter_mut().map(|u| escape_path(u)).collect()); } all_inputs.insert(0, rule); writeln!(self.output, "build {}: {}", out_outputs.join(" "), all_inputs.join(" "))?; if let Some(pool) = pool { self.variable("pool", &pool, 1)?; } if let Some(dyndep) = dyndep { self.variable("dyndep", &dyndep, 1)?; } for (key, value) in variables { self.variable(&key, &value, 1)?; } Ok(()) } pub fn include(&mut self, path: &str) -> Result<(), io::Error> { writeln!(self.output, "include {}", path) } pub fn subninja(&mut self, path: &str) -> Result<(), io::Error> { writeln!(self.output, "subninja {}", path) } pub fn default(&mut self, paths: Vec) -> Result<(), io::Error> { writeln!(self.output, "default {}", paths.join(" ")) } fn _indent(&self, num: usize) -> String { " ".repeat(num) } } pub fn exec_ninja(root: &PathBuf, args: Vec) -> Result<(), Box> { println!("[exec] cmd: ninja {:?}", args); let stat = Command::new("ninja") .args(args) .current_dir(root) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .spawn()? .wait()?; if stat.success() { Ok(()) } else { Err(format!("ninja exited with error: {}", stat.code().unwrap()).into()) } } pub fn exec(cmd: &str, dir: &PathBuf, args: Vec) -> Result<(), Box> { println!("[exec] cmd: {} {:?}", cmd, args); let stat = Command::new(cmd) .args(args) .current_dir(dir) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .spawn()? .wait()?; if stat.success() { Ok(()) } else { Err(format!("{} exited with error: {}", cmd, stat.code().unwrap()).into()) } }