Bump version, add --debug

This commit is contained in:
Casey Rodarmor 2016-10-30 13:14:39 -07:00
parent 9a368fb351
commit cc683cbb04
6 changed files with 120 additions and 42 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "j" name = "j"
version = "0.2.7" version = "0.2.8"
authors = ["Casey Rodarmor <casey@rodarmor.com>"] authors = ["Casey Rodarmor <casey@rodarmor.com>"]
license = "WTFPL/MIT/Apache-2.0" license = "WTFPL/MIT/Apache-2.0"
description = "a command runner" description = "a command runner"

19
notes
View File

@ -1,15 +1,8 @@
notes todo
----- ----
- --debug tests that: before release
- should consider renaming it to --evaluate if it actually evaluates stuff --------------
- test values of assignments
- test values of interpolations
- test results of concatination
- test string escape parsing
- should it evaluate `` in recipes?
- before release:
- where can users get help? - where can users get help?
- irc, email, github, mailing list - irc, email, github, mailing list
@ -54,7 +47,8 @@ notes
. irc . irc
. r/rust . r/rust
enhancements: enhancements
------------
- lazy assignment: don't evaluate unused assignments - lazy assignment: don't evaluate unused assignments
- use cow strings where we currently use String - use cow strings where we currently use String
@ -66,6 +60,7 @@ enhancements:
. just ../foo # ../justfile:foo . just ../foo # ../justfile:foo
. just xyz/foo # xyz/justfile:foo . just xyz/foo # xyz/justfile:foo
. just xyz/ # xyz/justfile:DEFAULT . just xyz/ # xyz/justfile:DEFAULT
. path prefix is starting dir, so just ../foo can run ../../justfile:foo
- allow setting and exporting environment variables - allow setting and exporting environment variables
. export a as "HELLO_BAR" . export a as "HELLO_BAR"
. export a . export a

View File

