Skip to content

Commit b96aba9

Browse files
authored
Run recipes from submodules in correct directory (#2829)
1 parent 3cb490a commit b96aba9

File tree

3 files changed

+113
-37
lines changed

3 files changed

+113
-37
lines changed

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3797,10 +3797,9 @@ Available recipes:
37973797
foo ... # foo is a great module!
37983798
```
37993799

3800-
Modules are still missing a lot of features, for example, the ability to depend
3801-
on recipes and refer to variables in other modules. See the
3802-
[module improvement tracking issue](https://github.com/casey/just/issues/2252)
3803-
for more information.
3800+
Modules are still missing a lot of features, for example, the ability to refer
3801+
to variables in other modules. See the [module improvement tracking
3802+
issue](https://github.com/casey/just/issues/2252) for more information.
38043803

38053804
### Hiding `justfile`s
38063805

src/justfile.rs

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use {super::*, serde::Serialize};
33
#[derive(Debug)]
44
struct Invocation<'src: 'run, 'run> {
55
arguments: Vec<&'run str>,
6-
module: &'run Justfile<'src>,
76
recipe: &'run Recipe<'src>,
87
}
98

@@ -69,24 +68,23 @@ impl<'src> Justfile<'src> {
6968
)
7069
}
7170

72-
fn evaluate_assignments<'run>(
71+
fn evaluate_scopes<'run>(
7372
&'run self,
7473
arena: &'run Arena<Scope<'src, 'run>>,
7574
config: &'run Config,
7675
dotenv: &'run BTreeMap<String, String>,
7776
overrides: &BTreeMap<String, String>,
7877
root: &'run Scope<'src, 'run>,
79-
scopes: &mut BTreeMap<String, &'run Scope<'src, 'run>>,
78+
scopes: &mut BTreeMap<String, (&'run Justfile<'src>, &'run Scope<'src, 'run>)>,
8079
search: &'run Search,
8180
) -> RunResult<'src> {
8281
let scope = Evaluator::evaluate_assignments(config, dotenv, self, overrides, root, search)?;
8382

8483
let scope = arena.alloc(scope);
85-
scopes.insert(self.module_path.clone(), scope);
86-
scopes.get(&self.module_path).unwrap();
84+
scopes.insert(self.module_path.clone(), (self, scope));
8785

8886
for module in self.modules.values() {
89-
module.evaluate_assignments(
87+
module.evaluate_scopes(
9088
arena,
9189
config,
9290
dotenv,
@@ -128,7 +126,7 @@ impl<'src> Justfile<'src> {
128126
let root = Scope::root();
129127
let arena = Arena::new();
130128
let mut scopes = BTreeMap::new();
131-
self.evaluate_assignments(
129+
self.evaluate_scopes(
132130
&arena,
133131
config,
134132
&dotenv,
@@ -138,7 +136,7 @@ impl<'src> Justfile<'src> {
138136
search,
139137
)?;
140138

141-
let scope = scopes.get(&self.module_path).unwrap();
139+
let scope = scopes.get(&self.module_path).unwrap().1;
142140

143141
match &config.subcommand {
144142
Subcommand::Command {
@@ -230,25 +228,20 @@ impl<'src> Justfile<'src> {
230228

231229
let ran = Ran::default();
232230
for invocation in invocations {
233-
let context = ExecutionContext {
234-
config,
235-
dotenv: &dotenv,
236-
module: invocation.module,
237-
search,
238-
};
239-
240231
Self::run_recipe(
241232
&invocation
242233
.arguments
243234
.iter()
244235
.copied()
245236
.map(str::to_string)
246237
.collect::<Vec<String>>(),
247-
&context,
238+
config,
239+
&dotenv,
248240
false,
249241
&ran,
250242
invocation.recipe,
251243
&scopes,
244+
search,
252245
)?;
253246
}
254247

@@ -289,7 +282,6 @@ impl<'src> Justfile<'src> {
289282
let recipe = self.get_recipe(&path[position]).unwrap();
290283
Ok(Invocation {
291284
arguments: arguments.into(),
292-
module: self,
293285
recipe,
294286
})
295287
} else {
@@ -308,11 +300,13 @@ impl<'src> Justfile<'src> {
308300

309301
fn run_recipe(
310302
arguments: &[String],
311-
context: &ExecutionContext<'src, '_>,
303+
config: &Config,
304+
dotenv: &BTreeMap<String, String>,
312305
is_dependency: bool,
313306
ran: &Ran<'src>,
314307
recipe: &Recipe<'src>,
315-
scopes: &BTreeMap<String, &Scope<'src, '_>>,
308+
scopes: &BTreeMap<String, (&Justfile<'src>, &Scope<'src, '_>)>,
309+
search: &Search,
316310
) -> RunResult<'src> {
317311
let mutex = ran.mutex(&recipe.namepath, arguments);
318312

@@ -322,41 +316,59 @@ impl<'src> Justfile<'src> {
322316
return Ok(());
323317
}
324318

325-
if !context.config.yes && !recipe.confirm()? {
319+
if !config.yes && !recipe.confirm()? {
326320
return Err(Error::NotConfirmed {
327321
recipe: recipe.name(),
328322
});
329323
}
330324

331-
let scope = scopes
325+
let (module, scope) = scopes
332326
.get(&recipe.module_path())
333327
.expect("failed to retrieve scope for module");
334328

335-
let (outer, positional) =
336-
Evaluator::evaluate_parameters(context, is_dependency, arguments, &recipe.parameters, scope)?;
329+
let context = ExecutionContext {
330+
config,
331+
dotenv,
332+
module,
333+
search,
334+
};
335+
336+
let (outer, positional) = Evaluator::evaluate_parameters(
337+
&context,
338+
is_dependency,
339+
arguments,
340+
&recipe.parameters,
341+
scope,
342+
)?;
337343

338344
let scope = outer.child();
339345

340-
let mut evaluator = Evaluator::new(context, true, &scope);
346+
let mut evaluator = Evaluator::new(&context, true, &scope);
341347

342348
Self::run_dependencies(
343-
context,
349+
config,
350+
&context,
344351
recipe.priors(),
352+
dotenv,
345353
&mut evaluator,
346354
ran,
347355
recipe,
348356
scopes,
357+
search,
349358
)?;
350359

351-
recipe.run(context, &scope, &positional, is_dependency)?;
360+
recipe.run(&context, &scope, &positional, is_dependency)?;
352361

353362
Self::run_dependencies(
354-
context,
363+
config,
364+
&context,
355365
recipe.subsequents(),
366+
dotenv,
356367
&mut evaluator,
357368
&Ran::default(),
358369
recipe,
359370
scopes,
371+
search,
360372
)?;
361373

362374
*guard = true;
@@ -365,12 +377,15 @@ impl<'src> Justfile<'src> {
365377
}
366378

367379
fn run_dependencies<'run>(
380+
config: &Config,
368381
context: &ExecutionContext<'src, 'run>,
369382
dependencies: &[Dependency<'src>],
383+
dotenv: &BTreeMap<String, String>,
370384
evaluator: &mut Evaluator<'src, 'run>,
371385
ran: &Ran<'src>,
372386
recipe: &Recipe<'src>,
373-
scopes: &BTreeMap<String, &Scope<'src, 'run>>,
387+
scopes: &BTreeMap<String, (&Justfile<'src>, &Scope<'src, 'run>)>,
388+
search: &Search,
374389
) -> RunResult<'src> {
375390
if context.config.no_dependencies {
376391
return Ok(());
@@ -389,10 +404,11 @@ impl<'src> Justfile<'src> {
389404
thread::scope::<_, RunResult>(|thread_scope| {
390405
let mut handles = Vec::new();
391406
for (recipe, arguments) in evaluated {
392-
handles.push(
393-
thread_scope
394-
.spawn(move || Self::run_recipe(&arguments, context, true, ran, recipe, scopes)),
395-
);
407+
handles.push(thread_scope.spawn(move || {
408+
Self::run_recipe(
409+
&arguments, config, dotenv, true, ran, recipe, scopes, search,
410+
)
411+
}));
396412
}
397413
for handle in handles {
398414
handle
@@ -403,7 +419,9 @@ impl<'src> Justfile<'src> {
403419
})?;
404420
} else {
405421
for (recipe, arguments) in evaluated {
406-
Self::run_recipe(&arguments, context, true, ran, recipe, scopes)?;
422+
Self::run_recipe(
423+
&arguments, config, dotenv, true, ran, recipe, scopes, search,
424+
)?;
407425
}
408426
}
409427

tests/modules.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,65 @@ fn submodule_shebang_recipes_run_in_submodule_directory() {
601601
.run();
602602
}
603603

604+
#[test]
605+
fn cross_module_dependency_runs_in_submodule_directory() {
606+
Test::new()
607+
.write("foo/bar", "BAR")
608+
.write("foo/mod.just", "foo:\n @cat bar")
609+
.justfile(
610+
"
611+
mod foo
612+
613+
main: foo::foo
614+
",
615+
)
616+
.arg("main")
617+
.stdout("BAR")
618+
.run();
619+
}
620+
621+
#[test]
622+
fn cross_module_dependency_with_no_cd_runs_in_invocation_directory() {
623+
Test::new()
624+
.write("root_file", "ROOT")
625+
.write(
626+
"foo/mod.just",
627+
"
628+
[no-cd]
629+
foo:
630+
@cat root_file
631+
",
632+
)
633+
.justfile(
634+
"
635+
mod foo
636+
637+
main: foo::foo
638+
",
639+
)
640+
.arg("main")
641+
.stdout("ROOT")
642+
.run();
643+
}
644+
645+
#[test]
646+
fn nested_cross_module_dependency_runs_in_correct_directory() {
647+
Test::new()
648+
.write("outer/inner/file", "NESTED")
649+
.write("outer/inner/mod.just", "task:\n @cat file")
650+
.write("outer/mod.just", "mod inner")
651+
.justfile(
652+
"
653+
mod outer
654+
655+
main: outer::inner::task
656+
",
657+
)
658+
.arg("main")
659+
.stdout("NESTED")
660+
.run();
661+
}
662+
604663
#[cfg(not(windows))]
605664
#[test]
606665
fn module_paths_beginning_with_tilde_are_expanded_to_homdir() {

0 commit comments

Comments
 (0)