Add functions (#277)
– Parse unary (no-argument) functions – Add functions for detecting the current os, arch, and os family, according to rustc's cfg attributes
This commit is contained in:
parent
66391de3f8
commit
afa4aebd4a
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -119,6 +119,7 @@ dependencies = [
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"target 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -199,6 +200,11 @@ name = "strsim"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "target"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "tempdir"
|
||||
version = "0.3.5"
|
||||
@ -299,6 +305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum regex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ac6ab4e9218ade5b423358bbd2567d1617418403c7a512603630181813316322"
|
||||
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
|
||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||
"checksum target 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "10000465bb0cc031c87a44668991b284fd84c0e6bd945f62d4af04e9e52a222a"
|
||||
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
|
||||
|
@ -18,6 +18,7 @@ itertools = "0.7"
|
||||
lazy_static = "1.0.0"
|
||||
libc = "0.2.21"
|
||||
regex = "0.2.2"
|
||||
target = "1.0.0"
|
||||
tempdir = "0.3.5"
|
||||
unicode-width = "0.1.3"
|
||||
|
||||
|
@ -55,10 +55,14 @@ export : 'export' assignment
|
||||
expression : value '+' expression
|
||||
| value
|
||||
|
||||
value : STRING
|
||||
value : NAME '(' arguments? ')'
|
||||
| STRING
|
||||
| RAW_STRING
|
||||
| NAME
|
||||
| BACKTICK
|
||||
| NAME
|
||||
|
||||
arguments : expression ',' arguments
|
||||
| expression ','?
|
||||
|
||||
recipe : '@'? NAME parameter* ('+' parameter)? ':' dependencies? body?
|
||||
|
||||
|
28
README.asc
28
README.asc
@ -249,7 +249,31 @@ string!
|
||||
"
|
||||
```
|
||||
|
||||
=== Command Evaluation using Backticks
|
||||
=== Functions
|
||||
|
||||
Just provides a few built-in functions that might be useful when writing recipes.
|
||||
|
||||
==== System Information
|
||||
|
||||
- `arch()` – Instruction set architecture. Possible values are: `"aarch64"`, `"arm"`, `"asmjs"`, `"hexagon"`, `"mips"`, `"msp430"`, `"powerpc"`, `"powerpc64"`, `"s390x"`, `"sparc"`, `"wasm32"`, `"x86"`, `"x86_64"`, and `"xcore"`.
|
||||
|
||||
- `os()` – Operating system. Possible values are: `"android"`, `"bitrig"`, `"dragonfly"`, `"emscripten"`, `"freebsd"`, `"haiku"`, `"ios"`, `"linux"`, `"macos"`, `"netbsd"`, `"openbsd"`, `"solaris"`, and `"windows"`.
|
||||
|
||||
- `os_family()` - Operating system family; possible values are: `"unix"` and `"windows"`.
|
||||
|
||||
For example:
|
||||
|
||||
```make
|
||||
system-info:
|
||||
@echo "This is an {{arch()}} machine".
|
||||
```
|
||||
|
||||
```
|
||||
$ just system-info
|
||||
This is an x86_64 machine
|
||||
```
|
||||
|
||||
=== Command Evaluation Using Backticks
|
||||
|
||||
Backticks can be used to store the result of commands:
|
||||
|
||||
@ -390,7 +414,7 @@ search QUERY:
|
||||
lynx 'https://www.google.com/?q={{QUERY}}'
|
||||
```
|
||||
|
||||
=== Write Recipes in other Languages
|
||||
=== Writing Recipes in Other Languages
|
||||
|
||||
Recipes that start with a `#!` are executed as scripts, so you can write recipes in other languages:
|
||||
|
||||
|
@ -82,37 +82,40 @@ impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
|
||||
expression: &Expression<'a>,
|
||||
arguments: &Map<&str, Cow<str>>
|
||||
) -> RunResult<'a, String> {
|
||||
Ok(match *expression {
|
||||
match *expression {
|
||||
Expression::Variable{name, ..} => {
|
||||
if self.evaluated.contains_key(name) {
|
||||
self.evaluated[name].clone()
|
||||
Ok(self.evaluated[name].clone())
|
||||
} else if self.scope.contains_key(name) {
|
||||
self.scope[name].clone()
|
||||
Ok(self.scope[name].clone())
|
||||
} else if self.assignments.contains_key(name) {
|
||||
self.evaluate_assignment(name)?;
|
||||
self.evaluated[name].clone()
|
||||
Ok(self.evaluated[name].clone())
|
||||
} else if arguments.contains_key(name) {
|
||||
arguments[name].to_string()
|
||||
Ok(arguments[name].to_string())
|
||||
} else {
|
||||
return Err(RuntimeError::Internal {
|
||||
Err(RuntimeError::Internal {
|
||||
message: format!("attempted to evaluate undefined variable `{}`", name)
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
Expression::String{ref cooked_string} => cooked_string.cooked.clone(),
|
||||
Expression::Call{name, ..} => ::functions::evaluate_function(name),
|
||||
Expression::String{ref cooked_string} => Ok(cooked_string.cooked.clone()),
|
||||
Expression::Backtick{raw, ref token} => {
|
||||
if self.dry_run {
|
||||
format!("`{}`", raw)
|
||||
Ok(format!("`{}`", raw))
|
||||
} else {
|
||||
self.run_backtick(raw, token)?
|
||||
Ok(self.run_backtick(raw, token)?)
|
||||
}
|
||||
}
|
||||
Expression::Concatination{ref lhs, ref rhs} => {
|
||||
Ok(
|
||||
self.evaluate_expression(lhs, arguments)?
|
||||
+
|
||||
&self.evaluate_expression(rhs, arguments)?
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn run_backtick(
|
||||
|
@ -75,6 +75,7 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
||||
return Err(token.error(UndefinedVariable{variable: name}));
|
||||
}
|
||||
}
|
||||
Expression::Call{ref token, ..} => ::functions::resolve_function(token)?,
|
||||
Expression::Concatination{ref lhs, ref rhs} => {
|
||||
self.resolve_expression(lhs)?;
|
||||
self.resolve_expression(rhs)?;
|
||||
@ -129,4 +130,15 @@ mod test {
|
||||
width: Some(2),
|
||||
kind: UndefinedVariable{variable: "yy"},
|
||||
}
|
||||
|
||||
compilation_error_test! {
|
||||
name: unknown_function,
|
||||
input: "a = foo()",
|
||||
index: 4,
|
||||
line: 0,
|
||||
column: 4,
|
||||
width: Some(3),
|
||||
kind: UnknownFunction{function: "foo"},
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,12 +29,13 @@ pub enum CompilationErrorKind<'a> {
|
||||
InvalidEscapeSequence{character: char},
|
||||
MixedLeadingWhitespace{whitespace: &'a str},
|
||||
OuterShebang,
|
||||
ParameterFollowsVariadicParameter{parameter: &'a str},
|
||||
ParameterShadowsVariable{parameter: &'a str},
|
||||
RequiredParameterFollowsDefaultParameter{parameter: &'a str},
|
||||
ParameterFollowsVariadicParameter{parameter: &'a str},
|
||||
UndefinedVariable{variable: &'a str},
|
||||
UnexpectedToken{expected: Vec<TokenKind>, found: TokenKind},
|
||||
UnknownDependency{recipe: &'a str, unknown: &'a str},
|
||||
UnknownFunction{function: &'a str},
|
||||
UnknownStartOfToken,
|
||||
UnterminatedString,
|
||||
}
|
||||
@ -123,6 +124,9 @@ impl<'a> Display for CompilationError<'a> {
|
||||
UndefinedVariable{variable} => {
|
||||
writeln!(f, "Variable `{}` not defined", variable)?;
|
||||
}
|
||||
UnknownFunction{function} => {
|
||||
writeln!(f, "Call to unknown function `{}`", function)?;
|
||||
}
|
||||
UnknownStartOfToken => {
|
||||
writeln!(f, "Unknown start of token:")?;
|
||||
}
|
||||
|
@ -2,10 +2,11 @@ use common::*;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Expression<'a> {
|
||||
Variable{name: &'a str, token: Token<'a>},
|
||||
String{cooked_string: CookedString<'a>},
|
||||
Backtick{raw: &'a str, token: Token<'a>},
|
||||
Call{name: &'a str, token: Token<'a>},
|
||||
Concatination{lhs: Box<Expression<'a>>, rhs: Box<Expression<'a>>},
|
||||
String{cooked_string: CookedString<'a>},
|
||||
Variable{name: &'a str, token: Token<'a>},
|
||||
}
|
||||
|
||||
impl<'a> Expression<'a> {
|
||||
@ -14,12 +15,19 @@ impl<'a> Expression<'a> {
|
||||
stack: vec![self],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn functions(&'a self) -> Functions<'a> {
|
||||
Functions {
|
||||
stack: vec![self],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Expression<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Expression::Backtick {raw, .. } => write!(f, "`{}`", raw)?,
|
||||
Expression::Call {name, .. } => write!(f, "{}()", name)?,
|
||||
Expression::Concatination{ref lhs, ref rhs } => write!(f, "{} + {}", lhs, rhs)?,
|
||||
Expression::String {ref cooked_string} => write!(f, "\"{}\"", cooked_string.raw)?,
|
||||
Expression::Variable {name, .. } => write!(f, "{}", name)?,
|
||||
@ -37,7 +45,10 @@ impl<'a> Iterator for Variables<'a> {
|
||||
|
||||
fn next(&mut self) -> Option<&'a Token<'a>> {
|
||||
match self.stack.pop() {
|
||||
None | Some(&Expression::String{..}) | Some(&Expression::Backtick{..}) => None,
|
||||
None
|
||||
| Some(&Expression::String{..})
|
||||
| Some(&Expression::Backtick{..})
|
||||
| Some(&Expression::Call{..}) => None,
|
||||
Some(&Expression::Variable{ref token,..}) => Some(token),
|
||||
Some(&Expression::Concatination{ref lhs, ref rhs}) => {
|
||||
self.stack.push(lhs);
|
||||
@ -47,3 +58,26 @@ impl<'a> Iterator for Variables<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Functions<'a> {
|
||||
stack: Vec<&'a Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Functions<'a> {
|
||||
type Item = &'a Token<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<&'a Token<'a>> {
|
||||
match self.stack.pop() {
|
||||
None
|
||||
| Some(&Expression::String{..})
|
||||
| Some(&Expression::Backtick{..})
|
||||
| Some(&Expression::Variable{..}) => None,
|
||||
Some(&Expression::Call{ref token, ..}) => Some(token),
|
||||
Some(&Expression::Concatination{ref lhs, ref rhs}) => {
|
||||
self.stack.push(lhs);
|
||||
self.stack.push(rhs);
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
src/functions.rs
Normal file
33
src/functions.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use common::*;
|
||||
use target;
|
||||
|
||||
pub fn resolve_function<'a>(token: &Token<'a>) -> CompilationResult<'a, ()> {
|
||||
if !&["arch", "os", "os_family"].contains(&token.lexeme) {
|
||||
Err(token.error(CompilationErrorKind::UnknownFunction{function: token.lexeme}))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluate_function<'a>(name: &'a str) -> RunResult<'a, String> {
|
||||
match name {
|
||||
"arch" => Ok(arch().to_string()),
|
||||
"os" => Ok(os().to_string()),
|
||||
"os_family" => Ok(os_family().to_string()),
|
||||
_ => Err(RuntimeError::Internal {
|
||||
message: format!("attempted to evaluate unknown function: `{}`", name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arch() -> &'static str {
|
||||
target::arch()
|
||||
}
|
||||
|
||||
pub fn os() -> &'static str {
|
||||
target::os()
|
||||
}
|
||||
|
||||
pub fn os_family() -> &'static str {
|
||||
target::os_family()
|
||||
}
|
16
src/lexer.rs
16
src/lexer.rs
@ -131,6 +131,8 @@ impl<'a> Lexer<'a> {
|
||||
lazy_static! {
|
||||
static ref BACKTICK: Regex = token(r"`[^`\n\r]*`" );
|
||||
static ref COLON: Regex = token(r":" );
|
||||
static ref PAREN_L: Regex = token(r"[(]" );
|
||||
static ref PAREN_R: Regex = token(r"[)]" );
|
||||
static ref AT: Regex = token(r"@" );
|
||||
static ref COMMENT: Regex = token(r"#([^!\n\r].*)?$" );
|
||||
static ref EOF: Regex = token(r"(?-m)$" );
|
||||
@ -140,7 +142,7 @@ impl<'a> Lexer<'a> {
|
||||
static ref INTERPOLATION_START_TOKEN: Regex = token(r"[{][{]" );
|
||||
static ref NAME: Regex = token(r"([a-zA-Z_][a-zA-Z0-9_-]*)" );
|
||||
static ref PLUS: Regex = token(r"[+]" );
|
||||
static ref STRING: Regex = token("\"" );
|
||||
static ref STRING: Regex = token(r#"["]"# );
|
||||
static ref RAW_STRING: Regex = token(r#"'[^']*'"# );
|
||||
static ref UNTERMINATED_RAW_STRING: Regex = token(r#"'[^']*"# );
|
||||
static ref INTERPOLATION_START: Regex = re(r"^[{][{]" );
|
||||
@ -209,6 +211,10 @@ impl<'a> Lexer<'a> {
|
||||
(captures.get(1).unwrap().as_str(), captures.get(2).unwrap().as_str(), Colon)
|
||||
} else if let Some(captures) = AT.captures(self.rest) {
|
||||
(captures.get(1).unwrap().as_str(), captures.get(2).unwrap().as_str(), At)
|
||||
} else if let Some(captures) = PAREN_L.captures(self.rest) {
|
||||
(captures.get(1).unwrap().as_str(), captures.get(2).unwrap().as_str(), ParenL)
|
||||
} else if let Some(captures) = PAREN_R.captures(self.rest) {
|
||||
(captures.get(1).unwrap().as_str(), captures.get(2).unwrap().as_str(), ParenR)
|
||||
} else if let Some(captures) = PLUS.captures(self.rest) {
|
||||
(captures.get(1).unwrap().as_str(), captures.get(2).unwrap().as_str(), Plus)
|
||||
} else if let Some(captures) = EQUALS.captures(self.rest) {
|
||||
@ -338,6 +344,8 @@ mod test {
|
||||
InterpolationStart => "{",
|
||||
Line{..} => "^",
|
||||
Name => "N",
|
||||
ParenL => "(",
|
||||
ParenR => ")",
|
||||
Plus => "+",
|
||||
RawString => "'",
|
||||
StringToken => "\"",
|
||||
@ -510,6 +518,12 @@ c: b
|
||||
"$N:N$>^_$$<N:$>^_$^_$$<N:N$>^_$$<N:N$>^_<.",
|
||||
}
|
||||
|
||||
summary_test! {
|
||||
tokenize_parens,
|
||||
r"((())) )abc(+",
|
||||
"((())))N(+.",
|
||||
}
|
||||
|
||||
error_test! {
|
||||
name: tokenize_space_then_tab,
|
||||
input: "a:
|
||||
|
@ -7,6 +7,7 @@ extern crate edit_distance;
|
||||
extern crate itertools;
|
||||
extern crate libc;
|
||||
extern crate regex;
|
||||
extern crate target;
|
||||
extern crate tempdir;
|
||||
extern crate unicode_width;
|
||||
|
||||
@ -23,6 +24,7 @@ mod configuration;
|
||||
mod cooked_string;
|
||||
mod expression;
|
||||
mod fragment;
|
||||
mod functions;
|
||||
mod justfile;
|
||||
mod lexer;
|
||||
mod misc;
|
||||
|
@ -251,7 +251,19 @@ impl<'a> Parser<'a> {
|
||||
fn expression(&mut self, interpolation: bool) -> CompilationResult<'a, Expression<'a>> {
|
||||
let first = self.tokens.next().unwrap();
|
||||
let lhs = match first.kind {
|
||||
Name => Expression::Variable {name: first.lexeme, token: first},
|
||||
Name => {
|
||||
if self.peek(ParenL) {
|
||||
if let Some(token) = self.expect(ParenL) {
|
||||
return Err(self.unexpected_token(&token, &[ParenL]));
|
||||
}
|
||||
if let Some(token) = self.expect(ParenR) {
|
||||
return Err(self.unexpected_token(&token, &[ParenR]));
|
||||
}
|
||||
Expression::Call {name: first.lexeme, token: first}
|
||||
} else {
|
||||
Expression::Variable {name: first.lexeme, token: first}
|
||||
}
|
||||
}
|
||||
Backtick => Expression::Backtick {
|
||||
raw: &first.lexeme[1..first.lexeme.len()-1],
|
||||
token: first
|
||||
@ -764,6 +776,26 @@ c = a + b + a + b",
|
||||
kind: UnexpectedToken{expected: vec![Plus, Eol, InterpolationEnd], found: Dedent},
|
||||
}
|
||||
|
||||
compilation_error_test! {
|
||||
name: unclosed_parenthesis_in_expression,
|
||||
input: "x = foo(",
|
||||
index: 8,
|
||||
line: 0,
|
||||
column: 8,
|
||||
width: Some(0),
|
||||
kind: UnexpectedToken{expected: vec![ParenR], found: Eof},
|
||||
}
|
||||
|
||||
compilation_error_test! {
|
||||
name: unclosed_parenthesis_in_interpolation,
|
||||
input: "a:\n echo {{foo(}}",
|
||||
index: 15,
|
||||
line: 1,
|
||||
column: 12,
|
||||
width: Some(2),
|
||||
kind: UnexpectedToken{expected: vec![ParenR], found: InterpolationEnd},
|
||||
}
|
||||
|
||||
compilation_error_test! {
|
||||
name: plus_following_parameter,
|
||||
input: "a b c+:",
|
||||
|
@ -27,24 +27,39 @@ impl<'a, 'b> RecipeResolver<'a, 'b> {
|
||||
resolver.seen = empty();
|
||||
}
|
||||
|
||||
for recipe in recipes.values() {
|
||||
for line in &recipe.lines {
|
||||
for fragment in line {
|
||||
if let Fragment::Expression{ref expression, ..} = *fragment {
|
||||
for variable in expression.variables() {
|
||||
let name = variable.lexeme;
|
||||
let undefined = !assignments.contains_key(name)
|
||||
&& !recipe.parameters.iter().any(|p| p.name == name);
|
||||
if undefined {
|
||||
// There's a borrow issue here that seems too difficult to solve.
|
||||
// The error derived from the variable token has too short a lifetime,
|
||||
// There are borrow issues here that seems too difficult to solve.
|
||||
// The errors derived from the variable token has too short a lifetime,
|
||||
// so we create a new error from its contents, which do live long
|
||||
// enough.
|
||||
//
|
||||
// I suspect the solution here is to give recipes, pieces, and expressions
|
||||
// two lifetime parameters instead of one, with one being the lifetime
|
||||
// of the struct, and the second being the lifetime of the tokens
|
||||
// that it contains
|
||||
// that it contains.
|
||||
|
||||
for recipe in recipes.values() {
|
||||
for line in &recipe.lines {
|
||||
for fragment in line {
|
||||
if let Fragment::Expression{ref expression, ..} = *fragment {
|
||||
for function in expression.functions() {
|
||||
if let Err(error) = ::functions::resolve_function(function) {
|
||||
return Err(CompilationError {
|
||||
text: text,
|
||||
index: error.index,
|
||||
line: error.line,
|
||||
column: error.column,
|
||||
width: error.width,
|
||||
kind: UnknownFunction {
|
||||
function: &text[error.index..error.index + error.width.unwrap()],
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
for variable in expression.variables() {
|
||||
let name = variable.lexeme;
|
||||
let undefined = !assignments.contains_key(name)
|
||||
&& !recipe.parameters.iter().any(|p| p.name == name);
|
||||
if undefined {
|
||||
let error = variable.error(UndefinedVariable{variable: name});
|
||||
return Err(CompilationError {
|
||||
text: text,
|
||||
@ -152,4 +167,14 @@ mod test {
|
||||
width: Some(3),
|
||||
kind: UndefinedVariable{variable: "lol"},
|
||||
}
|
||||
|
||||
compilation_error_test! {
|
||||
name: unknown_function_in_interpolation,
|
||||
input: "a:\n echo {{bar()}}",
|
||||
index: 11,
|
||||
line: 1,
|
||||
column: 8,
|
||||
width: Some(3),
|
||||
kind: UnknownFunction{function: "bar"},
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ pub enum TokenKind {
|
||||
InterpolationStart,
|
||||
Line,
|
||||
Name,
|
||||
ParenL,
|
||||
ParenR,
|
||||
Plus,
|
||||
RawString,
|
||||
StringToken,
|
||||
@ -63,6 +65,8 @@ impl Display for TokenKind {
|
||||
Name => "name",
|
||||
Plus => "'+'",
|
||||
At => "'@'",
|
||||
ParenL => "'('",
|
||||
ParenR => "')'",
|
||||
StringToken => "string",
|
||||
RawString => "raw string",
|
||||
Text => "command text",
|
||||
|
@ -1,6 +1,7 @@
|
||||
extern crate brev;
|
||||
extern crate executable_path;
|
||||
extern crate libc;
|
||||
extern crate target;
|
||||
extern crate tempdir;
|
||||
|
||||
use executable_path::executable_path;
|
||||
@ -1174,6 +1175,35 @@ foo:
|
||||
status: EXIT_SUCCESS,
|
||||
}
|
||||
|
||||
integration_test! {
|
||||
name: test_os_arch_functions_in_interpolation,
|
||||
justfile: r#"
|
||||
foo:
|
||||
echo {{arch()}} {{os()}} {{os_family()}}
|
||||
"#,
|
||||
args: (),
|
||||
stdout: format!("{} {} {}\n", target::arch(), target::os(), target::os_family()).as_str(),
|
||||
stderr: format!("echo {} {} {}\n", target::arch(), target::os(), target::os_family()).as_str(),
|
||||
status: EXIT_SUCCESS,
|
||||
}
|
||||
|
||||
integration_test! {
|
||||
name: test_os_arch_functions_in_expression,
|
||||
justfile: r#"
|
||||
a = arch()
|
||||
o = os()
|
||||
f = os_family()
|
||||
|
||||
foo:
|
||||
echo {{a}} {{o}} {{f}}
|
||||
"#,
|
||||
args: (),
|
||||
stdout: format!("{} {} {}\n", target::arch(), target::os(), target::os_family()).as_str(),
|
||||
stderr: format!("echo {} {} {}\n", target::arch(), target::os(), target::os_family()).as_str(),
|
||||
status: EXIT_SUCCESS,
|
||||
}
|
||||
|
||||
|
||||
integration_test! {
|
||||
name: quiet_recipe,
|
||||
justfile: r#"
|
||||
@ -1260,6 +1290,19 @@ integration_test! {
|
||||
status: EXIT_FAILURE,
|
||||
}
|
||||
|
||||
integration_test! {
|
||||
name: unknown_function_in_assignment,
|
||||
justfile: r#"foo = foo() + "hello"
|
||||
bar:"#,
|
||||
args: ("bar"),
|
||||
stdout: "",
|
||||
stderr: r#"error: Call to unknown function `foo`
|
||||
|
|
||||
1 | foo = foo() + "hello"
|
||||
| ^^^
|
||||
"#,
|
||||
status: EXIT_FAILURE,
|
||||
}
|
||||
|
||||
integration_test! {
|
||||
name: dependency_takes_arguments,
|
||||
|
Loading…
Reference in New Issue
Block a user