Dune does not find rule for implicitly-produced file

In my Dune-based project, I want to apply a filter to all JSON files, so that they will be “normalized” via jq . -S before being saved as test oracles. This will prevent spurious differences (whitespace, key order, etc.) from creating unnecessary noise.

The problem is that the commands that produce such files can only write their outputs to other files, not to stdout, so I cannot directly filter them with jq. They create a .json file (whose contents I do not care about), and this file will be jq-ified to produce a final JSON, which is the output I care about.

However, when I try to do so using a rule containing an action with multiple commands (with progn), the command reading the first .json file fails with No rule found, as if it expected the file to be necessarily produced by a specific rule.

Here’s a minimal example: after doing dune init project proj and adding this rule to lib/dune:

(rule
 (targets "b.txt")
 (action (progn
         (run touch a.txt) ; produces a.txt, but I don't care about it
         (with-stdin-from "a.txt" (with-stdout-to "b.txt" (run cat))) ; consumes a.txt
         )
 )
)

I get Error: No rule found for lib/a.txt when running dune build.

Since the first run command creates the a.txt file, and the commands are supposed to run in sequence, I expected the second command to be able to use a.txt. But the with-stdin-from part does not seem to like it.

Is there a way to do this without separating the commands in two rules?

Wrap the offending actions with no-infer.

So:

(no-infer (with-stdin-from "a.txt" (with-stdout-to "b.txt" (run cat))))
1 Like

The no-infer answer is good, but I feel like the cleanest solution would be to consider a.txt as a legitimate intermediate target and split your rule in two:

(rule
 (targets a.txt)
 (action (run touch a.txt)))

(rule
 (targets b.txt)
 (action
  (with-stdin-from "a.txt" (with-stdout-to "b.txt" (run cat)))))
2 Likes