Run recipes with working directory set to submodule directory (#1788)

This commit is contained in:
Casey Rodarmor 2023-12-28 16:55:36 -08:00 committed by GitHub
parent 94b3af6cb7
commit a1bd70a030
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 102 additions and 26 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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 {

View File

@ -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,
expected: BTreeSet::new(),
tokens,
depth: 0,
expected: BTreeSet::new(),
next: 0,
path: path.into(),
submodule,
tokens,
}
.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 {

View File

@ -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
},

View File

@ -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>,

View File

@ -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();

View File

@ -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,
})
}
}

View File

@ -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();
}

View File

@ -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();
}