Skip to content

Commit

Permalink
Allow passing arguments to dependencies (#555)
Browse files Browse the repository at this point in the history
Allow recipes that take parameters to be used as dependencies.
  • Loading branch information
casey authored Dec 7, 2019
1 parent 2d3134a commit 0931fa8
Show file tree
Hide file tree
Showing 19 changed files with 441 additions and 155 deletions.
5 changes: 3 additions & 2 deletions GRAMMAR.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,13 @@ string : STRING
sequence : expression ',' sequence
| expression ','?
recipe : '@'? NAME parameter* ('+' parameter)? ':' dependencies? body?
recipe : '@'? NAME parameter* ('+' parameter)? ':' dependency* body?
parameter : NAME
| NAME '=' value
dependencies : NAME+
dependency : NAME
| '(' NAME expression* ')
body : INDENT line+ DEDENT
Expand Down
14 changes: 11 additions & 3 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -547,16 +547,24 @@ build target:
cd {{target}} && make
```

Other recipes may not depend on a recipe with parameters.

To pass arguments, put them after the recipe name:
To pass arguments on the command line, put them after the recipe name:

```sh
$ just build my-awesome-project
Building my-awesome-project...
cd my-awesome-project && make
```

To pass arguments to a dependency, put the dependency in parentheses along with the arguments:

```make
default: (build "main")

build target:
@echo 'Building {{target}}...'
cd {{target}} && make
```

Parameters may have default values:

```make
Expand Down
35 changes: 2 additions & 33 deletions src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::common::*;
use CompilationErrorKind::*;

pub(crate) struct Analyzer<'src> {
recipes: Table<'src, Recipe<'src, Name<'src>>>,
recipes: Table<'src, UnresolvedRecipe<'src>>,
assignments: Table<'src, Assignment<'src>>,
aliases: Table<'src, Alias<'src, Name<'src>>>,
sets: Table<'src, Set<'src>>,
Expand Down Expand Up @@ -91,7 +91,7 @@ impl<'src> Analyzer<'src> {
})
}

