dune files¶
dune
files are the main part of dune. They are used to describe libraries,
executables, tests, and everything dune needs to know about.
The syntax of dune
files is described in Metadata format section.
Stanzas¶
dune
files are composed of stanzas. For instance a typical
dune
looks like:
(library
(name mylib)
(libraries base lwt))
(rule
(target foo.ml)
(deps generator/gen.exe)
(action (run %{deps} -o %{target})))
The following sections describe the available stanzas and their meaning.
jbuild_version¶
Deprecated. This stanza is no longer used and will be removed in the future.
library¶
The library
stanza must be used to describe OCaml libraries. The
format of library stanzas is as follows:
(library
(name <library-name>)
<optional-fields>)
<library-name>
is the real name of the library. It determines the
names of the archive files generated for the library as well as the
module name under which the library will be available, unless
(wrapped false)
is used (see below). It must be a valid OCaml
module name but doesn’t need to start with a uppercase letter.
For instance, the modules of a library named foo
will be
available as Foo.XXX
outside of foo
itself. It is however
allowed to write an explicit Foo
module, in which case this will
be the interface of the library and you are free to expose only the
modules you want.
Note that by default libraries and other things that consume OCaml/Reason modules only consume modules from the directory where the stanza appear. In order to declare a multi-directory library, you need to use the include_subdirs stanza.
<optional-fields>
are:
(public_name <name>)
this is the name under which the library can be referred to as a dependency when it is not part of the current workspace, i.e. when it is installed. Without a(public_name ...)
field, the library will not be installed by dune. The public name must start by the package name it is part of and optionally followed by a dot and anything else you want. The package name must be one of the packages that dune knows about, as determined by the dune-project files(synopsis <string>)
should give a one-line description of the library. This is used by tools that list installed libraries(modules <modules>)
specifies what modules are part of the library. By default dune will use all the .ml/.re files in the same directory as thedune
file. This include ones that are present in the file system as well as ones generated by user rules. You can restrict this list by using a(modules <modules>)
field.<modules>
uses the Ordered set language where elements are module names and don’t need to start with a uppercase letter. For instance to exclude moduleFoo
:(modules (:standard \ foo))
(libraries <library-dependencies>)
is used to specify the dependencies of the library. See the section about Library dependencies for more details(wrapped <boolean>)
specifies whether the modules of the library should be available only through the top-level library module, or should all be exposed at the top level. The default istrue
and it is highly recommended to keep it this way. Because OCaml top-level modules must all be unique when linking an executables, polluting the top-level namespace will make your library unusable with other libraries if there is a module name clash. This option is only intended for libraries that manually prefix all their modules by the library name and to ease porting of existing projects to dune(wrapped (transition <message>))
Is the same as(wrapped true)
except that it will also generate unwrapped (not prefixed by the library name) modules to preserve compatibility. This is useful for libraries that would like to transition from(wrapped false)
to(wrapped true)
without breaking compatibility for users. The<message>
will be included in the deprecation notice for the unwrapped modules.(preprocess <preprocess-spec>)
specifies how to preprocess files if needed. The default isno_processing
. Other options are described in the Preprocessing specification section(preprocessor_deps (<deps-conf list>))
specifies extra dependencies of the preprocessor, for instance if the preprocessor reads a generated file. The specification of dependencies is described in the Dependency specification section(optional)
, if present it indicates that the library should only be built and installed if all the dependencies are available, either in the workspace or in the installed world. You can use this to provide extra features without adding hard dependencies to your project(c_names (<names>))
, if your library has stubs, you must list the C files in this field, without the.c
extension(cxx_names (<names>))
is the same asc_names
but for C++ stubs(install_c_headers (<names>))
, if your library has public C header files that must be installed, you must list them in this field, without the.h
extension(modes <modes>)
modes which should be built by default. The most common use for this feature is to disable native compilation when writing libraries for the OCaml toplevel. The following modes are available:byte
,native
andbest
.best
isnative
orbyte
when native compilation is not available(no_dynlink)
is to disable dynamic linking of the library. This is for advanced use only, by default you shouldn’t set this option(kind <kind>)
is the kind of the library. The default isnormal
, other available choices areppx_rewriter
andppx_deriver
and must be set when the library is intended to be used as a ppx rewriter or a[@@deriving ...]
plugin. The reason whyppx_rewriter
andppx_deriver
are split is historical and hopefully we won’t need two options soon. Both ppx kinds support an optional field(cookies <cookies>)
where<cookies>
is a list of pairs(<name> <value>)
with<name>
being the cookie name and<value>
is a string that supports Variables expansion evaluated by each invocation of the preprocessor (note: libraries that share cookies with the same name should agree on their expanded value)(ppx_runtime_libraries (<library-names>))
is for when the library is a ppx rewriter or a[@@deriving ...]
plugin and has runtime dependencies. You need to specify these runtime dependencies here(virtual_deps (<opam-packages>)
. Sometimes opam packages enable a specific feature only if another package is installed. This is for instance the case ofctypes
which will only installctypes.foreign
if the dummyctypes-foreign
package is installed. You can specify such virtual dependencies here. You don’t need to do so unless you use dune to synthesize thedepends
anddepopts
sections of your opam filejs_of_ocaml
. See the section about js_of_ocamlflags
,ocamlc_flags
andocamlopt_flags
. See the section about OCaml flags(library_flags (<flags>))
is a list of flags that are passed as it toocamlc
andocamlopt
when building the library archive files. You can use this to specify-linkall
for instance.<flags>
is a list of strings supporting Variables expansion(c_flags <flags>)
specifies the compilation flags for C stubs, using the Ordered set language. This field supports(:include ...)
forms(cxx_flags <flags>)
is the same asc_flags
but for C++ stubs(c_library_flags <flags>)
specifies the flags to pass to the C compiler when constructing the library archive file for the C stubs.<flags>
uses the Ordered set language and supports(:include ...)
forms. When you are writing bindings for a C library namedbar
, you should typically write-lbar
here, or whatever flags are necessary to to link against this library
(self_build_stubs_archive <c-libname>)
indicates to dune that the library has stubs, but that the stubs are built manually. The aim of the field is to embed a library written in foreign language and/or building with another build system. It is not for casual uses, see the re2 library for an example of use(modules_without_implementation <modules>)
specifies a list of modules that have only a.mli
or.rei
but no.ml
or.re
file. Such modules are usually referred as mli only modules. They are not officially supported by the OCaml compiler, however they are commonly used. Such modules must only define types. Since it is not reasonably possible for dune to check that this is the case, dune requires the user to explicitly list such modules to avoid surprises.<modules>
must be a subset of the modules listed in the(modules ...)
field.(private_modules <modules>)
specifies a list of modules that will be marked as private. Private modules are inaccessible from outside the libraries they are defined in.(allow_overlapping_dependencies)
allows external dependencies to overlap with libraries that are present in the workspace(no_keep_locs)
does nothing. It used to be a necessary hack when we were waiting for proper support for virtual libraries. Do not use in new code, it will be deleted in dune 2.0(enabled_if <blang expression>)
allows to conditionally disable a library. A disabled library cannot be built and will not be installed. The condition is specified using the blang, and the field allows for the%{os_type}
variable, which is expanded to the type of OS being targeted by the current build. Its value is the same as the value of theos_type
parameter in the output ofocamlc -config
Note that when binding C libraries, dune doesn’t provide special support for
tools such as pkg-config
, however it integrates easily with configurator by
using (c_flags (:include ...))
and (c_library_flags (:include ...))
.
executable¶
The executable
stanza must be used to describe an executable. The
format of executable stanzas is as follows:
(executable
(name <name>)
<optional-fields>)
<name>
is a module name that contains the main entry point of the
executable. There can be additional modules in the current directory, you only
need to specify the entry point. Given an executable
stanza with (name
<name>)
, dune will know how to build <name>.exe
, <name>.bc
and
<name>.bc.js
. <name>.exe
is a native code executable, <name>.bc
is a
bytecode executable which requires ocamlrun
to run and <name>.bc.js
is a
JavaScript generated using js_of_ocaml.
Note that in case native compilation is not available, <name>.exe
will in fact be a custom byte-code executable. Custom in the sense of
ocamlc -custom
, meaning that it is a native executable that embeds
the ocamlrun
virtual machine as well as the byte code. As such you
can always rely on <name>.exe
being available. Moreover, it is
usually preferable to use <name>.exe
in custom rules or when
calling the executable by hand. This is because running a byte-code
executable often requires loading shared libraries that are locally
built, and so requires additional setup such as setting specific
environment variables and dune doesn’t do at the moment.
Native compilation is considered not available when there is no ocamlopt
binary at the same place as where ocamlc
was found.
Executables can also be linked as object or shared object files. See linking modes for more information.
<optional-fields>
are:
(public_name <public-name>)
specifies that the executable should be installed under that name. It is the same as adding the following stanza to yourdune
file:(install (section bin) (files (<name>.exe as <public-name>)))
Linking modes¶
The modes
field allows to select what linking modes should be used
to link executables. Each mode is a pair (<compilation-mode>
<binary-kind>)
where <compilation-mode>
describes whether the
byte code or native code backend of the OCaml compiler should be used
and <binary-kind>
describes what kind of file should be produced.
<compilation-mode>
must be byte
, native
or best
, where
best
is native
with a fallback to byte-code when native
compilation is not available.
<binary-kind>
is one of:
c
for producing OCaml bytecode embedded in a C fileexe
for normal executablesobject
for producing static object files that can be manually linked into C applicationsshared_object
for producing object files that can be dynamically loaded into an application. This mode can be used to write a plugin in OCaml for a non-OCaml application.js
for producing Javascript from bytecode executables, see Explicit JS mode.
For instance the following executables
stanza will produce byte
code executables and native shared objects:
(executables
((names (a b c))
(modes ((byte exe) (native shared_object)))))
Additionally, you can use the following short-hands:
c
for(byte c)
exe
for(best exe)
object
for(best object)
shared_object
for(best shared_object)
byte
for(byte exe)
native
for(native exe)
js
for(byte js)
For instance the following modes
fields are all equivalent:
(modes (exe object shared_object))
(modes ((best exe)
(best object)
(best shared_object)))
The extensions for the various linking modes are chosen as follows:
compilation mode |
binary kind |
extensions |
byte |
exe |
.bc and .bc.js |
native/best |
exe |
.exe |
byte |
object |
.bc%{ext_obj} |
native/best |
object |
.exe%{ext_obj} |
byte |
shared_object |
.bc%{ext_dll} |
native/best |
shared_object |
%{ext_dll} |
byte |
c |
.bc.c |
byte |
js |
.bc.js |
Where %{ext_obj}
and %{ext_dll}
are the extensions for object
and shared object files. Their value depends on the OS, for instance
on Unix %{ext_obj}
is usually .o
and %{ext_dll}
is usually
.so
while on Windows %{ext_obj}
is .obj
and %{ext_dll}
is .dll
.
Note that when (byte exe)
is specified but neither (best exe)
nor (native exe)
are specified, Dune still knows how to build
an executable with the extension .exe
. In such case, the .exe
version is the same as the .bc
one except that it is linked with
the -custom
option of the compiler. You should always use the
.exe
rather that the .bc
inside build rules.
executables¶
The executables
stanza is the same as the executable
stanza, except that
it is used to describe several executables sharing the same configuration.
It shares the same fields as the executable
stanza, except that instead of
(name ...)
and (public_name ...)
you must use:
(names <names>)
where<names>
is a list of entry point names. As forexecutable
you only need to specify the modules containing the entry point of each executable(public_names <names>)
describes under what name each executable should be installed. The list of names must be of the same length as the list in the(names ...)
field. Moreover you can use-
for executables that shouldn’t be installed
rule¶
The rule
stanza is used to create custom user rules. It tells dune how
to generate a specific set of files from a specific set of dependencies.
The syntax is as follows:
(rule
(target[s] <filenames>)
(action <action>)
<optional-fields>)
<filenames>
is a list of file names (if defined with targets
)
or exactly one file name (if defined with target
). Note that
currently dune only supports user rules with targets in the current
directory.
<action>
is the action to run to produce the targets from the dependencies.
See the User actions section for more details.
<optional-fields>
are:
(deps <deps-conf list>)
to specify the dependencies of the rule. See the Dependency specification section for more details.(mode <mode>)
to specify how to handle the targets, see modes for details(fallback)
is deprecated and is the same as(mode fallback)
(locks (<lock-names>))
specify that the action must be run while holding the following locks. See the Locks section for more details.
Note that contrary to makefiles or other build systems, user rules currently
don’t support patterns, such as a rule to produce %.y
from %.x
for any
given %
. This might be supported in the future.
modes¶
By default, the target of a rule must not exist in the source tree and dune will error out when this is the case.
However, it is possible to change this behavior using the mode
field. The following modes are available:
standard
, this is the standard modefallback
, in this mode, when the targets are already present in the source tree, dune will ignore the rule. It is an error if only a subset of the targets are present in the tree. The common use of fallback rules is to generate default configuration files that may be generated by a configure script.
promote
or(promote <options>)
, in this mode, the files in the source tree will be ignored. Once the rule has been executed, the targets will be copied back to the source treeThe following options are available: -
(until-clean)
means thatdune clean
will remove the promoted files from the source tree -(into <dir>)
means that the files are promoted in<dir>
instead of the current directory. This feature is available since Dune 1.8 -(only <predicate>)
means that only a subset of the targets should be promoted. The argument is a predicate in a syntax similar to the argument of (dirs …). This feature is available since dune 1.10promote-until-clean
is the same as(promote (until-clean))
(promote-into <dir>)
is the same as(promote (into <dir>))
(promote-until-clean-into <dir>)
is the same as(promote (until-clean) (into <dir>))
The (promote <options>)
form is only available since Dune
1.10. Before Dune 1.10, you need to use one of the promote-...
forms. The promote-...
forms should disappear in Dune 2.0, so
using the more generic (promote <options>)
form should be prefered
in new projects.
There are two use cases for promote rules. The first one is when the
generated code is easier to review than the generator, so it’s easier
to commit the generated code and review it. The second is to cut down
dependencies during releases: by passing --ignore-promoted-rules
to dune, rules will (mode promote)
will be ignored and the source
files will be used instead. The -p/--for-release-of-packages
flag
implies --ignore-promote-rules
. However, rules that promotes only
a subset of their targets via (only ...)
are never ignored.
inferred rules¶
When using the action DSL (see User actions), it is most of the time obvious what are the dependencies and targets.
For instance:
(rule
(target b)
(deps a)
(action (copy %{deps} %{target})))
In this example it is obvious by inspecting the action what the dependencies and targets are. When this is the case you can use the following shorter syntax, where dune infers dependencies and targets for you:
(rule <action>)
For instance:
(rule (copy a b))
Note that in dune, targets must always be known
statically. Especially, this mean that dune must be able to
statically determine all targets. For instance, this (rule ...)
stanza is rejected by dune:
(rule (copy a b.%{read:file}))
ocamllex¶
(ocamllex <names>)
is essentially a shorthand for:
(rule
(target <name>.ml)
(deps <name>.mll)
(action (chdir %{workspace_root}
(run %{bin:ocamllex} -q -o %{target} %{deps}))))
To use a different rule mode, use the long form:
(ocamllex
(modules <names>)
(mode <mode>))
ocamlyacc¶
(ocamlyacc <names>)
is essentially a shorthand for:
(rule
(targets <name>.ml <name>.mli)
(deps <name>.mly)
(action (chdir %{workspace_root}
(run %{bin:ocamlyacc} %{deps}))))
To use a different rule mode, use the long form:
(ocamlyacc
(modules <names>)
(mode <mode>))
menhir¶
A menhir
stanza is available to support the menhir parser generator. See
the Menhir section for details.
cinaps¶
A cianps
stanza is available to support the cinaps
tool. See
the cinaps website for more
details.
alias¶
The alias
stanza lets you add dependencies to an alias, or specify an action
to run to construct the alias.
The syntax is as follows:
(alias
(name <alias-name>)
(deps <deps-conf list>)
<optional-fields>)
<name>
is an alias name such as runtest
.
<deps-conf list>
specifies the dependencies of the alias. See the
Dependency specification section for more details.
<optional-fields>
are:
<action>
, an action to run when constructing the alias. See the User actions section for more details.(package <name>)
indicates that this alias stanza is part of package<name>
and should be filtered out if<name>
is filtered out from the command line, either with--only-packages <pkgs>
or-p <pkgs>
(locks (<lock-names>))
specify that the action must be run while holding the following locks. See the Locks section for more details.(enabled_if <blang expression>)
specifies the boolean condition that must be true for the tests to run. The condition is specified using the blang, and the field allows for variables to appear in the expressions.
The typical use of the alias
stanza is to define tests:
(alias
(name runtest)
(action (run %{exe:my-test-program.exe} blah)))
See the section about Running tests for details.
Note that if your project contains several packages and you run the tests
from the opam file using a build-test
field, then all your runtest
alias
stanzas should have a (package ...)
field in order to partition the set of
tests.
install¶
Dune supports installing packages on the system, i.e. copying freshly built artifacts from the workspace to the system. See the installation section for more details.
Handling of the .exe extension on Windows¶
Under Microsoft Windows, executables must be suffixed with
.exe
. Dune tries to make sure that executables are always
installed with this extension on Windows.
More precisely, when installing a file via an (install ...)
stanza, if the source file has extension .exe
or .bc
, then
dune implicitly adds the .exe
extension to the destination, if
not already present.
copy_files¶
The copy_files
and copy_files#
stanzas allow to specify that
files from another directory could be copied if needed to the current
directory.
The syntax is as follows:
(copy_files <glob>)
<glob>
represents the set of files to copy, see the glob for details.
The difference between copy_files
and copy_files#
is the same
as the difference between the copy
and copy#
action. See the
User actions section for more details.
include¶
The include
stanza allows to include the contents of another file into the
current dune file. Currently, the included file cannot be generated and must be
present in the source tree. This feature is intended to be used in conjunction
with promotion, when parts of a dune file are to be generated.
For instance:
(include dune.inc)
(rule (with-stdout-to dune.inc.gen (run ./gen-dune.exe)))
(alias
(name runtest)
(action (diff dune.inc dune.inc.gen)))
With this dune file, running dune as follow will replace the
dune.inc
file in the source tree by the generated one:
$ dune build @runtest --auto-promote
tests¶
The tests
stanza allows one to easily define multiple tests. For example we
can define two tests at once with:
(tests
(names mytest expect_test)
<optional fields>)
This will define an executable named mytest.exe
that will be executed as
part of the runtest
alias. If the directory also contains an
expect_test.expected
file, then expect_test
will be used to define an
expect test. That is, the test will be executed and its output will be compared
to expect_test.expected
.
The optional fields that are supported are a subset of the alias and executables
fields. In particular, all fields except for public_names
are supported from
the executables stanza. Alias fields apart from
name
are allowed.
By default the test binaries are run without options. The action
field can
be used to override the test binary invocation, for example if you’re using
alcotest and wish to see all the test failures on the standard output when
running dune runtest you can use the following stanza:
(tests
(names mytest)
(libraries alcotest mylib)
(action (run %{test} -e)))
test¶
The test
stanza is the singular form of tests
. The only difference is
that it’s of the form:
(test
(name foo)
<optional fields>)
where the name
field is singular. The same optional fields are supported.
env¶
The env
stanza allows to modify the environment. The syntax is as
follow:
(env
(<profile1> <settings1>)
(<profile2> <settings2>)
...
(<profilen> <settingsn>))
The first form (<profile> <settings>)
that correspond to the
selected build profile will be used to modify the environment in this
directory. You can use _
to match any build profile.
Fields supported in <settings>
are:
any OCaml flags field, see OCaml flags for more details.
(c_flags <flags>)
and(cxx_flags <flags>)
to specify compilation flags for C and C++ stubs, respectively. See library for more details.(env-vars (<var1> <val1>) .. (<varN> <valN>))
. This will add the corresponding variables to the environment in which the build commands are executed, and under whichdune exec
runs. At the moment, this mechanism is only supported indune-workspace
files.(binaries <filepath> (<filepath> as <name>))
. This will make the binary at<filepath>
as<name>
. If the<name>
isn’t provided, then it will be inferred from the basename of<filepath>
by dropping the.exe
suffix if it exists.(inline_tests <state>)
where state is eitherenabled
,disabled
orignored
. This field is available since Dune 1.11. It controls the value of the variable%{inline_tests}
that is read by the inline test framework. The default value isdisabled
for therelease
profile andenabled
otherwise.
dirs (since 1.6)¶
The dirs
stanza allows to tell specify the sub-directories dune will
include in a build. The syntax is based on dune’s predicate language and allows
the user the following operations:
The special value
:standard
which refers to the default set of used directories. These are the directories that don’t start with.
or_
.Set operations. Differences are expressed with backslash:
* \ bar
, unions are done by listing multiple items.Sets can be defined using globs.
Examples:
(dirs *) ;; include all directories
(dirs :standard \ ocaml) ;; include all directories except ocaml
(dirs :standard \ test* foo*) ;; exclude all directories that start with test or foo
A directory that is not included by this stanza will not be eagerly scanned by
Dune. Any dune
or other special files in it won’t be interpreted either and
will be treated as raw data. It is however possible to depend on files inside
ignored sub-directories.
data_only_dirs (since 1.6)¶
Dune allows the user to treat directories as data only. Dune files in these directories will not be evaluated for their rules, but the contents of these directories will still be usable as dependencies for other rules.
The syntax is the same as for the dirs
stanza except that :standard
is by default empty.
Example:
;; dune files in fixtures_* dirs are ignored
(data_only_dirs fixtures_*)
ignored_subdirs (deprecated in 1.6)¶
One may also specify data only directories using the ignored_subdirs
stanza. The meaning is the same as data_only_dirs
but the syntax isn’t as
flexible and only accepts a list of directory names. It is advised to switch to
the new data_only_dirs
stanza.
Example:
(ignored_subdirs (<sub-dir1> <sub-dir2> ...))
All of the specified <sub-dirn>
will be ignored by dune. Note that users
should rely on the dirs
stanza along with the appropriate set operations
instead of this stanza. For example:
(dirs :standard \ <sub-dir1> <sub-dir2> ...)
vendored_dirs (since 1.11)¶
Dune supports vendoring of other dune-based projects natively since simply copying a project into a subdirectory of your own project will work. Simply doing that has a few limitations though. You can workaround those by explicitly marking such directories as containing vendored code.
Example:
(vendored_dirs vendor)
Dune will not resolve aliases in vendored directories meaning by default it will
not build all installable targets, run the test, format or lint the code located
in such a directory while still building the parts your project depend upon.
Libraries and executable in vendored directories will also be built with a -w
-a
flag to suppress all warnings and prevent pollution of your build output.
include_subdirs¶
The include_subdirs
stanza is used to control how dune considers
sub-directories of the current directory. The syntax is as follow:
(include_subdirs <mode>)
Where <mode>
maybe be one of:
no
, the defaultunqualified
When the include_subdirs
stanza is not present or <mode>
is
no
, dune considers sub-directories as independent. When <mode>
is unqualified
, dune will assume that the sub-directories of the
current directory are part of the same group of directories. In
particular, dune will scan all these directories at once when looking
for OCaml/Reason files. This allows you to split a library between
several directories. unqualified
means that modules in
sub-directories are seen as if they were all in the same directory. In
particular, you cannot have two modules with the same name in two
different directories. It is planned to add a qualified
mode in
the future.
Note that sub-directories are included recursively, however the
recursion will stop when encountering a sub-directory that contains
another include_subdirs
stanza. Additionally, it is not allowed
for a sub-directory of a directory with (include_subdirs <x>)
where <x>
is not no
to contain one of the following stanzas:
library
executable(s)
test(s)
toplevel¶
The toplevel
stanza allows one to define custom toplevels. Custom toplevels
automatically load a set of specified libraries and are runnable like normal
executables. Example:
(toplevel
(name tt)
(libraries str))
This will create a toplevel with the str
library loaded. We may build and
run this toplevel with:
$ dune exec ./tt.exe
external_variant¶
The external_variant
allow to declare a tagged implementation that does not
live inside the virtual library project.
(external_variant
(variant foo)
(implementation lib-foo)
(virtual_library vlib))
This will add lib-foo to the list of known implementations of vlib. For more details see Variants
Common items¶
Ordered set language¶
A few fields takes as argument an ordered set and can be specified using a small DSL.
This DSL is interpreted by dune into an ordered set of strings using the following rules:
:standard
denotes the standard value of the field when it is absentan atom not starting with a
:
is a singleton containing only this atoma list of sets is the concatenation of its inner sets
(<sets1> \ <sets2>)
is the set composed of elements of<sets1>
that do not appear in<sets2>
In addition, some fields support the inclusion of an external file using the
syntax (:include <filename>)
. This is useful for instance when you need to
run a script to figure out some compilation flags. <filename>
is expected to
contain a single S-expression and cannot contain (:include ...)
forms.
Note that inside an ordered set, the first element of a list cannot be an atom except if it starts with - or :. The reason for this is that we are planning to add simple programmatic features in the futures so that one may write:
(flags (if (>= %{ocaml_version} 4.06) ...))
This restriction will allow to add this feature without introducing a
breaking changes. If you want to write a list where the first element
doesn’t start by -, you can simply quote it: ("x" y z)
.
Most fields using the ordered set language also support Variables expansion. Variables are expanded after the set language is interpreted.
Boolean Language¶
The boolean language allows the user to define simple boolean expressions that dune can evaluate. Here’s a semi formal specification of the language:
op := '=' | '<' | '>' | '<>' | '>=' | '<='
expr := (and <expr>+)
| (or <expr>+)
| (<op> <template> <template>)
| <template>
After an expression is evaluated, it must be exactly the string true
or
false
to be considered as a boolean. Any other value will be treated as an
error.
Here’s a simple example of a condition that expresses running on OSX and having an flambda compiler with the help of variable expansion:
(and %{ocamlc-config:flambda} (= %{ocamlc-config:system} macosx))
Variables expansion¶
Some fields can contains variables of the form %{var}
that are
expanded by dune.
Dune supports the following variables:
project_root
is the root of the current project. It is typically the toplevel directory of your project and as long as you have adune-project
file there,project_root
is independent of the workspace configurationworkspace_root
is the root of the current workspace. Note that the value ofworkspace_root
is not constant and depends on whether your project is vendored or notCC
is the C compiler command line (list made of the compiler name followed by its flags) that was used to compile OCaml in the current build contextCXX
is the C++ compiler command line being used in the current build contextocaml_bin
is the path whereocamlc
livesocaml
is theocaml
binaryocamlc
is theocamlc
binaryocamlopt
is theocamlopt
binaryocaml_version
is the version of the compiler used in the current build contextocaml_where
is the output ofocamlc -where
arch_sixtyfour
istrue
if using a compiler targeting a 64 bit architecture andfalse
otherwisenull
is/dev/null
on Unix ornul
on Windowsext_obj
,ext_asm
,ext_lib
,ext_dll
andext_exe
are the file extension used for various artifactsocaml-config:v
for every variablev
in the output ofocamlc -config
. Note that dune processes the output ofocamlc -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 ofocamlc -config
. In particular, variables added in new versions of OCaml needs to be registered in dune before they can be usedprofile
the profile selected via--profile
context_name
the name of the context (default
or defined in the workspace file)os_type
is the type of the OS the build is targetting. This is the same asocaml-config:os_type
architecture
is the type of the architecture the build is targetting. This is the same asocaml-config:architecture
model
is the type of the cpu the build is targetting. This is the same asocaml-config:model
system
is the name of the OS the build is targetting. This is the same asocaml-config:system
ignoring_promoted_rule
istrue
if--ignore-promoted-rules
was passed on the command line andfalse
otherwise
In addition, (action ...)
fields support the following special variables:
target
expands to the one targettargets
expands to the list of targetdeps
expands to the list of dependencies^
expands to the list of dependencies, separated by spacesdep:<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 contextbin:<program>
expands to a path toprogram
. Ifprogram
is installed by a package in the workspace (see install stanzas), the locally built binary will be used, otherwise it will be searched in thePATH
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 ...)
lib:<public-library-name>:<file>
expands to a path to file<file>
of 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 installed world will be usedlibexec:<public-library-name>:<file>
is the same aslib:...
except when cross-compiling, in which case it will expand to the file from the host build contextlib-available:<library-name>
expands totrue
orfalse
depending on whether the library is available or not. A library is available iff at least one of the following condition holds:it is part the installed worlds
it is available locally and is not optional
it is available locally and all its library dependencies are available
version:<package>
expands to the version of the given package. Note that this is only supported for packages that are being defined in the current scoperead:<path>
expands to the contents of the given fileread-lines:<path>
expands to the list of lines in the given fileread-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:
List.iter (fun s -> print_string (String.escaped s)) l
Expansion of lists
Forms that expands to list of items, such as %{cc}
, %{deps}
,
%{targets}
or %{read-lines:...}
, are suitable to be used in, say,
(run <prog> <arguments>)
. For instance in:
(run foo %{deps})
if there are two dependencies a
and b
, the produced command
will be equivalent to the shell command:
$ foo "a" "b"
If you want the two dependencies to be passed as a single argument, you have to quote the variable as in:
(run foo "%{deps}")
which is equivalent to the following shell command:
$ foo "a b"
(the items of the list are concatenated with space).
Note that, since %{deps}
is a list of items, the first one may be
used as a program name, for instance:
(rule
(targets result.txt)
(deps foo.exe (glob_files *.txt))
(action (run %{deps})))
Here is another example:
(rule
(target foo.exe)
(deps foo.c)
(action (run %{cc} -o %{target} %{deps} -lfoolib)))
Library dependencies¶
Dependencies on libraries are specified using (libraries ...)
fields in
library
and executables
stanzas.
For libraries defined in the current scope, you can use either the real name or
the public name. For libraries that are part of the installed world, or for
libraries that are part of the current workspace but in another scope, you need
to use the public name. For instance: (libraries base re)
.
When resolving libraries, libraries that are part of the workspace are always preferred to ones that are part of the installed world.
Alternative dependencies¶
In addition to direct dependencies you can specify alternative dependencies. This is described in the Alternative dependencies section
It is sometimes the case that one wants to not depend on a specific library, but instead on whatever is already installed. For instance to use a different backend depending on the target.
Dune allows this by using a (select ... from ...)
form inside the list
of library dependencies.
Select forms are specified as follows:
(select <target-filename> from
(<literals> -> <filename>)
(<literals> -> <filename>)
...)
<literals>
are lists of literals, where each literal is one of:
<library-name>
, which will evaluate to true if<library-name>
is available, either in the workspace or in the installed world!<library-name>
, which will evaluate to true if<library-name>
is not available in the workspace or in the installed world
When evaluating a select form, dune will create <target-filename>
by
copying the file given by the first (<literals> -> <filename>)
case where
all the literals evaluate to true. It is an error if none of the clauses are
selectable. You can add a fallback by adding a clause of the form (->
<file>)
at the end of the list.
Preprocessing specification¶
Dune accepts three kinds of preprocessing:
no_preprocessing
, meaning that files are given as it to the compiler, this is the default(action <action>)
to preprocess files using the given action(pps <ppx-rewriters-and-flags>)
to preprocess files using the given list of ppx rewriters(staged_pps <ppx-rewriters-and-flags>)
is similar to(pps ...)
but behave slightly differently and is needed for certain ppx rewriters (see below for details)future_syntax
is a special value that brings some of the newer OCaml syntaxes to older compilers. See Future syntax for more details
Dune normally assumes that the compilation pipeline is sequenced as follow:
code generation (including preprocessing)
dependency analysis
compilation
Dune uses this fact to optimize the pipeline and in particular share the result of code generation and preprocessing between the dependency analysis and compilation phases. However, some specific code generators or preprocessors require feedback from the compilation phase. As a result they must be applied in stages as follows:
first stage of code generation
dependency analysis
second step of code generation in parallel with compilation
This is the case for ppx rewriters using the OCaml typer for
instance. When using such ppx rewriters, you must use staged_pps
instead of pps
in order to force Dune to use the second pipeline,
which is slower but necessary in this case.
Preprocessing with actions¶
<action>
uses the same DSL as described in the User actions
section, and for the same reason given in that section, it will be
executed from the root of the current build context. It is expected to
be an action that reads the file given as only dependency named
input-file
and outputs the preprocessed file on its standard output.
More precisely, (preprocess (action <action>))
acts as if
you had setup a rule for every file of the form:
(rule (target file.pp.ml) (deps file.ml) (action (with-stdout-to %{target} (chdir %{workspace_root} <action>))))
The equivalent of a -pp <command>
option passed to the OCaml compiler is
(system "<command> %{input-file}")
.
Preprocessing with ppx rewriters¶
<ppx-rewriters-and-flags>
is expected to be a sequence where each
element is either a command line flag if starting with a -
or the
name of a library. If you want to pass command line flags that do not
start with a -
, you can separate library names from flags using
--
. So for instance from the following preprocess
field:
(preprocess (pps ppx1 -foo ppx2 -- -bar 42))
The list of libraries will be ppx1
and ppx2
and the command line
arguments will be: -foo -bar 42
.
Libraries listed here should be libraries implementing an OCaml AST rewriter and registering themselves using the ocaml-migrate-parsetree.driver API.
Dune will build a single executable by linking all these libraries and their
dependencies. Note that it is important that all these libraries are linked with
-linkall
. Dune automatically uses -linkall
when the (kind ...)
field is set to ppx_rewriter
or ppx_deriver
.
Per module preprocessing specification¶
By default a preprocessing specification will apply to all modules in the library/set of executables. It is possible to select the preprocessing on a module-by-module basis by using the following syntax:
(preprocess (per_module (<spec1> <module-list1>) (<spec2> <module-list2>) ...))
Where <spec1>
, <spec2>
, … are preprocessing specifications
and <module-list1>
, <module-list2>
, … are list of module
names.
For instance:
(preprocess (per_module (((action (run ./pp.sh X=1 %{input-file})) foo bar)) (((action (run ./pp.sh X=2 %{input-file})) baz))))
Future syntax¶
The future_syntax
preprocessing specification is equivalent to
no_preprocessing
when using one of the most recent versions of the
compiler. When using an older one, it is a shim preprocessor that
backports some of the newer syntax elements. This allows you to use some of
the new OCaml features while keeping compatibility with older
compilers.
One example of supported syntax is the custom let-syntax that was introduced in 4.08, allowing the user to define custom let operators.
Dependency specification¶
Dependencies in dune
files can be specified using one of the following
syntax:
(:name <dependencies>)
will bind the the list of dependencies to thename
variable. This variable will be available as%{name}
in actions.(file <filename>)
or simply<filename>
: depend on this file(alias <alias-name>)
: depend on the construction of this alias, for instance:(alias src/runtest)
(alias_rec <alias-name>)
: depend 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)
, …(glob_files <glob>)
: depend on all files matched by<glob>
, see the glob for details
(source_tree <dir>)
: depend on all source files in the subtree with root<dir>
(universe)
: depend 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 installed world, you must still specify all dependencies that come from the workspace.(package <pkg>)
depend 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>)
: depend on the value of the environment variable<var>
. If this variable becomes set, becomes unset, or changes value, the target will be rebuilt.
In all these cases, the argument supports Variables expansion.
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:
(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 such named dependency list can also include unnamed
dependencies (like index.html
in the example above). Also, such
user defined names will shadow built in variables. So
(:workspace_root x)
will shadow the built in %{workspace_root}
variable.
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
.
Currently dune only support globbing files in a single directory. And in particular the glob is interpreted as follows:
anything before the last
/
is taken as a literal pathanything after the last
/
, or everything if the glob contains no/
, is interpreted using the glob syntax
The glob syntax is interpreted as follows:
\<char>
matches exactly<char>
, even if it is 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>
, …
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 bothocamlc
andocamlopt
(ocamlc_flags <flags>)
to specify flags passed toocamlc
only(ocamlopt_flags <flags>)
to specify flags passed toocamlopt
only
For all these fields, <flags>
is specified in the Ordered set language.
These fields all support (:include ...)
forms.
The default value for (flags ...)
is taken from the environment,
as a result it is recommended to write (flags ...)
fields as
follows:
(flags (:standard <my options>))
js_of_ocaml¶
A js_of_ocaml field exists in executable and libraries stanzas that allows one to customize options relevant to jsoo.
User actions¶
(action ...)
fields describe user actions.
User actions are always run from the same subdirectory of the current build
context as the dune file they are defined in. So for instance an action defined
in src/foo/dune
will be run from _build/<context>/src/foo
.
The argument of (action ...)
fields is a small DSL that is interpreted by
dune directly and doesn’t require an external shell. All atoms in the DSL
support Variables expansion. Moreover, you don’t need to specify dependencies
explicitly for the special %{<kind>:...}
forms, these are recognized and
automatically handled by dune.
The DSL is currently quite limited, so if you want to do something complicated it is recommended to write a small OCaml program and use the DSL to invoke it. You can use shexp to write portable scripts or configurator for configuration related tasks.
The following constructions are available:
(run <prog> <args>)
to execute a program.<prog>
is resolved locally if it is available in the current workspace, otherwise it is resolved using thePATH
(chdir <dir> <DSL>)
to change the current directory(setenv <var> <value> <DSL>)
to set an environment variable(with-<outputs>-to <file> <DSL>)
to redirect the output to a file, where<outputs>
is one of:stdout
,stderr
oroutputs
(for bothstdout
andstderr
)(ignore-<outputs> <DSL)
to ignore the output, where<outputs>
is one of:stdout
,stderr
oroutputs
(progn <DSL>...)
to execute several commands in sequence(echo <string>)
to output a string on stdout(write-file <file> <string>)
writes<string>
to<file>
(cat <file>)
to print the contents of a file to stdout(copy <src> <dst>)
to copy a file(copy# <src> <dst>)
to copy a file and add a line directive at the beginning(system <cmd>)
to execute a command using the system shell:sh
on Unix andcmd
on Windows(bash <cmd>)
to execute a command using/bin/bash
. This is obviously not very portable(diff <file1> <file2>)
is similar to(run diff <file1> <file2>)
but is better and allows promotion. See Diffing and promotion for more details(diff? <file1> <file2>)
is the same as(diff <file1> <file2>)
except that it is ignored when<file1>
or<file2>
doesn’t exists(cmp <file1> <file2>)
is similar to(run cmp <file1> <file2>)
but allows promotion. See Diffing and promotion for more details
As mentioned copy#
inserts a line directive at the beginning of
the destination file. More precisely, it inserts the following line:
# 1 "<source file name>"
Most languages recognize such lines and update their current location,
in order to report errors in the original file rather than the
copy. This is important as the copy exists only under the _build
directory and in order for editors to jump to errors when parsing the
output of the build system, errors must point to files that exist in
the source tree. In the beta versions of dune, copy#
was
called copy-and-add-line-directive
. However, most of time one
wants this behavior rather than a bare copy, so it was renamed to
something shorter.
Note: expansion of the special %{<kind>:...}
is done relative to the current
working directory of the part of the DSL being executed. So for instance if you
have this action in a src/foo/dune
:
(action (chdir ../../.. (echo %{path:dune})))
Then %{path:dune}
will expand to src/foo/dune
. When you run various
tools, they often use the filename given on the command line in error messages.
As a result, if you execute the command from the original directory, it will
only see the basename.
To understand why this is important, let’s consider this dune file living in
src/foo
:
(rule
(target blah.ml)
(deps blah.mll)
(action (run ocamllex -o %{target} %{deps})))
Here the command that will be executed is:
ocamllex -o blah.ml blah.mll
And it will be executed in _build/<context>/src/foo
. As a result, if there
is an error in the generated blah.ml
file it will be reported as:
File "blah.ml", line 42, characters 5-10:
Error: ...
Which can be a problem as you editor might think that blah.ml
is at the root
of your project. What you should write instead is:
(rule
(target blah.ml)
(deps blah.mll)
(action (chdir %{workspace_root} (run ocamllex -o %{target} %{deps}))))
Locks¶
Given two rules that are independent, dune will assume that there associated action can be run concurrently. Two rules are considered independent if none of them depend on the other, either directly or through a chain of dependencies. This basic assumption allows 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:
(alias
(name runtest)
(deps foo)
(locks m)
(action (run test.exe %{deps})))
(alias
(name 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:
(alias
(name runtest)
(deps foo)
(locks /tcp-port/1042)
(action (run test.exe %{deps})))
Diffing and promotion¶
(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 doesn’t nothingwhen they are not, the differences are shown and the action fails
However, it is different for the following reason:
the exact command used to diff files can be configured via the
--diff-command
command line argument. Note that it is only called when the files are not byte equalsby default, it will use
patdiff
if it is installed.patdiff
is a better diffing program. You can install it via opam with:$ opam install patdiff
on Windows, both
(diff a b)
and(diff? a b)
normalize the end of lines before comparing the filessince
(diff a b)
is a builtin action, dune knowns thata
andb
are needed and so you don’t need to specify them explicitly as dependenciesyou can use
(diff? a b)
after a command that might or might not produceb
. For cases where commands optionally produce a corrected fileit allows promotion. See below
Note that (cmp a b)
does no end of lines normalization and doesn’t
print a diff when the files differ. cmp
is meant to be used with
binary files.
Promotion¶
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:
(rule
(with-stdout-to data.out (run ./test.exe)))
(alias
(name 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 printedcheck the diff to make sure it is what you expect
run
dune promote
. This will copy the generateddata.out
file todata.expected
directly in the source tree
You can also use dune runtest --auto-promote
which will
automatically do the promotion.
OCaml syntax¶
If a dune
file starts with (* -*- tuareg -*- *)
, then it is
interpreted as an OCaml script that generates the dune
file as described
in the rest of this section. The code in the script will have access to a
Jbuild_plugin
module containing details about the build context it is executed in.
The OCaml syntax gives you an escape hatch for when the S-expression
syntax is not enough. It is not clear whether the OCaml syntax will be
supported in the long term as it doesn’t work well with incremental
builds. It is possible that it will be replaced by just an include
stanza where one can include a generated file.
Consequently you must not build complex systems based on it.