Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,12 @@ $ just bar
/subdir
```

Use `set no-cd`<sup>master</sup> to make all recipes in the current module
default to the same behavior.

`set no-cd` and `set working-directory` can be overridden on a per-recipe basis
with the `[no-cd]` and `[working-directory]` attributes.

You can override the working directory for all recipes with
`set working-directory := '…'`:

Expand Down Expand Up @@ -1043,6 +1049,7 @@ foo:
| `ignore-comments` | boolean | `false` | Ignore recipe lines beginning with `#`. |
| `no-exit-message`<sup>1.39.0</sup> | boolean | `false` | Don't print exit messages if recipes fail. |
| `lazy`<sup>1.47.0</sup> | boolean | `false` | Don't evaluate unused variables. |
| `no-cd`<sup>master</sup> | boolean | `false` | Don't change directory when executing recipes by recipe attribute. |
| `positional-arguments` | boolean | `false` | Pass positional arguments. |
| `quiet` | boolean | `false` | Disable echoing recipe lines before executing. |
| `script-interpreter`<sup>1.33.0</sup> | `[COMMAND, ARGS…]` | `['sh', '-eu']` | Set command used to invoke recipes with empty `[script]` attribute. |
Expand Down
18 changes: 18 additions & 0 deletions src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,24 @@ impl<'run, 'src> Analyzer<'run, 'src> {
}));
}

if let Some(second) = Keyword::from_lexeme(set.name.lexeme()) {
let first = match second {
Keyword::NoCd => Keyword::WorkingDirectory,
Keyword::WorkingDirectory => Keyword::NoCd,
_ => {
return Ok(());
}
};

if let Some(conflict) = self.sets.get(first.lexeme()) {
return Err(set.name.error(NoCdAndWorkingDirectorySetting {
first,
first_line: conflict.name.line,
second,
}));
}
}

Ok(())
}

