Dune: How to add a dependency on one executable to another?

Hi everyone.

I have a Dune project consisting of two executables, each defined with the executable stanza in their own subdirectories.

One of the executables uses the Bos library to invoke the other executable; it’s a “driver” of that executable.

Suppose the first exectuable is called emit and the second executable is called package.

I’d like to ensure that emit has been been built by Dune before I try to invoke it with package (otherwise, I might get a “file not found” error, or changes to emit might not be visible when I invoke package).

As far as I can tell, I cannot add arbitrary dependencies to the executable stanza of Dune.

Is there a solution? Perhaps I’m going about this the wrong way?

Perhaps you can define a rule with both executables as dependencies to do what you want?

Best wishes,
Nicolás

Another option is to extract the needed functionality from emit into a library, and then call out to the library using package.

1 Like

See https://github.com/ocaml/dune/issues/3499 (Allow dependencies for executables).

The work-around I use is to copy the executable and then install the copy. The copying rule adds the dependency. e.g.

Thank you for the GitHub link and for reporting it! It describes the issue exactly. I’ll take a look at the workaround in the mean time.

I may have a similar problem, or maybe I just don’t know how to read the Dune documentation.

I have a dune executable whose public name is foo, and I write another executable bar (in a separate directory) that calls foo at runtime. I would like to test the second executable, but I don’t know how to specify in its dependencies that the test needs foo installed to run.

I wrote a synthetic repro case here: Gabriel Scherer / dune-executable-dependencies-repro-case · GitLab

Because I don’t know how to say that the test for bar also depends on foo, the test passes or fails depending on the internal build order (whether foo happens to have been built first). This is with Dune 3.16.0.

# start from a clean state
$ dune clean

# this run of "dune runtest" fails
$ dune runtest
sh: line 1: foo: command not found
File "test/bar.expected", line 1, characters 0-0:
diff --git 1/_build/default/test/bar.expected 2/_build/default/test/bar.out
index 31f77a6..e69de29 100644
--- 1/_build/default/test/bar.expected
+++ 2/_build/default/test/bar.out
@@ -1 +0,0 @@
-Foo !

# dune build now also fails
$ dune build
sh: line 1: foo: command not found  
File "test/bar.expected", line 1, characters 0-0:
diff --git 1/_build/default/test/bar.expected 2/_build/default/test/bar.out
index 31f77a6..e69de29 100644
--- 1/_build/default/test/bar.expected
+++ 2/_build/default/test/bar.out
@@ -1 +0,0 @@
-Foo !

# but a second run (different ordering) succeeds
$ dune build

# and now the test passes
$ dune runtest

The test rule is as follows:

(rule
 (alias runtest)
 (action (progn
   (with-stdout-to bar.out
     (run bar))
   (diff bar.expected bar.out)
 ))
)

I tried to add (deps foo bar), or (deps ../foo ../bar), but none of these seem to work. It looks like it can figure out the bar dependency from the action script, but of course there is no apparent dependency on foo in the action script.

(I went to read the documentation on Dependency specification, but I cannot find anything that seems related.)

I think (deps %{bin:foo}) should work.

I think one issue here is that there is a difference between the “public” executable foo (which is installed in _build/install) and the plain executable built by Dune from bar.ml (which is ../foo/foo.exe). I agree that it is confusing and maybe not well documented.

Cheers,
Nicolas

This seems to work, thanks! I remember having used this %{bin:...} stuff in the past, but I didn’t try it now because it is not listed in the Dependency Specification documentation. I just looked, they are mentioned in the Variables documentation, as something that can only be used inside (action ...) expressions – but then it shows examples that are outside this fragment.

2 Likes

You can actually add this type of deps, but it has to be a deps on a whole package, which will include the executable.
You do that with

(link_deps (package mypkg))

It might be a bit weird but it works very well.

1 Like