parent
babe97bf0d
commit
112462ec62
@ -48,7 +48,7 @@ expression : STRING
|
||||
| BACKTICK
|
||||
| expression '+' expression
|
||||
|
||||
recipe : NAME argument* ':' dependencies? body?
|
||||
recipe : '@'? NAME argument* ':' dependencies? body?
|
||||
|
||||
argument : NAME
|
||||
| NAME '=' STRING
|
||||
|
18
README.md
18
README.md
@ -73,6 +73,24 @@ Another recipe.
|
||||
|
||||
`just` prints each command to standard error before running it, which is why `echo 'This is a recipe!'` was printed. Lines starting with `@` will not be printed which is why `echo 'Another recipe.'` was not printed.
|
||||
|
||||
A recipe name may be prefixed with '@' to invert the meaning of '@' before each line:
|
||||
|
||||
```make
|
||||
@quiet:
|
||||
echo hello
|
||||
echo goodbye
|
||||
@# all done!
|
||||
```
|
||||
|
||||
Now only the lines starting with '@' will be echoed:
|
||||
|
||||
```sh
|
||||
$ j quiet
|
||||
hello
|
||||
goodbye
|
||||
# all done!
|
||||
```
|
||||
|
||||
Recipes stop running if a command fails. Here `cargo publish` will only run if `cargo test` succeeds:
|
||||
|
||||
```make
|
||||
|
@ -1205,3 +1205,34 @@ foo:
|
||||
"echo abc\n",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quiet_recipe() {
|
||||
integration_test(
|
||||
&[],
|
||||
r#"
|
||||
@quiet:
|
||||
# a
|
||||
# b
|
||||
@echo c
|
||||
"#,
|
||||
0,
|
||||
"c\n",
|
||||
"echo c\n",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quiet_shebang_recipe() {
|
||||
integration_test(
|
||||
&[],
|
||||
r#"
|
||||
@quiet:
|
||||
#!/bin/sh
|
||||
echo hello
|
||||
"#,
|
||||
0,
|
||||
"hello\n",
|
||||
"#!/bin/sh\necho hello\n",
|
||||
);
|
||||
}
|
||||
|
32
src/lib.rs
32
src/lib.rs
@ -72,6 +72,7 @@ struct Recipe<'a> {
|
||||
dependency_tokens: Vec<Token<'a>>,
|
||||
parameters: Vec<Parameter<'a>>,
|
||||
shebang: bool,
|
||||
quiet: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
@ -294,10 +295,13 @@ impl<'a> Recipe<'a> {
|
||||
evaluated_lines.push(evaluator.evaluate_line(line, &argument_map)?);
|
||||
}
|
||||
|
||||
if options.dry_run {
|
||||
for line in evaluated_lines {
|
||||
if options.dry_run || self.quiet {
|
||||
for line in &evaluated_lines {
|
||||
warn!("{}", line);
|
||||
}
|
||||
}
|
||||
|
||||
if options.dry_run {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -377,7 +381,7 @@ impl<'a> Recipe<'a> {
|
||||
if quiet_command {
|
||||
command = &command[1..];
|
||||
}
|
||||
if options.dry_run || !(quiet_command || options.quiet) {
|
||||
if options.dry_run || !((quiet_command ^ self.quiet) || options.quiet) {
|
||||
warn!("{}", command);
|
||||
}
|
||||
if options.dry_run {
|
||||
@ -1369,6 +1373,7 @@ impl<'a> Token<'a> {
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
enum TokenKind {
|
||||
At,
|
||||
Backtick,
|
||||
Colon,
|
||||
Comment,
|
||||
@ -1382,8 +1387,8 @@ enum TokenKind {
|
||||
Line,
|
||||
Name,
|
||||
Plus,
|
||||
StringToken,
|
||||
RawString,
|
||||
StringToken,
|
||||
Text,
|
||||
}
|
||||
|
||||
@ -1403,6 +1408,7 @@ impl Display for TokenKind {
|
||||
Line => "command",
|
||||
Name => "name",
|
||||
Plus => "\"+\"",
|
||||
At => "\"@\"",
|
||||
StringToken => "string",
|
||||
RawString => "raw string",
|
||||
Text => "command text",
|
||||
@ -1424,6 +1430,7 @@ fn tokenize(text: &str) -> Result<Vec<Token>, CompileError> {
|
||||
lazy_static! {
|
||||
static ref BACKTICK: Regex = token(r"`[^`\n\r]*`" );
|
||||
static ref COLON: Regex = token(r":" );
|
||||
static ref AT: Regex = token(r"@" );
|
||||
static ref COMMENT: Regex = token(r"#([^!].*)?$" );
|
||||
static ref EOF: Regex = token(r"(?-m)$" );
|
||||
static ref EOL: Regex = token(r"\n|\r\n" );
|
||||
@ -1585,6 +1592,8 @@ fn tokenize(text: &str) -> Result<Vec<Token>, CompileError> {
|
||||
(captures.at(1).unwrap(), captures.at(2).unwrap(), Backtick)
|
||||
} else if let Some(captures) = COLON.captures(rest) {
|
||||
(captures.at(1).unwrap(), captures.at(2).unwrap(), Colon)
|
||||
} else if let Some(captures) = AT.captures(rest) {
|
||||
(captures.at(1).unwrap(), captures.at(2).unwrap(), At)
|
||||
} else if let Some(captures) = PLUS.captures(rest) {
|
||||
(captures.at(1).unwrap(), captures.at(2).unwrap(), Plus)
|
||||
} else if let Some(captures) = EQUALS.captures(rest) {
|
||||
@ -1746,7 +1755,7 @@ impl<'a> Parser<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn recipe(&mut self, name: Token<'a>) -> Result<(), CompileError<'a>> {
|
||||
fn recipe(&mut self, name: Token<'a>, quiet: bool) -> Result<(), CompileError<'a>> {
|
||||
if let Some(recipe) = self.recipes.get(name.lexeme) {
|
||||
return Err(name.error(ErrorKind::DuplicateRecipe {
|
||||
recipe: recipe.name,
|
||||
@ -1871,6 +1880,7 @@ impl<'a> Parser<'a> {
|
||||
parameters: parameters,
|
||||
lines: lines,
|
||||
shebang: shebang,
|
||||
quiet: quiet,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
@ -1925,23 +1935,29 @@ impl<'a> Parser<'a> {
|
||||
Some(token) => match token.kind {
|
||||
Eof => break,
|
||||
Eol => continue,
|
||||
At => if let Some(name) = self.accept(Name) {
|
||||
self.recipe(name, true)?;
|
||||
} else {
|
||||
let unexpected = &self.tokens.next().unwrap();
|
||||
return Err(self.unexpected_token(unexpected, &[Name]));
|
||||
},
|
||||
Name => if token.lexeme == "export" {
|
||||
let next = self.tokens.next().unwrap();
|
||||
if next.kind == Name && self.accepted(Equals) {
|
||||
self.assignment(next, true)?;
|
||||
} else {
|
||||
self.tokens.put_back(next);
|
||||
self.recipe(token)?;
|
||||
self.recipe(token, false)?;
|
||||
}
|
||||
} else if self.accepted(Equals) {
|
||||
self.assignment(token, false)?;
|
||||
} else {
|
||||
self.recipe(token)?;
|
||||
self.recipe(token, false)?;
|
||||
},
|
||||
Comment => return Err(token.error(ErrorKind::InternalError {
|
||||
message: "found comment in token stream".to_string()
|
||||
})),
|
||||
_ => return return Err(self.unexpected_token(&token, &[Name])),
|
||||
_ => return return Err(self.unexpected_token(&token, &[Name, At])),
|
||||
},
|
||||
None => return Err(CompileError {
|
||||
text: self.text,
|
||||
|
35
src/unit.rs
35
src/unit.rs
@ -35,22 +35,23 @@ fn tokenize_error(text: &str, expected: CompileError) {
|
||||
fn token_summary(tokens: &[Token]) -> String {
|
||||
tokens.iter().map(|t| {
|
||||
match t.kind {
|
||||
super::TokenKind::Backtick => "`",
|
||||
super::TokenKind::Colon => ":",
|
||||
super::TokenKind::Comment{..} => "#",
|
||||
super::TokenKind::Dedent => "<",
|
||||
super::TokenKind::Eof => ".",
|
||||
super::TokenKind::Eol => "$",
|
||||
super::TokenKind::Equals => "=",
|
||||
super::TokenKind::Indent{..} => ">",
|
||||
super::TokenKind::InterpolationEnd => "}",
|
||||
super::TokenKind::InterpolationStart => "{",
|
||||
super::TokenKind::Line{..} => "^",
|
||||
super::TokenKind::Name => "N",
|
||||
super::TokenKind::Plus => "+",
|
||||
super::TokenKind::StringToken => "\"",
|
||||
super::TokenKind::RawString => "'",
|
||||
super::TokenKind::Text => "_",
|
||||
At => "@",
|
||||
Backtick => "`",
|
||||
Colon => ":",
|
||||
Comment{..} => "#",
|
||||
Dedent => "<",
|
||||
Eof => ".",
|
||||
Eol => "$",
|
||||
Equals => "=",
|
||||
Indent{..} => ">",
|
||||
InterpolationEnd => "}",
|
||||
InterpolationStart => "{",
|
||||
Line{..} => "^",
|
||||
Name => "N",
|
||||
Plus => "+",
|
||||
RawString => "'",
|
||||
StringToken => "\"",
|
||||
Text => "_",
|
||||
}
|
||||
}).collect::<Vec<_>>().join("")
|
||||
}
|
||||
@ -744,7 +745,7 @@ fn interpolation_outside_of_recipe() {
|
||||
line: 0,
|
||||
column: 0,
|
||||
width: Some(2),
|
||||
kind: ErrorKind::UnexpectedToken{expected: vec![Name], found: InterpolationStart},
|
||||
kind: ErrorKind::UnexpectedToken{expected: vec![Name, At], found: InterpolationStart},
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user