OPAM integration

opam is the official package manager for OCaml, and dune offers some integration with it.

Invocation from opam

You should set the build: field of your <package>.opam file as follows:

build: [
  ["dune" "subst"] {pinned}
  ["dune" "build" "-p" name "-j" jobs]
]

-p pkg is a shorthand for --root . --only-packages pkg --profile release --default-target @install. -p is the short version of --for-release-of-packages.

This has the following effects:

  • it tells dune to build everything that is installable and to ignore packages other than name defined in your project
  • it sets the root to prevent dune from looking it up
  • it silently ignores all rules with (mode promote)
  • it sets the build profile to release
  • it uses whatever concurrency option opam provides
  • it sets the default target to @install rather than @@default

Note that name and jobs are variables expanded by opam. name expands to the package name and jobs to the number of jobs available to build the package.

Tests

To setup the building and running of tests in opam, add this line to your <package>.opam file:

build: [
  (* Previous lines here... *)
  ["dune" "runtest" "-p" name "-j" jobs] {with-test}
]

<package>.opam files

When a <package>.opam file is present, dune will know that the package named <package> exists. It will know how to construct a <package>.install file in the same directory to handle installation via opam. Dune also defines the recursive install alias, which depends on all the buildable <package>.install files in the workspace. So for instance to build everything that is installable in a workspace, run at the root:

$ dune build @install

Declaring a package this way will allow you to add elements such as libraries, executables, documentation, … to your package by declaring them in dune files.

Such elements can only be declared in the scope defined by the corresponding <package>.opam file. Typically, your <package>.opam files should be at the root of your project, since this is where opam pin ... will look for them.

Note that <package> must be non-empty, so in particular .opam files are ignored.

Generating opam files

Here’s a complete example of a dune file with opam metadata specification:

(lang dune 1.10)
(name cohttp)
(source (github mirage/ocaml-cohttp))
(license ISC)
(authors "Anil Madhavapeddy" "Rudi Grinberg")
(maintainers "team@mirage.org")

(package
 (name cohttp)
 (synopsis "An OCaml library for HTTP clients and servers")
 (description "A longer description")
 (depends
  (alcotest :with-test)
  (dune (> 1.5))
  (foo (and :dev (> 1.5) (< 2.0)))
  (uri (>= 1.9.0))
  (uri (< 2.0.0))
  (fieldslib (> v0.12))
  (fieldslib (< v0.13))))

(package
 (name cohttp-async)
 (synopsis "HTTP client and server for the Async library")
 (description "A _really_ long description")
 (depends
  (cohttp (>= 1.0.2))
  (conduit-async (>= 1.0.3))
  (async (>= v0.10.0))
  (async (< v0.12))))

Opam template

A user may want to manually fill in some field in the opam file. In these situations, dune provides an escape hatch in the form of a user written opam template. An opam template must be named <package>.opam.template and should be a syntactically valid opam file. Any field defined in this template file will be taken as is by dune and never overwritten.

Note the template file cannot be generated by a rule and must be available in the source tree.

Odig conventions

Dune follows the odig conventions and automatically installs any README*, CHANGE*, HISTORY* and LICENSE* files in the same directory as the <package>.opam file to a location where odig will find them.

Note that this includes files present in the source tree as well as generated files. So for instance a changelog generated by a user rule will be automatically installed as well.