OPAM 1.1 is no longer supported. Documentation for OPAM 1.2 is available
here.

Creating OPAM packages

In this tutorial, you will learn how to package an OCaml library or
software for OPAM. The first section will introduce you the ounit
OPAM package. Albeit simple, it is a real example of what an OPAM
package is, and actually most packages in the OPAM repository are that
simple. The second section will be a comprehensive guide illustrated
by more complicated examples of real packaging cases.

An important thing to keep in mind is that OPAM is (at least for now)
only a package manager, as opposed to — for example — the
oasis/odb suite, which includes a build system (oasis) as well
as a package manager (odb). This means that OPAM cannot help you to
actually build your ocaml software or library, to do so, you need to
use dedicated tools such as ocamlbuild, oasis, omake and
others. OPAM packages are just built the same way you would build the
software yourself (with shell commands).

The ounit OPAM package

Let get started by learning from the ounit OPAM package. This is a
widely used OCaml library to create unit tests for OCaml projects, and
is a perfect example of a minimalistic yet complete package.

By reading about ounit on the upstream website,
you learn that it is a library to create unit tests, that its latest version
is 1.1.2, and that it depends on ocamlfind.
You know the URL you can download its source tarball, and you must compute the md5sum of
this source tarball by running md5sum ounit-1.1.2.tar.gz (or, on some operating systems, md5 instead of md5sum).

You also learn that to build and install it, you have to:

make build
make install

And that’s all the information you need to build an OPAM package.

A minimum OPAM package is a directory containing three files: descr, opam, and url.
The name of the directory defines the package name and version: <package-name>.<package-version>.
In our case, the directory will be ounit.1.1.2 and contain the following files:

Notes

descr

This file is pure text. Its first line will be used as a short
description of the package, it is what is displayed for each package
when you do opam list. The whole text contained in there is
displayed when you do opam search <package>. Therefore you should
put a short meaningful description on the first line, and a long
description starting from the second line.

opam

The full ABNF specification of the syntax for opam files is
available in OPAM
developer manual.
In this file,
opam-version MUST be 1, and you should put your email in the
maintainer field. build has OCaml type string list list, and
contains the build instructions. Here,

make build
make install

gets translated into

build: [
[make "build"]
[make "install"]
]

You should adapt this to the required commands to build your package,
and each line contains the shell commands corresponding to a
string list. Note that make is a special variable which will be
automatically translated to either make on linux and OSX or gmake
on BSD systems.

The remove field follows the same syntax as the build field. The
depends field is a string list of dependencies, with each dependency
being another OPAM package. Here ounit depends only on ocamlfind.

url

This file contains at least one archive line containing the URL of
the source package, and optionally a checksum line that must contain
the MD5 sum of the source package if it is present. It is good practice
to systematically add a checksum line to your packages, unless the
source package has no fixed version (the typical example being a
source package hosted on github with no tags). This checksum will be
checked when creating and installing the package.

The URL can also contain a single git or darcs field instead of archive,
which points to GIT or DARCS repository URL. This will be checked out and
updated every time opam update is run, which is useful for development
packages.

Testing custom OPAM packages

The easiest way to test your new packages is to set-up a local
repository for testing purposes.

$ mkdir -p /tmp/testing
$ opam repo add testing /tmp/testing

These commands add a new (currently empty) repository named
testing (you can pick an other name if your prefer) which will
contain what is in /tmp/testing (Remark: you can also clone the
official git repository if you don't want to start from a fresh one,
this will work as well).

You can now check that this new repository exists:

$ opam repo # eq. to 'opam repo list'

This command displays the list of repositories, with testing
having the highest priority (you can use opam repo priority to
change the relative repository priorities later).

Now it is time to populate /tmp/testing/packages with your new package
files. For instance, if you want to test the version 1.1.3 of ounit,
you have to create /tmp/testing/packages/ounit.1.1.3/{opam,descr,url}
following the guidelines defined above.

To take this changes into account, update your testing repository:

$ opam update testing

