opam 2.0 tips
This blog post looks back on some of the improvements in opam 2.0, and gives tips on the new workflows available.
Package development environment management
Opam 2.0 has been vastly improved to handle locally defined packages. Assuming
you have a project
~/projects/foo, defining two packages
foo-bin, you would have:
~/projects/foo |-- foo-lib.opam |-- foo-bin.opam `-- src/ ...
(See also about computed dependency constraints for handling multiple package definitions with mutual constraints)
The underlying mechanism is the same, but this is an interface improvement that
replaces most of the opam 1.2 workflows based on
The usual commands (
remove, etc.) have been extended to
support specifying a directory as argument. So when working on project
cd ~/projects/foo opam install .
foo-bin will get automatically pinned to the current
directory (using git if your project is versioned), and installed. You may
prefer to use:
opam install . --deps-only
to just get the package dependencies ready before you start hacking on it.
See below for details on how to reproduce a
build environment more precisely. Note that
opam depext . will not work at the
moment, which will be fixed in the next release when the external dependency
handling is integrated (opam will still list you the proper packages to install
for your OS upon failure).
If your project is versioned and you made changes, remember to either commit, or
--working-dir so that your uncommitted changes are taken into account.
Opam 2.0 introduced a new feature called "local switches". This section explains what it is about, why, when and how to use them.
Opam switches allow to maintain several separate development environments, each with its own set of packages installed. This is particularly useful when you need different OCaml versions, or for working on projects with different dependency sets.
It can sometimes become tedious, though, to manage, or remember what switch to use with what project. Here is where "local switches" come in handy.
How local switches are handled
A local switch is simply stored inside a
_opam/ directory, and will be
selected automatically by opam whenever your current directory is below its
NOTE: it's highly recommended that you enable the new shell hooks when using local switches. Just run
opam init --enable-shell-hook: this will make sure your PATH is always set for the proper switch.
You will otherwise need to keep remembering to run
eval $(opam env)every time you
cdto a directory containing a local switch. See also how to display the current switch in your prompt
For example, if you have
~/projects/foo/_opam, the switch will be selected
whenever in project
foo, allowing you to tailor what it has installed for the
needs of your project.
If you remove the switch dir, or your whole project, opam will forget about it transparently. Be careful not to move it around, though, as some packages still contain hardcoded paths and don't handle relocation well (we're working on that).
Creating a local switch
This can generally start with:
cd ~/projects/foo opam switch create . --deps-only
Local switch handles are just their path, instead of a raw name. Additionally,
the above will detect package definitions present in
~/projects/foo, pick a
compatible version of OCaml (if you didn't explicitely mention any), and
automatically install all the local package dependencies.
--deps-only, the packages themselves would also get installed in the
Using an existing switch
If you just want an already existing switch to be selected automatically,
without recompiling one for each project, you can use
opam switch link:
cd ~/projects/bar opam switch link 4.07.1
will make sure that switch
4.07.1 is chosen whenever you are in project
You could even link to
../foo here, to share
foo's local switch between the
Reproducing build environments
If your package depends on development versions of some dependencies (e.g. you had to push a fix upstream), add to your opam file:
depends: [ "some-package" ] # Remember that pin-depends are depends too pin-depends: [ [ "some-package.version" "git+https://gitfoo.com/blob.git#mybranch" ] ]
This will have no effect when your package is published in a repository, but
when it gets pinned to its dev version, opam will first make sure to pin
some-package to the given URL.
Dependency contraints are sometimes too wide, and you don't want to explore all the versions of your dependencies while developing. For this reason, you may want to reproduce a known-working set of dependencies. If you use:
opam lock .
opam will check what version of the dependencies are installed in your current
switch, and explicit them in
opam lock is a plugin at
the moment, but will get automatically installed when needed.
Then, assuming you checked these files into version control, any user can do
opam install . --deps-only --locked
to instruct opam to reproduce the same build environment (the
is also available to
opam switch create, to make things easier).
The generated lock-files will also contain added constraints to reproduce the
presence/absence of optional dependencies, and reproduce the appropriate
dependency pins using
pin-depends. Add the
--direct-only option if you don't
want to enforce the versions of all recursive dependencies, but only direct
OCamlPro is a R&D lab founded in 2011, with the mission to help industrial users benefit from state-of-the art programming languages like OCaml and Rust.
We design, create and implement custom ad-hoc software for our clients. We also have a long experience in developing and maintaining open-source tooling for OCaml, such as Opam, TryOCaml, ocp-indent, ocp-index and ocp-browser, and we contribute to the core-development of OCaml, notably with our work on the Flambda optimizer branch.
Another area of expertise is that of Formal Methods, with tools such as our SMT Solver Alt-Ergo (check our Alt-Ergo Users'). We also provide vocational trainings in OCaml and Rust, and we can build courses on formal methods on-demand. Please reach out, we'll be delighted to discuss your challenges: firstname.lastname@example.org or book a quick discussion.