extern crate hyper; use std::io; use std::process::{Command, ExitStatus}; use std::path::PathBuf; use std::ffi::OsStr; use std::fs::create_dir_all; macro_rules! destroy { ($e: expr) => { match $e { Err(_) => { let err: Result<(), ()> = Err(()); return err; }, _ => (), } } } /// Tests if a file is in a directory. pub fn contains_file(path: &str, file: &str) -> bool { let mut path = PathBuf::from(path); path.push(file); path.exists() } /// Tries to build a certain directory using the specified builders. pub fn run_command(command: &str, path: &str) -> Result { let mut child = match Command::new(command) .current_dir(path) .spawn() { Err(x) => return Err(x), Ok(child) => child, }; child.wait() } /// Run a build commands, and wait untils its finished, returning its result. pub fn run_command_with_args(command: &str, path: &str, args: I) -> Result where I: IntoIterator, S: AsRef { let mut child = Command::new(command) .current_dir(path) .args(args) .spawn()?; child.wait() } /// Tries to build a certain directory using the specified builders. pub fn build(path: &PathBuf, builders: &Vec>) -> Result<(), ()> { let mut path = path.clone(); loop { if path.to_str().unwrap() == "/" { // Couldn't find a buildable directory return Err(()); } for builder in builders { if builder.is_buildable(path.to_str().unwrap()) { builder.build(path.to_str().unwrap())?; return Ok(()); } } path.pop(); }; } /// Destucture a Result to return a Result<(), ()> pub fn destroy(result: Result) -> Result<(), ()> { match result { Err(_) => Err(()), Ok(_) => Ok(()), } } /// A general builders that contains many builders and can build any type of code. pub struct GeneralBuilder { /// The builders contained. builders: Vec>, } impl GeneralBuilder { /// Creates a new general builder with the defaults builders. pub fn new() -> GeneralBuilder { GeneralBuilder { builders: vec![ Box::new(MakeBuilder::new()), Box::new(CMakeBuilder::new()), Box::new(CargoBuilder::new()), ], } } /// Triggers a build. pub fn build(&self, path: &str) -> Result<(), ()> { build(&PathBuf::from(path), &self.builders) } } /// A generic builder. It can build some projects. pub trait Builder { /// Checks if a directory has corresponding files so it can build it. /// e.g.: if there is a Makefile fn is_buildable(&self, path: &str) -> bool; /// Trigger all the commands to build the project. fn build(&self, path: &str) -> Result<(), ()>; } /// The builder that looks for makefiles. pub struct MakeBuilder; impl MakeBuilder { /// Creates a new make builder. pub fn new() -> MakeBuilder { MakeBuilder } } impl Builder for MakeBuilder { fn is_buildable(&self, path: &str) -> bool { contains_file(path, "Makefile") } fn build(&self, path: &str) -> Result<(), ()> { destroy!(run_command("make", path)); Ok(()) } } /// The builder that builds cmake projects. pub struct CMakeBuilder; impl CMakeBuilder { /// Creates a new cmake builder. pub fn new() -> CMakeBuilder { CMakeBuilder } } impl Builder for CMakeBuilder { fn is_buildable(&self, path: &str) -> bool { contains_file(path, "CMakeLists.txt") } fn build(&self, path: &str) -> Result<(), ()> { let mut build = PathBuf::from(path); build.push("build"); // If there's a build/Makefile, we'll only make if ! contains_file(path, "build/Makefile") { // Create build directory destroy!(create_dir_all(&build)); // Run cmake .. in build directory destroy!(run_command_with_args("cmake", build.to_str().unwrap(), [".."].iter())); } // Run make in build directory destroy!(run_command("make", build.to_str().unwrap())); Ok(()) } } /// The builder that looks for Cargo.toml. pub struct CargoBuilder; impl CargoBuilder { /// Creates a new cargo builder. pub fn new() -> CargoBuilder { CargoBuilder } } impl Builder for CargoBuilder { fn is_buildable(&self, path: &str) -> bool { contains_file(path, "Cargo.toml") } fn build(&self, path: &str) -> Result<(), ()> { destroy(run_command_with_args("cargo", path, ["build"].iter())) } }