fn analyze_recipe(&self, recipe: &Recipe<'src, Name<'src>>) -> CompilationResult<'src, ()> {
fn analyze_recipe(&self, recipe: &UnresolvedRecipe<'src>) -> CompilationResult<'src, ()> {
if let Some(original) = self.recipes.get(recipe.name.lexeme()) {
return Err(recipe.name.token().error(DuplicateRecipe {
recipe: original.name(),
Expand Down Expand Up @@ -125,17 +125,6 @@ impl<'src> Analyzer<'src> {
}
}

let mut dependencies = BTreeSet::new();
for dependency in &recipe.dependencies {
if dependencies.contains(dependency.lexeme()) {
return Err(dependency.token().error(DuplicateDependency {
recipe: recipe.name.lexeme(),
dependency: dependency.lexeme(),
}));
}
dependencies.insert(dependency.lexeme());
}

let mut continued = false;
for line in &recipe.body {
if !recipe.shebang && !continued {
Expand Down Expand Up @@ -295,26 +284,6 @@ mod tests {
kind: ParameterShadowsVariable{parameter: "foo"},
}

analysis_error! {
name: dependency_has_parameters,
input: "foo arg:\nb: foo",
offset: 12,
line: 1,
column: 3,
width: 3,
kind: DependencyHasParameters{recipe: "b", dependency: "foo"},
}

analysis_error! {
name: duplicate_dependency,
input: "a b c: b c z z",
offset: 13,
line: 0,
column: 13,
width: 1,
kind: DuplicateDependency{recipe: "a", dependency: "z"},
}

analysis_error! {
name: duplicate_recipe,
input: "a:\nb:\na:",
Expand Down
5 changes: 3 additions & 2 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ pub(crate) use crate::{
search_config::SearchConfig, search_error::SearchError, set::Set, setting::Setting,
settings::Settings, shebang::Shebang, show_whitespace::ShowWhitespace, state::State,
string_literal::StringLiteral, subcommand::Subcommand, table::Table, thunk::Thunk, token::Token,
token_kind::TokenKind, use_color::UseColor, variables::Variables, verbosity::Verbosity,
warning::Warning,
token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables,
verbosity::Verbosity, warning::Warning,
};

// type aliases
Expand Down
32 changes: 20 additions & 12 deletions src/compilation_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,6 @@ impl Display for CompilationError<'_> {
self.token.line.ordinal(),
)?;
}
DuplicateDependency { recipe, dependency } => {
writeln!(
f,
"Recipe `{}` has duplicate dependency `{}`",
recipe, dependency
)?;
}
DuplicateRecipe { recipe, first } => {
writeln!(
f,
Expand All @@ -114,13 +107,28 @@ impl Display for CompilationError<'_> {
self.token.line.ordinal(),
)?;
}
DependencyHasParameters { recipe, dependency } => {
writeln!(
DependencyArgumentCountMismatch {
dependency,
found,
min,
max,
} => {
write!(
f,
"Recipe `{}` depends on `{}` which requires arguments. \
Dependencies may not require arguments",
recipe, dependency
"Dependency `{}` got {} {} but takes ",
dependency,
found,
Count("argument", found),
)?;

if min == max {
let expected = min;
writeln!(f, "{} {}", expected, Count("argument", expected))?;
} else if found < min {
writeln!(f, "at least {} {}", min, Count("argument", min))?;
} else {
writeln!(f, "at most {} {}", max, Count("argument", max))?;
}
}
ParameterShadowsVariable { parameter } => {
writeln!(
Expand Down
10 changes: 4 additions & 6 deletions src/compilation_error_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,16 @@ pub(crate) enum CompilationErrorKind<'src> {
variable: &'src str,
circle: Vec<&'src str>,
},
DependencyHasParameters {
recipe: &'src str,
DependencyArgumentCountMismatch {
dependency: &'src str,
found: usize,
min: usize,
max: usize,
},
DuplicateAlias {
alias: &'src str,
first: usize,
},
DuplicateDependency {
recipe: &'src str,
dependency: &'src str,
},
DuplicateParameter {
recipe: &'src str,
parameter: &'src str,
Expand Down
21 changes: 20 additions & 1 deletion src/dependency.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,23 @@
use crate::common::*;

#[derive(PartialEq, Debug)]
pub(crate) struct Dependency<'src>(pub(crate) Rc<Recipe<'src>>);
pub(crate) struct Dependency<'src> {
pub(crate) recipe: Rc<Recipe<'src>>,
pub(crate) arguments: Vec<Expression<'src>>,
}

impl<'src> Display for Dependency<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
if self.arguments.is_empty() {
write!(f, "{}", self.recipe.name())
} else {
write!(f, "({}", self.recipe.name())?;

for argument in &self.arguments {
write!(f, " {}", argument)?;
}

write!(f, ")")
}
}
}
2 changes: 1 addition & 1 deletion src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
Ok(scope)
}

pub(crate) fn line_evaluator(
pub(crate) fn recipe_evaluator(
config: &'run Config,
dotenv: &'run BTreeMap<String, String>,
scope: &'run Scope<'src, 'run>,
Expand Down
2 changes: 1 addition & 1 deletion src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ use crate::common::*;
pub(crate) enum Item<'src> {
Alias(Alias<'src, Name<'src>>),
Assignment(Assignment<'src>),
Recipe(Recipe<'src, Name<'src>>),
Recipe(UnresolvedRecipe<'src>),
Set(Set<'src>),
}
53 changes: 45 additions & 8 deletions src/justfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl<'src> Justfile<'src> {
working_directory,
};

let mut ran = empty();
let mut ran = BTreeSet::new();
for (recipe, arguments) in grouped {
self.run_recipe(&context, recipe, arguments, &dotenv, &mut ran)?
}
Expand All @@ -201,17 +201,54 @@ impl<'src> Justfile<'src> {
&self,
context: &'run RecipeContext<'src, 'run>,
recipe: &Recipe<'src>,
arguments: &[&'src str],
arguments: &[&'run str],
dotenv: &BTreeMap<String, String>,
ran: &mut BTreeSet<&'src str>,
ran: &mut BTreeSet<Vec<String>>,
) -> RunResult<'src, ()> {
for Dependency(dependency) in &recipe.dependencies {
if !ran.contains(dependency.name()) {
self.run_recipe(context, dependency, &[], dotenv, ran)?;
let scope = Evaluator::evaluate_parameters(
context.config,
dotenv,
&recipe.parameters,
arguments,
&context.scope,
context.settings,
context.working_directory,
)?;

let mut evaluator = Evaluator::recipe_evaluator(
context.config,
dotenv,
&scope,
context.settings,
context.working_directory,
);

for Dependency { recipe, arguments } in &recipe.dependencies {
let mut invocation = vec![recipe.name().to_owned()];

for argument in arguments {
invocation.push(evaluator.evaluate_expression(argument)?);
}

if !ran.contains(&invocation) {
let arguments = invocation
.iter()
.skip(1)
.map(String::as_ref)
.collect::<Vec<&str>>();
self.run_recipe(context, recipe, &arguments, dotenv, ran)?;
}
}

recipe.run(context, dotenv, scope)?;

let mut invocation = Vec::new();
invocation.push(recipe.name().to_owned());
for argument in arguments.iter().cloned() {
invocation.push(argument.to_owned());
}
recipe.run(context, arguments, dotenv)?;
ran.insert(recipe.name());

ran.insert(invocation);
Ok(())
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ mod table;
mod thunk;
mod token;
mod token_kind;
mod unresolved_dependency;
mod unresolved_recipe;
mod use_color;
mod variables;
mod verbosity;
Expand Down
23 changes: 14 additions & 9 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl<'src> Node<'src> for Expression<'src> {
}
}

impl<'src> Node<'src> for Recipe<'src, Name<'src>> {
impl<'src> Node<'src> for UnresolvedRecipe<'src> {
fn tree(&self) -> Tree<'src> {
let mut t = Tree::atom("recipe");

Expand Down Expand Up @@ -111,14 +111,19 @@ impl<'src> Node<'src> for Recipe<'src, Name<'src>> {
}

if !self.dependencies.is_empty() {
t = t.push(
Tree::atom("deps").extend(
self
.dependencies
.iter()
.map(|dependency| dependency.lexeme()),
),
);
let mut dependencies = Tree::atom("deps");

for dependency in &self.dependencies {
let mut d = Tree::atom(dependency.recipe.lexeme());

for argument in &dependency.arguments {
d.push_mut(argument.tree());
}

dependencies.push_mut(d);
}

t.push_mut(dependencies);
}

if !self.body.is_empty() {
Expand Down
Loading

0 comments on commit 0931fa8

Please sign in to comment.