If everything is fine, OPAM should tell you than a new version of ounit is
available. If this is the case, you can install it by doing:

$ opam install ounit.1.1.3

Remark: you can use opam-admin to simulate the creation of OPAM
package archives done on opam.ocaml.org:

$ cd /tmp/testing && opam-admin check && opam-admin make -g ounit

This command will:

Check that your metadata are well-formed.

Download the upstream archive, and generate the correct checksum
(because of `-g);

Create the archive archives/ounit.1.1.3+opam.tar.gz containing
the content of the upstream archive + the files in
packages/ounit/ounit.1.1.3/files/

Create urls.txt and index.tar.gz at the root of your repository,
which will let you host it as an HTTP remote.

If archives/ounit.1.1.3+opam.tar.gz exists, OPAM will use it
directly instead of downloading the archive upstream.

If (i) the basic installation and (ii) the archive creation work, you
are in good shape to submit your new package upstream (see below).

Advanced OPAM packaging guide

This section will be as comprehensive as possible on the art of
creating OPAM packages, but in case of ambiguities, the ABNF syntax
documentation has priority.

Since everything has already be said about the descr file and almost
everything about the url file, this section is mostly about the
opam files.

OPAM variables

OPAM maintains a set of variables (key value pairs) that can be used
in opam files and that will be substituted by their values on
package creation. The list of variables that can be used in opam
files can be displayed by doing opam config list. The
following example shows the build section of package ocamlnet that
use the variable bin:

In this case, bin will be substituted by the value of the bin
variable. In case you need to substitue a substring, you can use
"--bindir=%{bin}%": here %{bin}% will be substituted by the
value of bin.

Optional dependencies

We mentioned the ability to specify optional dependencies for packages.
If a package has optional dependencies, they will not be installed automatically,
but will be taken into account if they are present before the installation.
If the optional dependency is not present, but are subsequently installed,
then the depending package will also be recompiled to take advantage of the newly
installed library.

Let us see how it works via the following example, which shows the opam file of the lwt package:

Notice the new depopts field, which contains the list of
optional dependencies, specified in the same format as the
depends field.

Also notice a new syntax for substitutions of the form
%{<package>:enable}%. If package is
installed, this pattern will be replaced by enable, otherwise by
disable. This ease the building of lines of type ./configure
--enable-<feature1> --disable-<feature2>.

Version constraints

If a package depends (respectively optionally depends) on a specific
version of a package, this can be specified by using the following
syntax for the field depends (respectively depopts) of the opam
file:

The above example shows two fields of the opam file of package
cohttp, that optionally depends of the version 108.00.02 of package
async.

The OPAM specification document specifies the format used by the
depends and depopts fields. As there is much more to say about
version constraints, you should read it to learn how to write very
fine grained version constraints.

Manually installing binaries or libaries

Most of the time, when a package is built, binaries and/or libraries
that it provides are installed by the package's build
commands. However, for various reasons, sometimes a source package's
build commands do not allow you to install all the files you would
like or do not install them with the name or path you would like. Opam
allows you to control installed files precisely by providing a
.install file. For example, the package xmlm has this following
opam file:

This has the semantics: “install the file of path
_build/test/xmltrip.native relative to the root of the source
package into the directory returned by the command opam config var
bin under the name xmltrip”. If the source filename starts by ?,
the installation will not fail if the file is not present.

Thus, this additional file gets installed and removed by opam, in
addition to those installed by the ocaml setup.ml -install and
removed by the ocamlfind remove xmlm commands.

The bin section installs files in opam's bin directory. You can
also define a list of files to be installed in the sections: lib,
toplevel, share, doc, misc, stublibs, and man. For
example, to install additional library files, you can have the
section:

lib: [ "META" "lib/foo.cmi" "lib/foo.cmo" "lib/foo.cmx" ]

Note the destination name in curly braces can be omitted if no
modification to the filename is necessary.

Ideally, the .install file should be dynamically created by the
package build system at the root of the project and should be named
$(OPAM_PACKAGE_NAME).install ($OPAM_PACKAGE_NAME is automatically
set by OPAM). If it comprehensively lists every file that should be
installed, then the build section of the package's opam file
should exclude a make install (or equivalent) command, and the
remove section should be omitted.

Aside from dynamically generating this file, it is also possible to
include a static version in a package under the files sub-directory,
as is the case for the xmlm example above. This is less ideal than
dynamically generating it, but is supported because package developers
may find it easier to generate this file manually than figuring out
how to dynamically generate it with their build system.

For comprehensive information about this facility refer to the Section 1.2.5 of OPAM
developer manual

Compiler version constraints

Some packages require a specific OCaml version to work and thus can
only be installed under specific compiler versions. To specify such a
constraint, you can add a field ocaml-version: [ <
"4.00.0" ] (or available: [ ocaml-version < "4.00.0" ] for OPAM 1.1 on) to the opam file. This particular constraint implies
that the package cannot be built or installed under OCaml 4.00.0 or
later.

Patching sources

You can instruct OPAM to apply patches to the source code before building a package. To do so, you have to add a patches field to the opam file, the syntax being of the form patches: ["bugfix1.patch" "bugfix2.patch"], where bugfix1.patch and bugfix2.patch are two files existing in the directory files. Before building such a package, OPAM will substitute any opam variable (of the form %{variable}%) to their respective values and apply the resulting patches to the source code. Only then will the package be built. For more information, please look at packages including patches, such as dbm.1.0.

Git / Darcs packages

It is possible to use a git repository instead of an archive file in
url files. To do so, you need to use the following syntax:

git: "<url>"

<url> being any url that git knows how to clone. For git packages,
OPAM has the following behaviour:

When installing a git package, OPAM will use git to clone its url
and use it as the package source

When updating packages, OPAM will do a git fetch in order to have
the last patches available for git packages

When upgrading packages, OPAM will merge the last changes before
rebuilding and upgrading the packages

You can specify a branch other than the default by suffixing the
url with #<branch>, eg. git: git@github.com:me/project.git#next

If you host your project on Github, you may
instead use github’s functionality to create a tarball from a git
repository. It is generally available at
https://github.com/<your-id>/<your-project>/tarball/<branch-or-tag>. You
can use this url to create “normal” — non-git packages from git
repositories hosted on github.

Note that git packages will normally never be included in the default
OPAM repository, and are mainly an aid for developers who use OPAM in
their development process. If you plan to do that, please have a look
at the Developing with OPAM
tutorial.

Darcs packages

Darcs repositories are supported as well. OPAM behaves the
same way it does with git repositories, as described above. You just need to
specify the repository url using the following syntax in url files:

darcs: "<url>"

Where to go from here

Although this tutorial covered most packaging cases, there are still
packages that requires more tuning that what have been described
above. If you find yourself stuck trying to package a software or a
library, please read the OPAM
developer manual
(you will find it in the
doc directory in the OPAM tarball) and/or read existing OPAM
package descriptions for inspiration.

Including packages to the official OPAM repository

This section will help you getting started with the process of
submitting packages to the official OPAM repository. This repository
is available at url [http://opam.ocaml.org], but its content is
generated from a git
repository hosted on
github.

To submit a package for inclusion in the official repository, all you
have to do is to fork opam-repository on github, commit a patch
containing the package(s) you want to include, and open a pull request
for it.

If the above sentence makes no sense for you, you probably don’t know
about either git or github (or both). git is a distributed
version control system, very popular at the time we write those
lines. Getting started with git is definitely a topic outside the
scope of this short tutorial, but you can read about it on git’s
documentation page.

Github is a web frontend to git. It allows users to store public git
repositories, and provides additional convenient features over git
such as “pull
requests” that
automatise the process of sharing patches with others. You can learn
how to use it here.

If you cannot (or do not want to) use github but still want to
contribute to packages, you can try sending a mail to
contact@ocamlpro.com with your patches to opam-repository. As this
method requires manual intervention from our not very big OPAM team,
it should only be used if the first method is not an option for
you. It might as well take more time for your request to be processed
in this case.