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]
name = "j"
version = "0.2.7"
version = "0.2.8"
authors = ["Casey Rodarmor <casey@rodarmor.com>"]
license = "WTFPL/MIT/Apache-2.0"
description = "a command runner"

19
notes
View File

@ -1,15 +1,8 @@
notes
-----
todo
----
- --debug tests that:
- 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:
before release
--------------
- where can users get help?
- irc, email, github, mailing list
@ -54,7 +47,8 @@ notes
. irc
. r/rust
enhancements:
enhancements
------------
- lazy assignment: don't evaluate unused assignments
- use cow strings where we currently use String
@ -66,6 +60,7 @@ enhancements:
. just ../foo # ../justfile:foo
. just xyz/foo # xyz/justfile:foo
. just xyz/ # xyz/justfile:DEFAULT
. path prefix is starting dir, so just ../foo can run ../../justfile:foo
- allow setting and exporting environment variables
. export a as "HELLO_BAR"
. export a

View File

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

View File

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

View File

@ -579,7 +579,7 @@ fn conjoin_and() {
#[test]
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"]),
other => panic!("expected an unknown recipe error, but got: {}", other),
}
@ -747,7 +747,7 @@ a:
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} => {
assert_eq!(recipe, "a");
assert_eq!(code, 200);
@ -758,7 +758,7 @@ a:
#[test]
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} => {
assert_eq!(recipe, "fail");
assert_eq!(code, 100);
@ -773,7 +773,7 @@ fn run_args() {
a return code:
@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} => {
assert_eq!(recipe, "a");
assert_eq!(code, 150);
@ -784,7 +784,7 @@ a return code:
#[test]
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} => {
assert_eq!(recipe, "a");
assert_eq!(found, 2);
@ -796,7 +796,7 @@ fn missing_args() {
#[test]
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} => {
assert_eq!(recipe, "a");
assert_eq!(found, 0);
@ -808,7 +808,7 @@ fn missing_default() {
#[test]
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} => {
assert_eq!(code, 100);
assert_eq!(token.lexeme, "`function f { return 100; }; f`");
@ -823,7 +823,7 @@ fn unknown_overrides() {
overrides.insert("foo", "bar");
overrides.insert("baz", "bob");
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} => {
assert_eq!(overrides, &["baz", "foo"]);
},