Repository: casey/just (rust)

Issue:
Dependency executed twice when order changes
Perhaps a bug, or I am the bug, but I cannot get `just` to handle dependency execution like it does in the docs. The docs state "Dependencies will always run first, even if they are passed after a recipe that depends on them" and then shows: 
```$ just test build
cc main.c foo.c bar.c -o main
./test
testing… all tests passed!
```
In this case, `build` is only ran once.

If I replicate this, but with some simple `echo` commands, I get the dependency being executed twice:
```
build:
    echo "building something"

test: build
    echo "testing something"
```
```
$ just test build
echo "building something"
building something
echo "testing something"
testing something
echo "building something"
building something
```
To get the intended behavior, I have to call `build` before `test`:
```
$just build test
echo "building something"
building something
echo "testing something"
testing something
```

The end goal is to have multiple recipes have the same dependency but only execute said dependency once, irregardless of order. Since the docs seem to show it being possible, I am wondering if I am the bug?

Pull request:
Don't run duplicate recipe arguments
Fixes #1173.

Patch:
diff --git a/src/justfile.rs b/src/justfile.rs
index ce0ce9fd..718f2fd0 100644
--- a/src/justfile.rs
+++ b/src/justfile.rs
@@ -284,6 +284,15 @@ impl<'src> Justfile<'src> {
     search: &Search,
     ran: &mut BTreeSet<Vec<String>>,
   ) -> RunResult<'src, ()> {
+    let mut invocation = vec![recipe.name().to_owned()];
+    for argument in arguments {
+      invocation.push((*argument).to_string());
+    }
+
+    if ran.contains(&invocation) {
+      return Ok(());
+    }
+
     let (outer, positional) = Evaluator::evaluate_parameters(
       context.config,
       dotenv,
@@ -300,20 +309,19 @@ impl<'src> Justfile<'src> {
       Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search);
 
     for Dependency { recipe, arguments } in recipe.dependencies.iter().take(recipe.priors) {
-      let mut invocation = vec![recipe.name().to_owned()];
-
-      for argument in arguments {
-        invocation.push(evaluator.evaluate_expression(argument)?);
-      }
-
-      if !ran.contains(&invocation) {
-        let arguments = invocation
-          .iter()
-          .skip(1)
-          .map(String::as_ref)
-          .collect::<Vec<&str>>();
-        self.run_recipe(context, recipe, &arguments, dotenv, search, ran)?;
-      }
+      let arguments = arguments
+        .iter()
+        .map(|argument| evaluator.evaluate_expression(argument))
+        .collect::<RunResult<Vec<String>>>()?;
+
+      self.run_recipe(
+        context,
+        recipe,
+        &arguments.iter().map(String::as_ref).collect::<Vec<&str>>(),
+        dotenv,
+        search,
+        ran,
+      )?;
     }
 
     recipe.run(context, dotenv, scope.child(), search, &positional)?;
@@ -339,11 +347,6 @@ impl<'src> Justfile<'src> {
       }
     }
 
-    let mut invocation = vec![recipe.name().to_owned()];
-    for argument in arguments {
-      invocation.push((*argument).to_string());
-    }
-
     ran.insert(invocation);
     Ok(())
   }


Test patch:
diff --git a/tests/lib.rs b/tests/lib.rs
index 798b0455..04ca4398 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -33,6 +33,7 @@ mod quiet;
 mod quote;
 mod readme;
 mod regexes;
+mod run;
 mod search;
 mod shebang;
 mod shell;
diff --git a/tests/run.rs b/tests/run.rs
new file mode 100644
index 00000000..2d7d7d32
--- /dev/null
+++ b/tests/run.rs
@@ -0,0 +1,19 @@
+use crate::common::*;
+
+#[test]
+fn dont_run_duplicate_recipes() {
+  Test::new()
+    .justfile(
+      "
+      foo:
+        # foo
+    ",
+    )
+    .args(&["foo", "foo"])
+    .stderr(
+      "
+      # foo
+    ",
+    )
+    .run();
+}