Add positional-arguments setting (#804)

Allow recipe arguments to be passed as positional arguments to commands.
This commit is contained in:
Casey Rodarmor 2021-04-24 18:29:58 -07:00 committed by GitHub
parent d03aedd5c4
commit 67bd318bf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 148 additions and 15 deletions

View File

@ -60,6 +60,7 @@ export : 'export' assignment
setting : 'set' 'dotenv-load' boolean?
| 'set' 'export' boolean?
| 'set' 'positional-arguments' boolean?
| 'set' 'shell' ':=' '[' string (',' string)* ','? ']'
boolean : ':=' ('true' | 'false')

View File

@ -388,9 +388,10 @@ foo:
[options="header"]
|=================
| Name | Value | Description
| `dotenv-load` | `true` or `false` | Load a `.env` file, if present.
| `export` | `true` or `false` | Export all variables as environment variables.
|`shell` | `[COMMAND, ARGS...]` | Set the command used to invoke recipes and evaluate backticks.
| `dotenv-load` | boolean | Load a `.env` file, if present.
| `export` | boolean | Export all variables as environment variables.
| `positional-arguments` | boolean | Pass positional arguments.
| `shell` | `[COMMAND, ARGS...]` | Set the command used to invoke recipes and evaluate backticks.
|=================
Boolean settings can be written as:
@ -429,6 +430,28 @@ hello
goodbye
```
==== Positional Arguments
If `positional-arguments` is `true`, recipe arguments will be passed as positional arguments to commands. For linewise recipes, argument `$0` will be the name of the Recipe.
For example, running this recipe:
```make
set positional-arguments
@foo bar:
echo $0
echo $1
```
Will produce the following output:
```
$ just foo hello
foo
hello
```
==== Shell
The `shell` setting controls the command used to invoke recipe lines and backticks. Shebang recipes are unaffected.

View File

@ -71,6 +71,9 @@ impl<'src> Analyzer<'src> {
Setting::Export(export) => {
settings.export = export;
},
Setting::PositionalArguments(positional_arguments) => {
settings.positional_arguments = positional_arguments;
},
Setting::Shell(shell) => {
assert!(settings.shell.is_none());
settings.shell = Some(shell);

View File

@ -266,7 +266,7 @@ impl<'src> Justfile<'src> {
}
}
recipe.run(context, dotenv, scope.child(), search)?;
recipe.run(context, dotenv, scope.child(), search, arguments)?;
let mut invocation = vec![recipe.name().to_owned()];
for argument in arguments.iter().cloned() {

View File

@ -4,14 +4,15 @@ use crate::common::*;
#[strum(serialize_all = "kebab_case")]
pub(crate) enum Keyword {
Alias,
DotenvLoad,
Else,
Export,
DotenvLoad,
True,
False,
If,
PositionalArguments,
Set,
Shell,
True,
}
impl Keyword {

View File

@ -191,7 +191,8 @@ impl<'src> Node<'src> for Set<'src> {
use Setting::*;
match &self.value {
DotenvLoad(value) | Export(value) => set.push_mut(value.to_string()),
DotenvLoad(value) | Export(value) | PositionalArguments(value) =>
set.push_mut(value.to_string()),
Shell(setting::Shell { command, arguments }) => {
set.push_mut(Tree::string(&command.cooked));
for argument in arguments {

View File

@ -717,6 +717,12 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
value: Setting::Export(value),
name,
});
} else if Keyword::PositionalArguments == lexeme {
let value = self.parse_set_bool()?;
return Ok(Set {
value: Setting::PositionalArguments(value),
name,
});
}
self.expect(ColonEquals)?;
@ -1678,6 +1684,24 @@ mod tests {
tree: (justfile (set dotenv_load false)),
}
test! {
name: set_positional_arguments_implicit,
text: "set positional-arguments",
tree: (justfile (set positional_arguments true)),
}
test! {
name: set_positional_arguments_true,
text: "set positional-arguments := true",
tree: (justfile (set positional_arguments true)),
}
test! {
name: set_positional_arguments_false,
text: "set positional-arguments := false",
tree: (justfile (set positional_arguments false)),
}
test! {
name: set_shell_no_arguments,
text: "set shell := ['tclsh']",

View File

@ -74,6 +74,7 @@ impl<'src, D> Recipe<'src, D> {
dotenv: &BTreeMap<String, String>,
scope: Scope<'src, 'run>,
search: &'run Search,
arguments: &[&'run str],
) -> RunResult<'src, ()> {
let config = &context.config;
@ -176,6 +177,10 @@ impl<'src, D> Recipe<'src, D> {
output_error,
})?;
if context.settings.positional_arguments {
command.args(arguments);
}
command.export(context.settings, dotenv, &scope);
// run it!
@ -260,6 +265,11 @@ impl<'src, D> Recipe<'src, D> {
cmd.arg(command);
if context.settings.positional_arguments {
cmd.arg(self.name.lexeme());
cmd.args(arguments);
}
if config.verbosity.quiet() {
cmd.stderr(Stdio::null());
cmd.stdout(Stdio::null());

View File

@ -2,9 +2,10 @@ use crate::common::*;
#[derive(Debug)]
pub(crate) enum Setting<'src> {
Shell(Shell<'src>),
Export(bool),
DotenvLoad(bool),
Export(bool),
PositionalArguments(bool),
Shell(Shell<'src>),
}
#[derive(Debug, PartialEq)]

View File

@ -2,17 +2,19 @@ use crate::common::*;
#[derive(Debug, PartialEq)]
pub(crate) struct Settings<'src> {
pub(crate) dotenv_load: Option<bool>,
pub(crate) export: bool,
pub(crate) shell: Option<setting::Shell<'src>>,
pub(crate) dotenv_load: Option<bool>,
pub(crate) export: bool,
pub(crate) positional_arguments: bool,
pub(crate) shell: Option<setting::Shell<'src>>,
}
impl<'src> Settings<'src> {
pub(crate) fn new() -> Settings<'src> {
Settings {
dotenv_load: None,
export: false,
shell: None,
dotenv_load: None,
export: false,
positional_arguments: false,
shell: None,
}
}

View File

@ -17,6 +17,7 @@ mod init;
mod interrupts;
mod invocation_directory;
mod misc;
mod positional_arguments;
mod quiet;
mod readme;
mod search;

View File

@ -0,0 +1,66 @@
test! {
name: linewise,
justfile: r#"
set positional-arguments
foo bar baz:
echo $0
echo $1
echo $2
echo "$@"
"#,
args: ("foo", "hello", "goodbye"),
stdout: "
foo
hello
goodbye
hello goodbye
",
stderr: r#"
echo $0
echo $1
echo $2
echo "$@"
"#,
}
test! {
name: variadic_linewise,
justfile: r#"
set positional-arguments
foo *bar:
echo $1
echo "$@"
"#,
args: ("foo", "a", "b", "c"),
stdout: "a\na b c\n",
stderr: "echo $1\necho \"$@\"\n",
}
test! {
name: shebang,
justfile: "
set positional-arguments
foo bar:
#!/bin/sh
echo $1
",
args: ("foo", "hello"),
stdout: "hello\n",
}
test! {
name: variadic_shebang,
justfile: r#"
set positional-arguments
foo *bar:
#!/bin/sh
echo $1
echo "$@"
"#,
args: ("foo", "a", "b", "c"),
stdout: "a\na b c\n",
}