mars/src/lib.rs

198 lines
4.7 KiB
Rust

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<ExitStatus, io::Error> {
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<I, S>(command: &str, path: &str, args: I) -> Result<ExitStatus, io::Error> where
I: IntoIterator<Item=S>,
S: AsRef<OsStr> {
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<Box<Builder>>) -> 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<T, E>(result: Result<T, E>) -> 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<Box<Builder>>,
}
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()))
}
}