This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,152 @@
Dependency Specification
========================
.. TODO(diataxis)
- reference - dependency spec
- reference - globbing
Dependencies in ``dune`` files can be specified using one of the following:
.. _source_tree:
- ``(:name <dependencies>)`` will bind the list of dependencies to the
``name`` variable. This variable will be available as ``%{name}`` in actions.
- ``(file <filename>)``, or simply ``<filename>``, depend on this file.
- ``(alias <alias-name>)`` depends on the construction of this alias. For
instance: ``(alias src/runtest)``.
- ``(alias_rec <alias-name>)`` depends on the construction of this
alias recursively in all children directories wherever it is
defined. For instance: ``(alias_rec src/runtest)`` might depend on
``(alias src/runtest)``, ``(alias src/foo/bar/runtest)``, etc.
- ``(glob_files <glob>)`` depends on all files matched by ``<glob>``. See the
:ref:`glob <glob>` for details.
- ``(glob_files_rec <glob>)`` is the recursive version of
``(glob_files <glob>)``. See the :ref:`glob <glob>` for details.
- ``(source_tree <dir>)`` depends on all source files in the subtree with root
``<dir>``.
- ``(universe)`` depends on everything in the universe. This is for
cases where dependencies are too hard to specify. Note that Dune
will not be able to cache the result of actions that depend on the
universe. In any case, this is only for dependencies in the
:term:`installed world`. You must still specify all dependencies that come
from the workspace.
- ``(package <pkg>)`` depends on all files installed by ``<package>``, as well
as on the transitive package dependencies of ``<package>``. This can be used
to test a command against the files that will be installed.
- ``(env_var <var>)`` depends on the value of the environment variable ``<var>``.
If this variable becomes set, becomes unset, or changes value, the target
will be rebuilt.
- ``(sandbox <config>)`` requires a particular sandboxing configuration.
``<config>`` can be one (or many) of:
- ``always``: the action requires a clean environment
- ``none``: the action must run in the build directory
- ``preserve_file_kind``: the action needs the files it reads to look
like normal files (so Dune won't use symlinks for sandboxing)
- ``(include <file>)`` read the s-expression in ``<file>`` and interpret it as
additional dependencies. The s-expression is expected to be a list of the
same constructs enumerated here.
In all these cases, the argument supports :doc:`variables`.
Named Dependencies
------------------
Dune allows a user to organize dependency lists by naming them. The user is
allowed to assign a group of dependencies a name that can later be referred to
in actions (like the ``%{deps}``, ``%{target}``, and ``%{targets}`` built in variables).
One instance where this is useful is for naming globs. Here's an
example of an imaginary bundle command:
.. code:: dune
(rule
(target archive.tar)
(deps
index.html
(:css (glob_files *.css))
(:js foo.js bar.js)
(:img (glob_files *.png) (glob_files *.jpg)))
(action
(run %{bin:bundle} index.html -css %{css} -js %{js} -img %{img} -o %{target})))
Note that a named dependency list can also include unnamed
dependencies (like ``index.html`` in the example above). Also, such
user defined names will shadow build in variables, so
``(:workspace_root x)`` will shadow the built-in ``%{workspace_root}``
variable.
.. _glob:
Glob
----
You can use globs to declare dependencies on a set of files. Note that globs
will match files that exist in the source tree as well as buildable targets, so
for instance you can depend on ``*.cmi``.
Dune supports globbing files in a single directory via ``(glob_files
...)`` and, starting with Dune 3.0, in all subdirectories recursively via ``(glob_files_rec
...)``. The glob is interpreted as follows:
- anything before the last ``/`` is taken as a literal path
- anything after the last ``/``, or everything if the glob contains no ``/``, is
interpreted using the glob syntax
Absolute paths are permitted in the ``(glob_files ...)`` term only. It's an error to pass
an absolute path (i.e., a path beginning with a ``/``) to ``(glob_files_rec ...)```.
The glob syntax is interpreted as follows:
- ``\<char>`` matches exactly ``<char>``, even if it's a special character
(``*``, ``?``, ...).
- ``*`` matches any sequence of characters, except if it comes first, in which
case it matches any character that is not ``.`` followed by anything.
- ``**`` matches any character that is not ``.`` followed by anything, except if
it comes first, in which case it matches anything.
- ``?`` matches any single character.
- ``[<set>]`` matches any character that is part of ``<set>``.
- ``[!<set>]`` matches any character that is not part of ``<set>``.
- ``{<glob1>,<glob2>,...,<globn>}`` matches any string that is matched by one of
``<glob1>``, ``<glob2>``, etc.
.. list-table:: Glob syntax examples
:header-rows: 1
* - Syntax
- Files matched
- Files not matched
* - ``x``
- ``x``
- ``y``
* - ``\*``
- ``*``
- ``x``
* - ``file*.txt``
- ``file1.txt``, ``file2.txt``
- ``f.txt``
* - ``*.txt``
- ``f.txt``
- ``.hidden.txt``
* - ``a**``
- ``aml``
- ``a.ml``
* - ``**``
- ``a/b``, ``a.b``
- (none)
* - ``a?.txt``
- ``a1.txt``, ``a2.txt``
- ``b1.txt``, ``a10.txt``
* - ``f[xyz].txt``
- ``fx.txt``, ``fy.txt``, ``fz.txt``
- ``f2.txt``, ``f.txt``
* - ``f[!xyz].txt``
- ``f2.txt``, ``fa.txt``
- ``fx.txt``, ``f.txt``
* - ``a.{ml,mli}``
- ``a.ml``, ``a.mli``
- ``a.txt``, ``b.ml``
* - ``../a.{ml,mli}``
- ``../a.ml``, ``../a.mli``
- ``a.ml``

View file

@ -0,0 +1,53 @@
Locks
=====
.. TODO(diataxis)
- howto: testing in general (note about concurrency)
- reference: locks
Given two rules that are independent, Dune will assume that their
associated actions can be run concurrently. Two rules are considered
independent if neither of them depend on the other, either directly or
through a chain of dependencies. This basic assumption allows Dune to
parallelize the build.
However, it is sometimes the case that two independent rules cannot be
executed concurrently. For instance, this can happen for more
complicated tests. In order to prevent Dune from running the
actions at the same time, you can specify that both actions take the
same lock:
.. code:: dune
(rule
(alias runtest)
(deps foo)
(locks m)
(action (run test.exe %{deps})))
(alias
(rule runtest)
(deps bar)
(locks m)
(action (run test.exe %{deps})))
Dune will make sure that the executions of ``test.exe foo`` and
``test.exe bar`` are serialized.
Although they don't live in the filesystem, lock names are interpreted as file
names. So for instance, ``(with-lock m ...)`` in ``src/dune`` and ``(with-lock
../src/m)`` in ``test/dune`` refer to the same lock.
Note also that locks are per build context. So if your workspace has two build
contexts setup, the same rule might still be executed concurrently between the
two build contexts. If you want a lock that is global to all build contexts,
simply use an absolute filename:
.. code:: dune
(rule
(alias runtest)
(deps foo)
(locks /tcp-port/1042)
(action (run test.exe %{deps})))

View file

@ -0,0 +1,22 @@
OCaml Flags
===========
In ``library``, ``executable``, ``executables``, and ``env`` stanzas,
you can specify OCaml compilation flags using the following fields:
- ``(flags <flags>)`` to specify flags passed to both ``ocamlc`` and
``ocamlopt``
- ``(ocamlc_flags <flags>)`` to specify flags passed to ``ocamlc`` only
- ``(ocamlopt_flags <flags>)`` to specify flags passed to ``ocamlopt`` only
For all these fields, ``<flags>`` is specified in the
:doc:`../reference/ordered-set-language`.
These fields all support ``(:include ...)`` forms.
The default value for ``(flags ...)`` is taken from the environment,
as a result it's recommended to write ``(flags ...)`` fields as
follows:
.. code:: dune
(flags (:standard <my options>))

View file

@ -0,0 +1,147 @@
Package Specification
=====================
.. TODO(diataxis)
- reference: packages
- howto: preparing an opam package
- tutorial: from zero to opam
Installation is the process of copying freshly built libraries,
binaries, and other files from the build directory to the system. Dune
offers two ways of doing this: via opam or directly via the ``install``
command. In particular, the installation model implemented by Dune
was copied from opam. Opam is the standard OCaml package manager.
In both cases, Dune only know how to install whole packages. A
package being a collection of executables, libraries, and other files.
In this section, we'll describe how to define a package, how to
"attach" various elements to it, and how to proceed with installing it
on the system.
.. _declaring-a-package:
Declaring a Package
-------------------
To declare a package, simply add a ``package`` stanza to your
``dune-project`` file:
.. code:: dune
(package
(name mypackage)
(synopsis "My first Dune package!")
(description "\| This is my first attempt at creating
"\| a project with Dune.
))
Once you have done this, Dune will know about the package named
``mypackage`` and you will be able to attach various elements to it.
The ``package`` stanza accepts more fields, such as dependencies.
Note that package names are in a global namespace, so the name you choose must
be universally unique. In particular, package managers never allow users to
release two packages with the same name.
.. TODO: describe this more in details
In older projects using Dune, packages were defined by manually writing a file
called ``<package-name>.opam`` at the root of the project. However, it's not
recommended to use this method in new projects, as we expect to deprecate it in
the future. The right way to define a package is with a ``package`` stanza in
the ``dune-project`` file.
See :doc:`../howto/opam-file-generation` for instructions on configuring Dune
to automatically generate ``.opam`` files based on the ``package`` stanzas.
Attaching Elements to a Package
-------------------------------
Attaching an element to a package means declaring to Dune that this
element is part of the said package. The method to attach an element
to a package depends on the kind of the element. In this subsection,
we will go through the various kinds of elements and describe how to
attach each of them to a package.
In the rest of this section, ``<prefix>`` refers to the directory in
which the user chooses to install packages. When installing via opam,
it's opam that sets this directory. When calling ``dune install``,
the installation directory is either guessed or can be manually
specified by the user. Defaults directories which replace guessing
can be set during the compilation of dune.
Sites of a Package
------------------
When packages need additional resources outside their binary, their location
could be hard to find. Moreover, some packages could add resources to another
package, e.g., in the case of plugins. These locations are called sites in
Dune. One package can define them. During execution, one site corresponds to a
list of directories. They are like layers, and the first directories have a higher
priority. Examples and precisions are available at :ref:`sites`.
Libraries
^^^^^^^^^
In order to attach a library to a package, merely add a
``public_name`` field to your library. This is the name that external
users of your libraries must use in order to refer to it. Dune
requires that a library's public name is either the name of the
package it is part of or start with the package name followed by a dot
character.
For instance:
.. code:: dune
(library
(name mylib)
(public_name mypackage.mylib))
After you have added a public name to a library, Dune will know to
install it as part of the package it is attached to. Dune installs
the library files in a directory ``<prefix>/lib/<package-name>``.
If the library name contains dots, the full directory in which the
library files are installed is ``lib/<comp1>/<comp2/.../<compn>``,
where ``<comp1>``, ``<comp2>``, ... ``<compn>`` are the dot-separated
component of the public library name. By definition, ``<comp1>`` is
always the package name.
Executables
^^^^^^^^^^^
Similar to libraries, to attach an executable to a package simply
add a ``public_name`` field to your ``executable`` stanza or a
``public_names`` field for ``executables`` stanzas. Designate this
name to match the available executables through the installed ``PATH``
(i.e., the name users must type in their shell to execute
the program), because Dune cannot guess an executable's relevant package
from its public name. It's also necessary to add a ``package`` field
unless the project contains a single package, in which case the executable
will be attached to this package.
For instance:
.. code:: dune
(executable
(name main)
(public_name myprog)
(package mypackage))
Once ``mypackage`` is installed on the system, the user will be able
to type the following in their shell:
.. code:: console
$ myprog
to execute the program.
Other Files
^^^^^^^^^^^
For all other kinds of elements, you must attach them manually via
an :doc:`/reference/dune/install` stanza.

View file

@ -0,0 +1,109 @@
Diffing and Promotion
=====================
You can use Diffing and Promotion flows to compare the outputs of your build in
the build directory with the source tree and/or copy the result of the rules
into your source tree to store the changes.
Diffing
=======
You can use the ``(diff <file1> <file2>)`` directive in a rule to compare
its output with the version in your source tree. It is useful when
your tests produce a file output and you want to make sure that output has
not changed.
.. TODO(diataxis)
- howto: diffing and promotion
- reference: diffing
``(diff <file1> <file2>)`` is very similar to ``(run diff <file1>
<file2>)``. In particular it behaves in the same way:
- When ``<file1>`` and ``<file2>`` are equal, it does nothing.
- When they are not, the differences are shown and the action fails.
However, it is different for the following reason:
- The exact command used for diff files can be configured via the
``--diff-command`` command line argument. Note that it's only
called when the files are not byte equals
- By default, it will use ``patdiff`` if it is installed. ``patdiff``
is a better diffing program. You can install it via opam with:
.. code:: console
$ opam install patdiff
- On Windows, both ``(diff a b)`` and ``(diff? a b)`` normalize
end-of-line characters before comparing the files.
- Since ``(diff a b)`` is a built-in action, Dune knows that ``a``
and ``b`` are needed, so you don't need to specify them
explicitly as dependencies.
- You can use ``(diff? a b)`` after a command that might or might not
produce ``b``, for cases where commands optionally produce a
*corrected* file
- If ``<file1>`` doesn't exist, it will compare with the empty file.
- It allows promotion. See below.
Note that ``(cmp a b)`` does no end-of-line normalization and doesn't
print a diff when the files differ. ``cmp`` is meant to be used with
binary files.
Promotion
=========
Promotion relates to copying the output of a Dune rule to your source tree.
Common uses include updating rule output after a failed diff (e.g., from a
test) or committing output to source control to cut down on dependencies
during packaging.
Promoting Test or Rule Output After Diffing
-------------------------------------------
Whenever an action ``(diff <file1> <file2>)`` or ``(diff? <file1>
<file2>)`` fails because the two files are different, Dune allows
you to promote ``<file2>`` as ``<file1>`` if ``<file1>`` is a source
file and ``<file2>`` is a generated file.
More precisely, let's consider the following Dune file:
.. code:: dune
(rule
(with-stdout-to data.out (run ./test.exe)))
(rule
(alias runtest)
(action (diff data.expected data.out)))
Where ``data.expected`` is a file committed in the source
repository. You can use the following workflow to update your test:
- Update the code of your test.
- Run ``dune runtest``. The diff action will fail and a diff will
be printed.
- Check the diff to make sure it's what you expect. This diff can be displayed
again by running ``dune promotion diff``.
- Run ``dune promote``. This will copy the generated ``data.out``
file to ``data.expected`` directly in the source tree.
You can also use ``dune runtest --auto-promote``, which will
automatically do the promotion.
Automatically Promoting Rule Output Into the Source Tree
--------------------------------------------------------
Dune rules support a ``(mode promote)`` directive that will automatically
copy their output into your source tree. This approach suits, for example, code
documentation generation flows where output needs to be committed to source
code control to enable easier browsing, or eliminate dependencies on a code
generation step during opam package installation.
More information, including customising when the source is copied, can be found
in :doc:`../reference/dune/rule`.

View file

@ -0,0 +1,76 @@
Sandboxing
==========
.. TODO(diataxis)
- explanation: sandboxing
- reference: sandboxing
The user actions that run external commands (``run``, ``bash``, ``system``)
are opaque to Dune, so Dune has to rely on manual specification of dependencies
and targets. One problem with manual specification is that it's error-prone.
It's often hard to know in advance what files the command will read,
and knowing a correct set of dependencies is very important for build
reproducibility and incremental build correctness.
To help with this problem Dune supports sandboxing.
An idealized view of sandboxing is that it runs the action in an environment
where it can't access anything except for its declared dependencies.
In practice, we have to make compromises and have some trade-offs between
simplicity, information leakage, performance, and portability.
The way sandboxing is currently implemented is that for each sandboxed action
we build a separate directory tree (sandbox directory) that mirrors the build
directory, filtering it to only contain the files that were declared as
dependencies. We run the action in that directory, and then we copy
the targets back to the build directory.
You can configure Dune to use sandboxing modes ``symlink``, ``hardlink``, or
``copy``, which determine how the individual files are populated (they will be
symlinked, hardlinked, or copied into the sandbox directory).
This approach is very simple and portable, but that comes with
certain limitations:
- The actions in the sandbox can use absolute paths to refer to anywhere outside
the sandbox. This means that only dependencies on relative paths in the build
tree can be enforced/detected by sandboxing.
- The sandboxed actions still run with full permissions of Dune itself, so
sandboxing is not a security feature. It won't prevent network access either.
- We don't erase the environment variables of the sandboxed
commands. This is something we want to change.
- Performance impact is usually small, but it can get noticeable for
fast actions with very large sets of dependencies.
Per-Action Sandboxing Configuration
-----------------------------------
Some actions may rely on sandboxing to work correctly.
For example, an action may need the input directory to contain nothing
except the input files, or the action might create temporary files that
break other build actions.
Some other actions may refuse to work with Sandboxing. For example,
if they rely on absolute path to the build directory staying fixed,
or if they deliberately use some files without declaring dependencies
(this is usually a very bad idea, by the way).
Generally it's better to improve the action so it works with or without
sandboxing (especially with), but sometimes you just can't do that.
Things like this can be described using the "sandbox" field in the dependency
specification language (see :doc:`dependency-spec`).
Global Sandboxing Configuration
-------------------------------
Dune always respects per-action sandboxing specification.
You can configure it globally to prefer a certain sandboxing mode if
the action allows it.
This is controlled by:
- ``dune --sandbox <...>`` CLI flag (see ``man dune-build``)
- ``DUNE_SANDBOX`` environment (see ``man dune-build``)
- ``(sandboxing_preference ..)`` field in the configuration file (see ``man
dune-config``)

View file

@ -0,0 +1,241 @@
Variables
=========
.. TODO(diataxis)
- reference: variables
- explanation: rule loading
Some fields can contains variables that are expanded by Dune.
The syntax of variables is as follows:
.. code::
%{var}
or, for more complex forms that take an argument:
.. code::
%{fun:arg}
In order to write a plain ``%{``, you need to write ``\%{`` in a
string.
Dune supports the following variables:
- ``project_root`` is the root of the current project. It is typically the root
of your project, and as long as you have a ``dune-project`` file there,
``project_root`` is independent of the workspace configuration.
- ``workspace_root`` is the root of the current workspace. Note that
the value of ``workspace_root`` isn't constant and depends on
whether your project is vendored or not.
- ``cc`` is the C compiler command line (list made of the compiler
name followed by its flags) that will be used to compile foreign code. For
more details about its content, please see :doc:`/reference/foreign-flags`.
- ``cxx`` is the C++ compiler command line being used in the
current build context.
- ``ocaml_bin`` is the path where ``ocamlc`` lives.
- ``ocaml`` is the ``ocaml`` binary.
- ``ocamlc`` is the ``ocamlc`` binary.
- ``ocamlopt`` is the ``ocamlopt`` binary.
- ``ocaml_version`` is the version of the compiler used in the
current build context.
- ``ocaml_where`` is the output of ``ocamlc -where``.
- ``arch_sixtyfour`` is ``true`` if using a compiler that targets a
64-bit architecture and ``false`` otherwise.
- ``null`` is ``/dev/null`` on Unix or ``nul`` on Windows.
- ``ext_obj``, ``ext_asm``, ``ext_lib``, ``ext_dll``, and ``ext_exe``
are the file extensions used for various artifacts.
- ``ext_plugin`` is ``.cmxs`` if ``natdynlink`` is supported and
``.cma`` otherwise.
- ``ocaml-config:v`` is for every variable ``v`` in the output of
``ocamlc -config``. Note that Dune processes the output
of ``ocamlc -config`` in order to make it a bit more stable across
versions, so the exact set of variables accessible this way might
not be exactly the same as what you can see in the output of
``ocamlc -config``. In particular, variables added in new OCaml versions
need to be registered in Dune before they can be used.
- ``profile`` is the profile selected via ``--profile``.
- ``context_name`` is the name of the context (``default``, or defined in the
workspace file)
- ``os_type`` is the type of the OS the build is targeting. This is
the same as ``ocaml-config:os_type``.
- ``architecture`` is the type of the architecture the build is targeting. This
is the same as ``ocaml-config:architecture``.
- ``model`` is the type of the CPU the build is targeting. This is
the same as ``ocaml-config:model``.
- ``system`` is the name of the OS the build is targeting. This is the same as
``ocaml-config:system``.
- ``ignoring_promoted_rules`` is ``true`` if
``--ignore-promoted-rules`` was passed on the command line and
``false`` otherwise.
- ``<ext>:<path>`` where ``<ext>`` is one of ``cmo``, ``cmi``, ``cma``,
``cmx``, or ``cmxa``. See :ref:`variables-for-artifacts`.
- ``env:<var>=<default`` expands to the value of the environment
variable ``<var>``, or ``<default>`` if it does not exist.
For example, ``%{env:BIN=/usr/bin}``.
Available since Dune 1.4.0.
- There are some Coq-specific variables detailed in :ref:`coq-variables`.
In addition, ``(action ...)`` fields support the following special variables:
- ``target`` expands to the one target.
- ``targets`` expands to the list of target.
- ``deps`` expands to the list of dependencies.
- ``^`` expands to the list of dependencies, separated by spaces.
- ``dep:<path>`` expands to ``<path>`` (and adds ``<path>`` as a dependency of
the action).
- ``exe:<path>`` is the same as ``<path>``, except when cross-compiling, in
which case it will expand to ``<path>`` from the host build context.
- ``bin:<program>`` expands ``<path>`` to ``program``. If ``program``
is installed by a workspace package (see :doc:`/reference/dune/install`
stanzas), the locally built binary will be used, otherwise it will be
searched in the ``<path>`` of the current build context. Note that ``(run
%{bin:program} ...)`` and ``(run program ...)`` behave in the same way.
``%{bin:...}`` is only necessary when you are using ``(bash ...)`` or
``(system ...)``.
- ``bin-available:<program>`` expands to ``true`` or ``false``, depending
on whether ``<program>`` is available or not.
- ``file-available:<path>`` expands to ``true`` or ``false``, depending on
whether the file at ``<path>`` is available in the current workspace.
- ``lib:<public-library-name>:<file>`` expands to the file's installation path
``<file>`` in the library ``<public-library-name>``. If
``<public-library-name>`` is available in the current workspace, the local
file will be used, otherwise the one from the :term:`installed world` will be
used.
- ``lib-private:<library-name>:<file>`` expands to the file's build path
``<file>`` in the library ``<library-name>``. Both public and private library
names are allowed as long as they refer to libraries within the same project.
- ``libexec:<public-library-name>:<file>`` is the same as ``lib:...``, except
when cross-compiling, in which case it will expand to the file from the host
build context.
- ``libexec-private:<library-name>:<file>`` is the same as ``lib-private:...``
except when cross-compiling, in which case it will expand to the file from the
host build context.
- ``lib-available:<library-name>`` expands to ``true`` or ``false`` depending on
whether the library is available or not. A library is available if at least
one of the following conditions holds:
- It's part the :term:`installed world`.
- It's available locally and is not optional.
- It's available locally, and all its library dependencies are
available.
- ``version:<package>`` expands to the version of the given
package. Packages defined in the current scope have priority over the
public packages. Public packages that don't install any libraries
will not be detected. How Dune determines the version
of a package is described :doc:`here <../advanced/package-version>`.
- ``read:<path>`` expands to the contents of the given file.
- ``read-lines:<path>`` expands to the list of lines in the given
file.
- ``read-strings:<path>`` expands to the list of lines in the given
file, unescaped using OCaml lexical convention.
The ``%{<kind>:...}`` forms are what allows you to write custom rules that work
transparently, whether things are installed or not.
Note that aliases are ignored by ``%{deps}``
The intent of this last form is to reliably read a list of strings
generated by an OCaml program via:
.. code:: ocaml
List.iter (fun s -> print_string (String.escaped s)) l
#. Dealing with circular dependencies introduced by variables
If you ever see Dune reporting a dependency cycle that involves a
variable such as `%{read:<path>}`, try to move `<path>` to a different
directory.
The reason you might see such dependency cycle is because Dune is
trying to evaluate the `%{read:<path>}` too early. For instance, let's
consider the following example:
.. code:: dune
(rule
(targets x)
(enabled_if %{read:y})
(action ...))
(rule
(with-stdout-to y (...)))
When Dune loads and interprets this file, it decides whether the
first rule is enabled by evaluating ``%{read:y}``. To
evaluate ``%{read:y}``, it must build ``y``. To build ``y``, it must
figure out the build rule that produces ``y``, and in order to do that, it must
first load and evaluate the above ``dune`` file. You can see how this
creates a cycle.
Some cycles might be more complex. In any case, when you see such an
error, the easiest thing to do is move the file that's being read
to a different directory, preferably a standalone one. You can use the
:doc:`/reference/dune/subdir` stanza to keep the logic self-contained in
the same ``dune`` file:
.. code:: dune
(rule
(targets x)
(enabled_if %{read:dir-for-y/y})
(action ...))
(subdir
dir-for-y
(rule
(with-stdout-to y (...))))
Expansion of Lists
------------------
Forms that expand to a list of items, such as ``%{cc}``, ``%{deps}``,
``%{targets}``, or ``%{read-lines:...}``, are suitable to be used in
``(run <prog> <arguments>)``. For instance in:
.. code:: dune
(run foo %{deps})
If there are two dependencies, ``a`` and ``b``, the produced command
will be equivalent to the shell command:
.. code:: console
$ foo "a" "b"
If you want both dependencies to be passed as a single argument,
you must quote the variable:
.. code:: dune
(run foo "%{deps}")
which is equivalent to the following shell command:
.. code:: console
$ foo "a b"
(The items of the list are concatenated with space.)
Please note: since ``%{deps}`` is a list of items, the first one may be
used as a program name. For instance:
.. code:: dune
(rule
(targets result.txt)
(deps foo.exe (glob_files *.txt))
(action (run %{deps})))
Here is another example:
.. code:: dune
(rule
(target foo.exe)
(deps foo.c)
(action (run %{cc} -o %{target} %{deps} -lfoolib)))