@ -23,16 +23,19 @@ macro_rules! die {
pub fn app() { pub fn app() {
let matches = App::new("j") let matches = App::new("j")
.version("0.2.7") .version("0.2.8")
.author("Casey R. <casey@rodarmor.com>") .author("Casey Rodarmor <casey@rodarmor.com>")
.about("Just a command runner - https://github.com/casey/j") .about("Just a command runner - https://github.com/casey/j")
.arg(Arg::with_name("list") .arg(Arg::with_name("list")
.short("l") .short("l")
.long("list") .long("list")
.help("Lists available recipes")) .help("Lists available recipes"))
.arg(Arg::with_name("debug") .arg(Arg::with_name("dry-run")
.long("debug") .long("dry-run")
.help("Prints the justfile with debugging information, such as evaluated expression and assignment")) .help("Print recipe text without executing"))
.arg(Arg::with_name("evaluate")
.long("evaluate")
.help("Print evaluated variables"))
.arg(Arg::with_name("show") .arg(Arg::with_name("show")
.short("s") .short("s")
.long("show") .long("show")
@ -108,11 +111,6 @@ pub fn app() {
let justfile = super::parse(&text).unwrap_or_else(|error| die!("{}", error)); let justfile = super::parse(&text).unwrap_or_else(|error| die!("{}", error));
if matches.is_present("debug") {
println!("{:#}", justfile);
process::exit(0);
}
if matches.is_present("list") { if matches.is_present("list") {
if justfile.count() == 0 { if justfile.count() == 0 {
warn!("Justfile contains no recipes"); warn!("Justfile contains no recipes");
@ -162,7 +160,10 @@ pub fn app() {
die!("Justfile contains no recipes"); die!("Justfile contains no recipes");
}; };
if let Err(run_error) = justfile.run(&overrides, &arguments) { let dry_run = matches.is_present("dry-run");
let evaluate = matches.is_present("evaluate");
if let Err(run_error) = justfile.run(&overrides, &arguments, dry_run, evaluate) {
warn!("{}", run_error); warn!("{}", run_error);
match run_error { match run_error {
super::RunError::Code{code, .. } => process::exit(code), super::RunError::Code{code, .. } => process::exit(code),

View File

@ -398,3 +398,62 @@ recipe arg:
"echo arg=baz=bar\necho barbbaz\n", "echo arg=baz=bar\necho barbbaz\n",
); );
} }
// shebangs are printed
#[test]
fn dry_run() {
integration_test(
"dry_run",
&["--dry-run", "shebang", "command"],
r#"
var = `echo stderr 1>&2; echo backtick`
command:
@touch /this/is/not/a/file
{{var}}
echo {{`echo command interpolation`}}
shebang:
#!/bin/sh
touch /this/is/not/a/file
{{var}}
echo {{`echo shebang interpolation`}}"#,
0,
"",
"stderr
#!/bin/sh
touch /this/is/not/a/file
backtick
echo shebang interpolation
touch /this/is/not/a/file
backtick
echo command interpolation
",
);
}
#[test]
fn evaluate() {
integration_test(
"evaluate",
&["--evaluate"],
r#"
foo = "a\t"
baz = "c"
bar = "b\t"
abc = foo + bar + baz
wut:
touch /this/is/not/a/file
"#,
0,
r#"abc = "a b c"
bar = "b "
baz = "c"
foo = "a "
"#,
"",
);
}

View File

@ -188,7 +188,8 @@ impl<'a> Recipe<'a> {
fn run( fn run(
&self, &self,
arguments: &[&'a str], arguments: &[&'a str],
scope: &BTreeMap<&'a str, String> scope: &BTreeMap<&'a str, String>,
dry_run: bool,
) -> Result<(), RunError<'a>> { ) -> Result<(), RunError<'a>> {
let argument_map = arguments .iter().enumerate() let argument_map = arguments .iter().enumerate()
.map(|(i, argument)| (self.arguments[i], *argument)).collect(); .map(|(i, argument)| (self.arguments[i], *argument)).collect();
@ -206,6 +207,13 @@ impl<'a> Recipe<'a> {
evaluated_lines.push(try!(evaluator.evaluate_line(&line, &argument_map))); evaluated_lines.push(try!(evaluator.evaluate_line(&line, &argument_map)));
} }
if dry_run {
for line in evaluated_lines {
warn!("{}", line);
}
return Ok(());
}
let tmp = try!( let tmp = try!(
tempdir::TempDir::new("j") tempdir::TempDir::new("j")
.map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error}) .map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error})
@ -266,11 +274,16 @@ impl<'a> Recipe<'a> {
for line in &self.lines { for line in &self.lines {
let evaluated = &try!(evaluator.evaluate_line(&line, &argument_map)); let evaluated = &try!(evaluator.evaluate_line(&line, &argument_map));
let mut command = evaluated.as_str(); let mut command = evaluated.as_str();
if !command.starts_with('@') { let quiet = command.starts_with('@');
warn!("{}", command); if quiet {
} else {
command = &command[1..]; command = &command[1..];
} }
if dry_run || !quiet {
warn!("{}", command);
}
if dry_run {
continue;
}
let status = process::Command::new("sh") let status = process::Command::new("sh")
.arg("-cu") .arg("-cu")
.arg(command) .arg(command)
@ -809,7 +822,9 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
fn run( fn run(
&'a self, &'a self,
overrides: &BTreeMap<&'a str, &'a str>, overrides: &BTreeMap<&'a str, &'a str>,
arguments: &[&'a str] arguments: &[&'a str],
dry_run: bool,
evaluate: bool,
) -> Result<(), RunError<'a>> { ) -> Result<(), RunError<'a>> {
let unknown_overrides = overrides.keys().cloned() let unknown_overrides = overrides.keys().cloned()
.filter(|name| !self.assignments.contains_key(name)) .filter(|name| !self.assignments.contains_key(name))
@ -820,6 +835,13 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
} }
let scope = try!(evaluate_assignments(&self.assignments, overrides)); let scope = try!(evaluate_assignments(&self.assignments, overrides));
if evaluate {
for (name, value) in scope {
println!("{} = \"{}\"", name, value);
}
return Ok(());
}
let mut ran = HashSet::new(); let mut ran = HashSet::new();
for (i, argument) in arguments.iter().enumerate() { for (i, argument) in arguments.iter().enumerate() {
@ -836,7 +858,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
expected: recipe.arguments.len(), expected: recipe.arguments.len(),
}); });
} }
try!(self.run_recipe(recipe, rest, &scope, &mut ran)); try!(self.run_recipe(recipe, rest, &scope, &mut ran, dry_run));
return Ok(()); return Ok(());
} }
} else { } else {
@ -854,7 +876,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
return Err(RunError::UnknownRecipes{recipes: missing}); return Err(RunError::UnknownRecipes{recipes: missing});
} }
for recipe in arguments.iter().map(|name| &self.recipes[name]) { for recipe in arguments.iter().map(|name| &self.recipes[name]) {
try!(self.run_recipe(recipe, &[], &scope, &mut ran)); try!(self.run_recipe(recipe, &[], &scope, &mut ran, dry_run));
} }
Ok(()) Ok(())
} }
@ -864,14 +886,15 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
recipe: &Recipe<'a>, recipe: &Recipe<'a>,
arguments: &[&'a str], arguments: &[&'a str],
scope: &BTreeMap<&'c str, String>, scope: &BTreeMap<&'c str, String>,
ran: &mut HashSet<&'a str> ran: &mut HashSet<&'a str>,
dry_run: bool,
) -> Result<(), RunError> { ) -> Result<(), RunError> {
for dependency_name in &recipe.dependencies { for dependency_name in &recipe.dependencies {
if !ran.contains(dependency_name) { if !ran.contains(dependency_name) {
try!(self.run_recipe(&self.recipes[dependency_name], &[], scope, ran)); try!(self.run_recipe(&self.recipes[dependency_name], &[], scope, ran, dry_run));
} }
} }
try!(recipe.run(arguments, &scope)); try!(recipe.run(arguments, &scope, dry_run));
ran.insert(recipe.name); ran.insert(recipe.name);
Ok(()) Ok(())
} }

View File

