Support for polyglot recipes!

This commit is contained in:
Casey Rodarmor 2016-10-07 17:56:52 -07:00
parent 2e97f3f026
commit 25785eea77

View File

@ -2,6 +2,7 @@
mod tests; mod tests;
extern crate regex; extern crate regex;
extern crate tempdir;
use std::io::prelude::*; use std::io::prelude::*;
@ -10,6 +11,8 @@ use std::collections::{BTreeMap, BTreeSet, HashSet};
use std::fmt::Display; use std::fmt::Display;
use regex::Regex; use regex::Regex;
use std::os::unix::fs::PermissionsExt;
macro_rules! warn { macro_rules! warn {
($($arg:tt)*) => {{ ($($arg:tt)*) => {{
extern crate std; extern crate std;
@ -81,7 +84,64 @@ fn error_from_signal<'a>(recipe: &'a str, exit_status: process::ExitStatus) -> R
impl<'a> Recipe<'a> { impl<'a> Recipe<'a> {
fn run(&self) -> Result<(), RunError<'a>> { fn run(&self) -> Result<(), RunError<'a>> {
// TODO: if shebang, run as script if self.shebang {
let tmp = try!(
tempdir::TempDir::new("j")
.map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error})
);
let mut path = tmp.path().to_path_buf();
path.push(self.name);
{
let mut f = try!(
fs::File::create(&path)
.map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error})
);
let mut text = String::new();
// add the shebang
text += self.lines[0];
text += "\n";
// add blank lines so that lines in the generated script
// have the same line number as the corresponding lines
// in the justfile
for _ in 1..(self.line_number + 2) {
text += "\n"
}
for line in &self.lines[1..] {
text += line;
text += "\n";
}
try!(
f.write_all(text.as_bytes())
.map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error})
);
}
// get current permissions
let mut perms = try!(
fs::metadata(&path)
.map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error})
).permissions();
// make the script executable
let current_mode = perms.mode();
perms.set_mode(current_mode | 0o100);
try!(fs::set_permissions(&path, perms).map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error}));
// run it!
let status = process::Command::new(path).status();
try!(match status {
Ok(exit_status) => if let Some(code) = exit_status.code() {
if code == 0 {
Ok(())
} else {
Err(RunError::Code{recipe: self.name, code: code})
}
} else {
Err(error_from_signal(self.name, exit_status))
},
Err(io_error) => Err(RunError::TmpdirIoError{recipe: self.name, io_error: io_error})
});
} else {
for command in &self.lines { for command in &self.lines {
let mut command = *command; let mut command = *command;
if !command.starts_with("@") { if !command.starts_with("@") {
@ -106,6 +166,7 @@ impl<'a> Recipe<'a> {
Err(io_error) => Err(RunError::IoError{recipe: self.name, io_error: io_error}) Err(io_error) => Err(RunError::IoError{recipe: self.name, io_error: io_error})
}); });
} }
}
Ok(()) Ok(())
} }
} }
@ -261,8 +322,8 @@ impl<'a> Display for Error<'a> {
match self.text.lines().nth(self.line) { match self.text.lines().nth(self.line) {
Some(line) => try!(write!(f, "{}", line)), Some(line) => try!(write!(f, "{}", line)),
None => die!("internal error: Error has invalid line number: {}", self.line), None => try!(write!(f, "internal error: Error has invalid line number: {}", self.line)),
} };
Ok(()) Ok(())
} }
@ -338,6 +399,7 @@ pub enum RunError<'a> {
Code{recipe: &'a str, code: i32}, Code{recipe: &'a str, code: i32},
UnknownFailure{recipe: &'a str}, UnknownFailure{recipe: &'a str},
IoError{recipe: &'a str, io_error: io::Error}, IoError{recipe: &'a str, io_error: io::Error},
TmpdirIoError{recipe: &'a str, io_error: io::Error},
} }
impl<'a> Display for RunError<'a> { impl<'a> Display for RunError<'a> {
@ -366,6 +428,8 @@ impl<'a> Display for RunError<'a> {
_ => write!(f, "Recipe \"{}\" could not be run because of an IO error while launching the `sh`:\n{}", recipe, io_error), _ => write!(f, "Recipe \"{}\" could not be run because of an IO error while launching the `sh`:\n{}", recipe, io_error),
}); });
}, },
&RunError::TmpdirIoError{recipe, ref io_error} =>
try!(write!(f, "Recipe \"{}\" could not be run because of an IO error while trying to create a temporary directory or write a file to that directory`:\n{}", recipe, io_error)),
} }
Ok(()) Ok(())
} }