Expand Down
11 changes: 11 additions & 0 deletions src/compile_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,17 @@ impl Display for CompileError<'_> {
f,
"recipe `{recipe}` has both `[no-cd]` and `[working-directory]` attributes"
),
NoCdAndWorkingDirectorySetting {
first,
first_line,
second,
} => write!(
f,
"`{}` set on line {} is incompatible with `{}`",
first.lexeme(),
first_line.ordinal(),
second.lexeme()
),
OptionNameContainsEqualSign { parameter } => {
write!(
f,
Expand Down
5 changes: 5 additions & 0 deletions src/compile_error_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ pub(crate) enum CompileErrorKind<'src> {
NoCdAndWorkingDirectoryAttribute {
recipe: &'src str,
},
NoCdAndWorkingDirectorySetting {
first: Keyword,
first_line: usize,
second: Keyword,
},
OptionNameContainsEqualSign {
parameter: String,
},
Expand Down
3 changes: 3 additions & 0 deletions src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ impl<'src, 'run> Evaluator<'src, 'run> {
Setting::Lazy(value) => {
settings.lazy = value;
}
Setting::NoCd(value) => {
settings.no_cd = value;
}
Setting::NoExitMessage(value) => {
settings.no_exit_message = value;
}
Expand Down
1 change: 1 addition & 0 deletions src/keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub(crate) enum Keyword {
Import,
Lazy,
Mod,
NoCd,
NoExitMessage,
PositionalArguments,
Quiet,
Expand Down
1 change: 1 addition & 0 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ impl<'src> Node<'src> for Set<'src> {
| Setting::Guards(value)
| Setting::IgnoreComments(value)
| Setting::Lazy(value)
| Setting::NoCd(value)
| Setting::NoExitMessage(value)
| Setting::PositionalArguments(value)
| Setting::Quiet(value)
Expand Down
7 changes: 7 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,7 @@ impl<'run, 'src> Parser<'run, 'src> {
Keyword::Guards => Some(Setting::Guards(self.parse_set_bool()?)),
Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)),
Keyword::Lazy => Some(Setting::Lazy(self.parse_set_bool()?)),
Keyword::NoCd => Some(Setting::NoCd(self.parse_set_bool()?)),
Keyword::NoExitMessage => Some(Setting::NoExitMessage(self.parse_set_bool()?)),
Keyword::PositionalArguments => Some(Setting::PositionalArguments(self.parse_set_bool()?)),
Keyword::Quiet => Some(Setting::Quiet(self.parse_set_bool()?)),
Expand Down Expand Up @@ -2650,6 +2651,12 @@ mod tests {
tree: (justfile (set quiet false)),
}

test! {
name: set_no_cd,
text: "set no-cd := true",
tree: (justfile (set no_cd true)),
}

test! {
name: set_positional_arguments_false,
text: "set positional-arguments := false",
Expand Down
17 changes: 14 additions & 3 deletions src/recipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,19 @@ impl<'src> Recipe<'src> {
.contains(AttributeDiscriminant::PositionalArguments)
}

pub(crate) fn change_directory(&self) -> bool {
!self.attributes.contains(AttributeDiscriminant::NoCd)
pub(crate) fn change_directory(&self, settings: &Settings) -> bool {
if self
.attributes
.contains(AttributeDiscriminant::WorkingDirectory)
{
return true;
}

if self.attributes.contains(AttributeDiscriminant::NoCd) {
return false;
}

!settings.no_cd
}

fn print_exit_message(&self, settings: &Settings) -> bool {
Expand All @@ -186,7 +197,7 @@ impl<'src> Recipe<'src> {
}

fn working_directory<'a>(&'a self, context: &'a ExecutionContext) -> Option<PathBuf> {
if !self.change_directory() {
if !self.change_directory(&context.module.settings) {
return None;
}

Expand Down
3 changes: 3 additions & 0 deletions src/setting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub(crate) enum Setting<'src> {
Guards(bool),
IgnoreComments(bool),
Lazy(bool),
NoCd(bool),
NoExitMessage(bool),
PositionalArguments(bool),
Quiet(bool),
Expand All @@ -39,6 +40,7 @@ impl<'src> Setting<'src> {
| Self::Guards(value)
| Self::IgnoreComments(value)
| Self::Lazy(value)
| Self::NoCd(value)
| Self::NoExitMessage(value)
| Self::PositionalArguments(value)
| Self::Quiet(value)
Expand Down Expand Up @@ -88,6 +90,7 @@ impl Display for Setting<'_> {
| Self::Guards(value)
| Self::IgnoreComments(value)
| Self::Lazy(value)
| Self::NoCd(value)
| Self::NoExitMessage(value)
| Self::PositionalArguments(value)
| Self::Quiet(value)
Expand Down
1 change: 1 addition & 0 deletions src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub(crate) struct Settings {
pub(crate) guards: bool,
pub(crate) ignore_comments: bool,
pub(crate) lazy: bool,
pub(crate) no_cd: bool,
pub(crate) no_exit_message: bool,
pub(crate) positional_arguments: bool,
pub(crate) quiet: bool,
Expand Down
1 change: 1 addition & 0 deletions tests/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ struct Settings<'a> {
guards: bool,
ignore_comments: bool,
lazy: bool,
no_cd: bool,
no_exit_message: bool,
positional_arguments: bool,
quiet: bool,
Expand Down
22 changes: 22 additions & 0 deletions tests/modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,28 @@ foo:
.failure();
}

#[test]
fn submodules_do_not_inherit_no_cd_setting() {
Test::new()
.write(
"foo/mod.just",
"bar:
@cat data.txt
",
)
.write("foo/data.txt", "MODULE\n")
.justfile(
"
set no-cd := true

mod foo
",
)
.args(["foo", "bar"])
.stdout("MODULE\n")
.success();
}

#[test]
fn modules_conflict_with_recipes() {
Test::new()
Expand Down
61 changes: 61 additions & 0 deletions tests/no_cd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,64 @@ fn shebang() {
.stdout("hello")
.success();
}

#[test]
fn setting_applies_to_recipes() {
Test::new()
.justfile(
"
set no-cd := true

foo:
cat bar
",
)
.current_dir("child")
.tree(tree! {
bar: "root",
child: {
bar: "child",
}
})
.stderr("cat bar\n")
.stdout("child")
.success();
}

#[test]
fn working_directory_attribute_overrides_setting() {
Test::new()
.justfile(
"
set no-cd := true

[working-directory('workspace')]
foo:
cat data.txt
",
)
.write("workspace/data.txt", "WORKSPACE")
.stderr("cat data.txt\n")
.stdout("WORKSPACE")
.success();
}

#[test]
fn evaluator_paths_ignore_setting() {
Test::new()
.justfile(
"
set no-cd := true

file := `cat data.txt`

@foo:
echo {{file}}
",
)
.current_dir("inv")
.write("data.txt", "MODULE")
.write("inv/data.txt", "INVOCATION")
.stdout("MODULE\n")
.success();
}
21 changes: 21 additions & 0 deletions tests/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,27 @@ fn variable() {
.success();
}

#[test]
fn no_cd_setting_conflicts_with_working_directory_setting() {
Test::new()
.justfile(
"
set no-cd := true
set working-directory := 'bar'
",
)
.stderr(
"
error: `no-cd` set on line 1 is incompatible with `working-directory`
——▶ justfile:2:5
2 │ set working-directory := 'bar'
│ ^^^^^^^^^^^^^^^^^
",
)
.failure();
}

#[test]
fn unused_non_const_assignments() {
Test::new()
Expand Down
21 changes: 21 additions & 0 deletions tests/working_directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,27 @@ fn no_cd_overrides_setting() {
.success();
}

#[test]
fn working_directory_setting_conflicts_with_no_cd_setting() {
Test::new()
.justfile(
"
set working-directory := 'bar'
set no-cd := true
",
)
.stderr(
"
error: `working-directory` set on line 1 is incompatible with `no-cd`
——▶ justfile:2:5
2 │ set no-cd := true
│ ^^^^^
",
)
.failure();
}

#[test]
fn working_dir_in_submodule_is_relative_to_module_path() {
Test::new()
Expand Down