@ -579,7 +579,7 @@ fn conjoin_and() {
#[test] #[test]
fn unknown_recipes() { fn unknown_recipes() {
match parse_success("a:\nb:\nc:").run(&BTreeMap::new(), &["a", "x", "y", "z"]).unwrap_err() { match parse_success("a:\nb:\nc:").run(&BTreeMap::new(), &["a", "x", "y", "z"], false, false).unwrap_err() {
RunError::UnknownRecipes{recipes} => assert_eq!(recipes, &["x", "y", "z"]), RunError::UnknownRecipes{recipes} => assert_eq!(recipes, &["x", "y", "z"]),
other => panic!("expected an unknown recipe error, but got: {}", other), other => panic!("expected an unknown recipe error, but got: {}", other),
} }
@ -747,7 +747,7 @@ a:
x x
"; ";
match parse_success(text).run(&BTreeMap::new(), &["a"]).unwrap_err() { match parse_success(text).run(&BTreeMap::new(), &["a"], false, false).unwrap_err() {
RunError::Code{recipe, code} => { RunError::Code{recipe, code} => {
assert_eq!(recipe, "a"); assert_eq!(recipe, "a");
assert_eq!(code, 200); assert_eq!(code, 200);
@ -758,7 +758,7 @@ a:
#[test] #[test]
fn code_error() { fn code_error() {
match parse_success("fail:\n @function x { return 100; }; x").run(&BTreeMap::new(), &["fail"]).unwrap_err() { match parse_success("fail:\n @function x { return 100; }; x").run(&BTreeMap::new(), &["fail"], false, false).unwrap_err() {
RunError::Code{recipe, code} => { RunError::Code{recipe, code} => {
assert_eq!(recipe, "fail"); assert_eq!(recipe, "fail");
assert_eq!(code, 100); assert_eq!(code, 100);
@ -773,7 +773,7 @@ fn run_args() {
a return code: a return code:
@function x { {{return}} {{code + "0"}}; }; x"#; @function x { {{return}} {{code + "0"}}; }; x"#;
match parse_success(text).run(&BTreeMap::new(), &["a", "return", "15"]).unwrap_err() { match parse_success(text).run(&BTreeMap::new(), &["a", "return", "15"], false, false).unwrap_err() {
RunError::Code{recipe, code} => { RunError::Code{recipe, code} => {
assert_eq!(recipe, "a"); assert_eq!(recipe, "a");
assert_eq!(code, 150); assert_eq!(code, 150);
@ -784,7 +784,7 @@ a return code:
#[test] #[test]
fn missing_args() { fn missing_args() {
match parse_success("a b c d:").run(&BTreeMap::new(), &["a", "b", "c"]).unwrap_err() { match parse_success("a b c d:").run(&BTreeMap::new(), &["a", "b", "c"], false, false).unwrap_err() {
RunError::ArgumentCountMismatch{recipe, found, expected} => { RunError::ArgumentCountMismatch{recipe, found, expected} => {
assert_eq!(recipe, "a"); assert_eq!(recipe, "a");
assert_eq!(found, 2); assert_eq!(found, 2);
@ -796,7 +796,7 @@ fn missing_args() {
#[test] #[test]
fn missing_default() { fn missing_default() {
match parse_success("a b c d:\n echo {{b}}{{c}}{{d}}").run(&BTreeMap::new(), &["a"]).unwrap_err() { match parse_success("a b c d:\n echo {{b}}{{c}}{{d}}").run(&BTreeMap::new(), &["a"], false, false).unwrap_err() {
RunError::ArgumentCountMismatch{recipe, found, expected} => { RunError::ArgumentCountMismatch{recipe, found, expected} => {
assert_eq!(recipe, "a"); assert_eq!(recipe, "a");
assert_eq!(found, 0); assert_eq!(found, 0);
@ -808,7 +808,7 @@ fn missing_default() {
#[test] #[test]
fn backtick_code() { fn backtick_code() {
match parse_success("a:\n echo {{`function f { return 100; }; f`}}").run(&BTreeMap::new(), &["a"]).unwrap_err() { match parse_success("a:\n echo {{`function f { return 100; }; f`}}").run(&BTreeMap::new(), &["a"], false, false).unwrap_err() {
RunError::BacktickCode{code, token} => { RunError::BacktickCode{code, token} => {
assert_eq!(code, 100); assert_eq!(code, 100);
assert_eq!(token.lexeme, "`function f { return 100; }; f`"); assert_eq!(token.lexeme, "`function f { return 100; }; f`");
@ -823,7 +823,7 @@ fn unknown_overrides() {
overrides.insert("foo", "bar"); overrides.insert("foo", "bar");
overrides.insert("baz", "bob"); overrides.insert("baz", "bob");
match parse_success("a:\n echo {{`function f { return 100; }; f`}}") match parse_success("a:\n echo {{`function f { return 100; }; f`}}")
.run(&overrides, &["a"]).unwrap_err() { .run(&overrides, &["a"], false, false).unwrap_err() {
RunError::UnknownOverrides{overrides} => { RunError::UnknownOverrides{overrides} => {
assert_eq!(overrides, &["baz", "foo"]); assert_eq!(overrides, &["baz", "foo"]);
}, },