Add --quiet/-q flag to supress all output (#17)
This is for avoiding writing to stderr during tests, although it's a nice option so it feels worth exposing to users.
This commit is contained in:
parent
599cc80f86
commit
e4d35a8270
14
src/app.rs
14
src/app.rs
@ -30,6 +30,10 @@ pub fn app() {
|
|||||||
.short("l")
|
.short("l")
|
||||||
.long("list")
|
.long("list")
|
||||||
.help("Lists available recipes"))
|
.help("Lists available recipes"))
|
||||||
|
.arg(Arg::with_name("quiet")
|
||||||
|
.short("q")
|
||||||
|
.long("quiet")
|
||||||
|
.help("Suppress all output"))
|
||||||
.arg(Arg::with_name("dry-run")
|
.arg(Arg::with_name("dry-run")
|
||||||
.long("dry-run")
|
.long("dry-run")
|
||||||
.help("Print recipe text without executing"))
|
.help("Print recipe text without executing"))
|
||||||
@ -69,6 +73,11 @@ pub fn app() {
|
|||||||
die!("--justfile and --working-directory may only be used together");
|
die!("--justfile and --working-directory may only be used together");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --dry-run and --quiet don't make sense together
|
||||||
|
if matches.is_present("dry-run") && matches.is_present("quiet") {
|
||||||
|
die!("--dry-run and --quiet may not be used together");
|
||||||
|
}
|
||||||
|
|
||||||
let justfile_option = matches.value_of("justfile");
|
let justfile_option = matches.value_of("justfile");
|
||||||
let working_directory_option = matches.value_of("working-directory");
|
let working_directory_option = matches.value_of("working-directory");
|
||||||
|
|
||||||
@ -164,10 +173,13 @@ pub fn app() {
|
|||||||
dry_run: matches.is_present("dry-run"),
|
dry_run: matches.is_present("dry-run"),
|
||||||
evaluate: matches.is_present("evaluate"),
|
evaluate: matches.is_present("evaluate"),
|
||||||
overrides: overrides,
|
overrides: overrides,
|
||||||
|
quiet: matches.is_present("quiet"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(run_error) = justfile.run(&arguments, &options) {
|
if let Err(run_error) = justfile.run(&arguments, &options) {
|
||||||
warn!("{}", run_error);
|
if !options.quiet {
|
||||||
|
warn!("{}", run_error);
|
||||||
|
}
|
||||||
match run_error {
|
match run_error {
|
||||||
RunError::Code{code, .. } | RunError::BacktickCode{code, ..} => process::exit(code),
|
RunError::Code{code, .. } | RunError::BacktickCode{code, ..} => process::exit(code),
|
||||||
_ => process::exit(-1),
|
_ => process::exit(-1),
|
||||||
|
@ -575,3 +575,100 @@ fn line_error_spacing() {
|
|||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quiet_flag_no_stdout() {
|
||||||
|
integration_test(
|
||||||
|
&["--quiet"],
|
||||||
|
r#"
|
||||||
|
default:
|
||||||
|
@echo hello
|
||||||
|
"#,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quiet_flag_no_stderr() {
|
||||||
|
integration_test(
|
||||||
|
&["--quiet"],
|
||||||
|
r#"
|
||||||
|
default:
|
||||||
|
@echo hello 1>&2
|
||||||
|
"#,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quiet_flag_no_command_echoing() {
|
||||||
|
integration_test(
|
||||||
|
&["--quiet"],
|
||||||
|
r#"
|
||||||
|
default:
|
||||||
|
exit
|
||||||
|
"#,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quiet_flag_no_error_messages() {
|
||||||
|
integration_test(
|
||||||
|
&["--quiet"],
|
||||||
|
r#"
|
||||||
|
default:
|
||||||
|
exit 100
|
||||||
|
"#,
|
||||||
|
100,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quiet_flag_no_assignment_backtick_stderr() {
|
||||||
|
integration_test(
|
||||||
|
&["--quiet"],
|
||||||
|
r#"
|
||||||
|
a = `echo hello 1>&2`
|
||||||
|
default:
|
||||||
|
exit 100
|
||||||
|
"#,
|
||||||
|
100,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quiet_flag_no_interpolation_backtick_stderr() {
|
||||||
|
integration_test(
|
||||||
|
&["--quiet"],
|
||||||
|
r#"
|
||||||
|
default:
|
||||||
|
echo `echo hello 1>&2`
|
||||||
|
exit 100
|
||||||
|
"#,
|
||||||
|
100,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quiet_flag_or_dry_run_flag() {
|
||||||
|
integration_test(
|
||||||
|
&["--quiet", "--dry-run"],
|
||||||
|
r#""#,
|
||||||
|
255,
|
||||||
|
"",
|
||||||
|
"--dry-run and --quiet may not be used together\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
60
src/lib.rs
60
src/lib.rs
@ -172,14 +172,20 @@ fn run_backtick<'a>(
|
|||||||
token: &Token<'a>,
|
token: &Token<'a>,
|
||||||
scope: &Map<&'a str, String>,
|
scope: &Map<&'a str, String>,
|
||||||
exports: &Set<&'a str>,
|
exports: &Set<&'a str>,
|
||||||
|
quiet: bool,
|
||||||
) -> Result<String, RunError<'a>> {
|
) -> Result<String, RunError<'a>> {
|
||||||
let mut cmd = process::Command::new("sh");
|
let mut cmd = process::Command::new("sh");
|
||||||
|
|
||||||
try!(export_env(&mut cmd, scope, exports));
|
try!(export_env(&mut cmd, scope, exports));
|
||||||
|
|
||||||
cmd.arg("-cu")
|
cmd.arg("-cu")
|
||||||
.arg(raw)
|
.arg(raw);
|
||||||
.stderr(process::Stdio::inherit());
|
|
||||||
|
cmd.stderr(if quiet {
|
||||||
|
process::Stdio::null()
|
||||||
|
} else {
|
||||||
|
process::Stdio::inherit()
|
||||||
|
});
|
||||||
|
|
||||||
match cmd.output() {
|
match cmd.output() {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
@ -216,7 +222,7 @@ impl<'a> Recipe<'a> {
|
|||||||
arguments: &[&'a str],
|
arguments: &[&'a str],
|
||||||
scope: &Map<&'a str, String>,
|
scope: &Map<&'a str, String>,
|
||||||
exports: &Set<&'a str>,
|
exports: &Set<&'a str>,
|
||||||
dry_run: bool,
|
options: &RunOptions,
|
||||||
) -> Result<(), RunError<'a>> {
|
) -> Result<(), RunError<'a>> {
|
||||||
let argument_map = arguments .iter().enumerate()
|
let argument_map = arguments .iter().enumerate()
|
||||||
.map(|(i, argument)| (self.parameters[i], *argument)).collect();
|
.map(|(i, argument)| (self.parameters[i], *argument)).collect();
|
||||||
@ -227,6 +233,7 @@ impl<'a> Recipe<'a> {
|
|||||||
exports: exports,
|
exports: exports,
|
||||||
assignments: &Map::new(),
|
assignments: &Map::new(),
|
||||||
overrides: &Map::new(),
|
overrides: &Map::new(),
|
||||||
|
quiet: options.quiet,
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.shebang {
|
if self.shebang {
|
||||||
@ -235,7 +242,7 @@ 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 {
|
if options.dry_run {
|
||||||
for line in evaluated_lines {
|
for line in evaluated_lines {
|
||||||
warn!("{}", line);
|
warn!("{}", line);
|
||||||
}
|
}
|
||||||
@ -305,14 +312,14 @@ 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();
|
||||||
let quiet = command.starts_with('@');
|
let quiet_command = command.starts_with('@');
|
||||||
if quiet {
|
if quiet_command {
|
||||||
command = &command[1..];
|
command = &command[1..];
|
||||||
}
|
}
|
||||||
if dry_run || !quiet {
|
if options.dry_run || !(quiet_command || options.quiet) {
|
||||||
warn!("{}", command);
|
warn!("{}", command);
|
||||||
}
|
}
|
||||||
if dry_run {
|
if options.dry_run {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,6 +327,11 @@ impl<'a> Recipe<'a> {
|
|||||||
|
|
||||||
cmd.arg("-cu").arg(command);
|
cmd.arg("-cu").arg(command);
|
||||||
|
|
||||||
|
if options.quiet {
|
||||||
|
cmd.stderr(process::Stdio::null());
|
||||||
|
cmd.stdout(process::Stdio::null());
|
||||||
|
}
|
||||||
|
|
||||||
try!(export_env(&mut cmd, scope, exports));
|
try!(export_env(&mut cmd, scope, exports));
|
||||||
|
|
||||||
try!(match cmd.status() {
|
try!(match cmd.status() {
|
||||||
@ -543,13 +555,15 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
|||||||
fn evaluate_assignments<'a>(
|
fn evaluate_assignments<'a>(
|
||||||
assignments: &Map<&'a str, Expression<'a>>,
|
assignments: &Map<&'a str, Expression<'a>>,
|
||||||
overrides: &Map<&str, &str>,
|
overrides: &Map<&str, &str>,
|
||||||
|
quiet: bool,
|
||||||
) -> Result<Map<&'a str, String>, RunError<'a>> {
|
) -> Result<Map<&'a str, String>, RunError<'a>> {
|
||||||
let mut evaluator = Evaluator {
|
let mut evaluator = Evaluator {
|
||||||
evaluated: Map::new(),
|
|
||||||
scope: &Map::new(),
|
|
||||||
exports: &Set::new(),
|
|
||||||
assignments: assignments,
|
assignments: assignments,
|
||||||
|
evaluated: Map::new(),
|
||||||
|
exports: &Set::new(),
|
||||||
overrides: overrides,
|
overrides: overrides,
|
||||||
|
quiet: quiet,
|
||||||
|
scope: &Map::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for name in assignments.keys() {
|
for name in assignments.keys() {
|
||||||
@ -560,11 +574,12 @@ fn evaluate_assignments<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct Evaluator<'a: 'b, 'b> {
|
struct Evaluator<'a: 'b, 'b> {
|
||||||
evaluated: Map<&'a str, String>,
|
|
||||||
scope: &'b Map<&'a str, String>,
|
|
||||||
exports: &'b Set<&'a str>,
|
|
||||||
assignments: &'b Map<&'a str, Expression<'a>>,
|
assignments: &'b Map<&'a str, Expression<'a>>,
|
||||||
|
evaluated: Map<&'a str, String>,
|
||||||
|
exports: &'b Set<&'a str>,
|
||||||
overrides: &'b Map<&'b str, &'b str>,
|
overrides: &'b Map<&'b str, &'b str>,
|
||||||
|
quiet: bool,
|
||||||
|
scope: &'b Map<&'a str, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Evaluator<'a, 'b> {
|
impl<'a, 'b> Evaluator<'a, 'b> {
|
||||||
@ -630,7 +645,7 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
|||||||
}
|
}
|
||||||
Expression::String{ref cooked, ..} => cooked.clone(),
|
Expression::String{ref cooked, ..} => cooked.clone(),
|
||||||
Expression::Backtick{raw, ref token} => {
|
Expression::Backtick{raw, ref token} => {
|
||||||
try!(run_backtick(raw, token, &self.scope, &self.exports))
|
try!(run_backtick(raw, token, &self.scope, &self.exports, self.quiet))
|
||||||
}
|
}
|
||||||
Expression::Concatination{ref lhs, ref rhs} => {
|
Expression::Concatination{ref lhs, ref rhs} => {
|
||||||
try!(self.evaluate_expression(lhs, arguments))
|
try!(self.evaluate_expression(lhs, arguments))
|
||||||
@ -837,6 +852,7 @@ struct RunOptions<'a> {
|
|||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
evaluate: bool,
|
evaluate: bool,
|
||||||
overrides: Map<&'a str, &'a str>,
|
overrides: Map<&'a str, &'a str>,
|
||||||
|
quiet: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
||||||
@ -865,7 +881,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
fn run(
|
fn run(
|
||||||
&'a self,
|
&'a self,
|
||||||
arguments: &[&'a str],
|
arguments: &[&'a str],
|
||||||
options: &RunOptions<'a>,
|
options: &RunOptions<'a>,
|
||||||
) -> Result<(), RunError<'a>> {
|
) -> Result<(), RunError<'a>> {
|
||||||
let unknown_overrides = options.overrides.keys().cloned()
|
let unknown_overrides = options.overrides.keys().cloned()
|
||||||
.filter(|name| !self.assignments.contains_key(name))
|
.filter(|name| !self.assignments.contains_key(name))
|
||||||
@ -875,7 +891,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
return Err(RunError::UnknownOverrides{overrides: unknown_overrides});
|
return Err(RunError::UnknownOverrides{overrides: unknown_overrides});
|
||||||
}
|
}
|
||||||
|
|
||||||
let scope = try!(evaluate_assignments(&self.assignments, &options.overrides));
|
let scope = try!(evaluate_assignments(&self.assignments, &options.overrides, options.quiet));
|
||||||
if options.evaluate {
|
if options.evaluate {
|
||||||
for (name, value) in scope {
|
for (name, value) in scope {
|
||||||
println!("{} = \"{}\"", name, value);
|
println!("{} = \"{}\"", name, value);
|
||||||
@ -899,7 +915,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
expected: recipe.parameters.len(),
|
expected: recipe.parameters.len(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
try!(self.run_recipe(recipe, rest, &scope, &mut ran, options.dry_run));
|
try!(self.run_recipe(recipe, rest, &scope, &mut ran, options));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -917,7 +933,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, options.dry_run));
|
try!(self.run_recipe(recipe, &[], &scope, &mut ran, options));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -928,14 +944,14 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
arguments: &[&'a str],
|
arguments: &[&'a str],
|
||||||
scope: &Map<&'c str, String>,
|
scope: &Map<&'c str, String>,
|
||||||
ran: &mut Set<&'a str>,
|
ran: &mut Set<&'a str>,
|
||||||
dry_run: bool,
|
options: &RunOptions<'a>,
|
||||||
) -> 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, dry_run));
|
try!(self.run_recipe(&self.recipes[dependency_name], &[], scope, ran, options));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try!(recipe.run(arguments, &scope, &self.exports, dry_run));
|
try!(recipe.run(arguments, &scope, &self.exports, options));
|
||||||
ran.insert(recipe.name);
|
ran.insert(recipe.name);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user