summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCasey Rodarmor <casey@rodarmor.com>2023-12-28 16:55:36 -0800
committerGitHub <noreply@github.com>2023-12-29 00:55:36 +0000
commita1bd70a030582501aba68418a44e140e76de4ac3 (patch)
treee9ee69286fd13c0b0fe64b72c8b7ed7a073ffa5a
parent94b3af6cb7a1175c8f65b7159a5998281b9219b6 (diff)
Run recipes with working directory set to submodule directory (#1788)
-rw-r--r--src/compiler.rs4
-rw-r--r--src/config.rs1
-rw-r--r--src/justfile.rs1
-rw-r--r--src/lib.rs6
-rw-r--r--src/parser.rs34
-rw-r--r--src/recipe.rs16
-rw-r--r--src/settings.rs1
-rw-r--r--src/testing.rs3
-rw-r--r--src/unresolved_recipe.rs8
-rw-r--r--tests/modules.rs36
-rw-r--r--tests/shebang.rs16
11 files changed, 101 insertions, 25 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index 457e8894..82730bde 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -20,7 +20,7 @@ impl Compiler {
let (relative, src) = loader.load(root, &current)?;
loaded.push(relative.into());
let tokens = Lexer::lex(relative, src)?;
- let mut ast = Parser::parse(&tokens)?;
+ let mut ast = Parser::parse(current != root, &current, &tokens)?;
paths.insert(current.clone(), relative.into());
srcs.insert(current.clone(), src);
@@ -120,7 +120,7 @@ impl Compiler {
#[cfg(test)]
pub(crate) fn test_compile(src: &str) -> CompileResult<Justfile> {
let tokens = Lexer::test_lex(src)?;
- let ast = Parser::parse(&tokens)?;
+ let ast = Parser::parse(false, &PathBuf::new(), &tokens)?;
let root = PathBuf::from("justfile");
let mut asts: HashMap<PathBuf, Ast> = HashMap::new();
asts.insert(root.clone(), ast);
diff --git a/src/config.rs b/src/config.rs
index ec611606..1c20aee7 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -18,7 +18,6 @@ pub(crate) fn chooser_default(justfile: &Path) -> OsString {
}
#[derive(Debug, PartialEq)]
-#[allow(clippy::struct_excessive_bools)]
pub(crate) struct Config {
pub(crate) check: bool,
pub(crate) color: Color,
diff --git a/src/justfile.rs b/src/justfile.rs
index 23134208..148c936e 100644
--- a/src/justfile.rs
+++ b/src/justfile.rs
@@ -299,7 +299,6 @@ impl<'src> Justfile<'src> {
.or_else(|| self.aliases.get(name).map(|alias| alias.target.as_ref()))
}
- #[allow(clippy::too_many_arguments)]
fn invocation<'run>(
&'run self,
depth: usize,
diff --git a/src/lib.rs b/src/lib.rs
index fa88edee..0ace8fb7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,9 +4,13 @@
clippy::let_underscore_untyped,
clippy::needless_pass_by_value,
clippy::similar_names,
+ clippy::struct_excessive_bools,
+ clippy::struct_field_names,
+ clippy::too_many_arguments,
clippy::too_many_lines,
clippy::unnecessary_wraps,
- clippy::wildcard_imports
+ clippy::wildcard_imports,
+ overlapping_range_endpoints
)]
pub(crate) use {
diff --git a/src/parser.rs b/src/parser.rs
index 37589885..cb254f16 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -32,22 +32,28 @@ pub(crate) struct Parser<'tokens, 'src> {
expected: BTreeSet<TokenKind>,
/// Current recursion depth
depth: usize,
+ /// Path to the file being parsed
+ path: PathBuf,
+ /// Parsing a submodule
+ submodule: bool,
}
impl<'tokens, 'src> Parser<'tokens, 'src> {
/// Parse `tokens` into an `Ast`
- pub(crate) fn parse(tokens: &'tokens [Token<'src>]) -> CompileResult<'src, Ast<'src>> {
- Self::new(tokens).parse_ast()
- }
-
- /// Construct a new Parser from a token stream
- fn new(tokens: &'tokens [Token<'src>]) -> Parser<'tokens, 'src> {
+ pub(crate) fn parse(
+ submodule: bool,
+ path: &Path,
+ tokens: &'tokens [Token<'src>],
+ ) -> CompileResult<'src, Ast<'src>> {
Parser {
- next: 0,
+ depth: 0,
expected: BTreeSet::new(),
+ next: 0,
+ path: path.into(),
+ submodule,
tokens,
- depth: 0,
}
+ .parse_ast()
}
fn error(&self, kind: CompileErrorKind<'src>) -> CompileResult<'src, CompileError<'src>> {
@@ -707,16 +713,18 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
let body = self.parse_body()?;
Ok(Recipe {
- parameters: positional.into_iter().chain(variadic).collect(),
- private: name.lexeme().starts_with('_'),
shebang: body.first().map_or(false, Line::is_shebang),
attributes,
- priors,
body,
dependencies,
doc,
name,
+ parameters: positional.into_iter().chain(variadic).collect(),
+ path: self.path.clone(),
+ priors,
+ private: name.lexeme().starts_with('_'),
quiet,
+ submodule: self.submodule,
})
}
@@ -934,7 +942,7 @@ mod tests {
fn test(text: &str, want: Tree) {
let unindented = unindent(text);
let tokens = Lexer::test_lex(&unindented).expect("lexing failed");
- let justfile = Parser::parse(&tokens).expect("parsing failed");
+ let justfile = Parser::parse(false, &PathBuf::new(), &tokens).expect("parsing failed");
let have = justfile.tree();
if have != want {
println!("parsed text: {unindented}");
@@ -972,7 +980,7 @@ mod tests {
) {
let tokens = Lexer::test_lex(src).expect("Lexing failed in parse test...");
- match Parser::parse(&tokens) {
+ match Parser::parse(false, &PathBuf::new(), &tokens) {
Ok(_) => panic!("Parsing unexpectedly succeeded"),
Err(have) => {
let want = CompileError {
diff --git a/src/recipe.rs b/src/recipe.rs
index e526b8b0..598e6a1b 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -28,10 +28,14 @@ pub(crate) struct Recipe<'src, D = Dependency<'src>> {
pub(crate) doc: Option<&'src str>,
pub(crate) name: Name<'src>,
pub(crate) parameters: Vec<Parameter<'src>>,
+ #[serde(skip)]
+ pub(crate) path: PathBuf,
pub(crate) priors: usize,
pub(crate) private: bool,
pub(crate) quiet: bool,
pub(crate) shebang: bool,
+ #[serde(skip)]
+ pub(crate) submodule: bool,
}
impl<'src, D> Recipe<'src, D> {
@@ -222,7 +226,11 @@ impl<'src, D> Recipe<'src, D> {
let mut cmd = context.settings.shell_command(config);
if self.change_directory() {
- cmd.current_dir(&context.search.working_directory);
+ cmd.current_dir(if self.submodule {
+ self.path.parent().unwrap()
+ } else {
+ &context.search.working_directory
+ });
}
cmd.arg(command);
@@ -358,7 +366,11 @@ impl<'src, D> Recipe<'src, D> {
let mut command = Platform::make_shebang_command(
&path,
if self.change_directory() {
- Some(&context.search.working_directory)
+ if self.submodule {
+ Some(self.path.parent().unwrap())
+ } else {
+ Some(&context.search.working_directory)
+ }
} else {
None
},
diff --git a/src/settings.rs b/src/settings.rs
index 26765e0f..5c5e64ce 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -6,7 +6,6 @@ pub(crate) const WINDOWS_POWERSHELL_SHELL: &str = "powershell.exe";
pub(crate) const WINDOWS_POWERSHELL_ARGS: &[&str] = &["-NoLogo", "-Command"];
#[derive(Debug, PartialEq, Serialize, Default)]
-#[allow(clippy::struct_excessive_bools)]
pub(crate) struct Settings<'src> {
pub(crate) allow_duplicate_recipes: bool,
pub(crate) dotenv_filename: Option<String>,
diff --git a/src/testing.rs b/src/testing.rs
index b5134545..adb62434 100644
--- a/src/testing.rs
+++ b/src/testing.rs
@@ -59,7 +59,8 @@ pub(crate) fn analysis_error(
) {
let tokens = Lexer::test_lex(src).expect("Lexing failed in parse test...");
- let ast = Parser::parse(&tokens).expect("Parsing failed in analysis test...");
+ let ast =
+ Parser::parse(false, &PathBuf::new(), &tokens).expect("Parsing failed in analysis test...");
let root = PathBuf::from("justfile");
let mut asts: HashMap<PathBuf, Ast> = HashMap::new();
diff --git a/src/unresolved_recipe.rs b/src/unresolved_recipe.rs
index 0a49443d..b7f379fa 100644
--- a/src/unresolved_recipe.rs
+++ b/src/unresolved_recipe.rs
@@ -45,16 +45,18 @@ impl<'src> UnresolvedRecipe<'src> {
.collect();
Ok(Recipe {
+ attributes: self.attributes,
body: self.body,
+ dependencies,
doc: self.doc,
name: self.name,
parameters: self.parameters,
+ path: self.path,
+ priors: self.priors,
private: self.private,
quiet: self.quiet,
shebang: self.shebang,
- priors: self.priors,
- attributes: self.attributes,
- dependencies,
+ submodule: self.submodule,
})
}
}
diff --git a/tests/modules.rs b/tests/modules.rs
index ca69340f..ea4796a4 100644
--- a/tests/modules.rs
+++ b/tests/modules.rs
@@ -493,3 +493,39 @@ fn recipes_may_be_named_mod() {
.stdout("FOO\n")
.run();
}
+
+#[test]
+fn submodule_linewise_recipes_run_in_submodule_directory() {
+ Test::new()
+ .write("foo/bar", "BAR")
+ .write("foo/mod.just", "foo:\n @cat bar")
+ .justfile(
+ "
+ mod foo
+ ",
+ )
+ .test_round_trip(false)
+ .arg("--unstable")
+ .arg("foo")
+ .arg("foo")
+ .stdout("BAR")
+ .run();
+}
+
+#[test]
+fn submodule_shebang_recipes_run_in_submodule_directory() {
+ Test::new()
+ .write("foo/bar", "BAR")
+ .write("foo/mod.just", "foo:\n #!/bin/sh\n cat bar")
+ .justfile(
+ "
+ mod foo
+ ",
+ )
+ .test_round_trip(false)
+ .arg("--unstable")
+ .arg("foo")
+ .arg("foo")
+ .stdout("BAR")
+ .run();
+}
diff --git a/tests/shebang.rs b/tests/shebang.rs
index 18091bb5..04451b73 100644
--- a/tests/shebang.rs
+++ b/tests/shebang.rs
@@ -1,3 +1,5 @@
+use super::*;
+
#[cfg(windows)]
test! {
name: powershell,
@@ -41,3 +43,17 @@ default:
"#,
stdout: "Hello-World\r\n",
}
+
+#[test]
+fn simple() {
+ Test::new()
+ .justfile(
+ "
+ foo:
+ #!/bin/sh
+ echo bar
+ ",
+ )
+ .stdout("bar\n")
+ .run();
+}