Introduction to opam for npm/yarn users
Getting Started
opam is the main package manager for OCaml. This is the equivalent of npm or yarn. opam is a command line tool to manipulate packages that are defined in opam files. Most of the opam packages are published on the opam repository which is the equivalent of the npm registry.
This document presents the corresponding opam commands, files and configurations for the most common npm idioms.
opam is now available on version 2. If you didn't upgrade yet, you better hurry.
There is a summary at the end of the page, containing all the most common commands to know. And the the official opam introduction contains a lot of information: https://opam.ocaml.org/doc/2.0/Usage.html
Initial configuration
Installation
First thing is to install opam, as one would install npm. There is an official documentation page to install opam. Most of the time, we can simply get it from a package manager. Otherwise, binaries are provided for every platform.
Initialization
npm and yarn don't need any initialization after they are installed, even if it is possible to customize a few settings. But for opam there is a necessary step.
opam init -a
Let's try to explain more in details what it does.
I am quoting the documentation of opam init
itself here, as it
explains clearly what is done.
The init command initialises a local "opam root" (by default, ~/.opam/) that holds opam's data and packages. This is a necessary step for normal operation of opam. The initial software repositories are fetched, and an initial 'switch' can also be installed, according to the configuration and options. These can be afterwards configured using opam switch and opam repository.
[…]
Additionally, this command allows to customise some aspects of opam's shell integration, when run initially (avoiding the interactive dialog), but also at any later time.
The interesting parts are:
- the opam root is
~/.opam
- opam uses a shell integration to make our life easier
- opam uses the concept of switch
A switch is the equivalent of the node_modules
folder in npm's
world. It contains all the packages that are installed. One difference
with npm is that we can have multiple global switches. As if we could
have different yarn global
projects. It can be handy sometimes, but
to avoid confusion I recommend to avoid using global switches.
Default settings can be changed if the -a
option is omitted while
calling opam init
.
Minimal package.json
The equivalent of package.json
is a app.opam
file. The part before
.opam
is the name of the package. So app.opam
could be
webpack.opam
. It is possible to have multiple opam files in the same
directory.
There is no opam command to manipulate the opam file. Things like npm
init
or yarn add
will have to be done by hand.
A minimal opam file looks like this:
opam-version: "2.0" name: "my-app" authors: "Louis" homepage: "https://github.com/khady/example" maintainer: "Louis <ex@ample.com>" dev-repo: "git+ssh://git@github.com:khady/example.git" bug-reports: "https://github.com/khady/example/issues" version: "0.1" build: [ [ "dune" "subst" ] {pinned} [ "dune" "build" "-p" name "-j" jobs ] ] depends: [ "dune" {build} "opam-lock" {dev} ]
{build}
tells opam that dune
is needed only to build to
project. {dev}
is to mark dev dependencies. There is another way to
handle dev dependencies for advance usage that I covered in this post.
npm/yarn commands
npm install / yarn
npm install
or yarn
commands cover multiple opam commands,
depending on the context.
First case is in absence of local switch in the current project. It
can be verified by looking for a _opam
directory at the root of the
project. It corresponds to a npm project without a node_modules
directory.
In this first case, we need to initialize a local switch.
opam switch create . 4.07.0 --deps-only
The second case is in presence of a local switch.
opam install . --deps-only
npm install <pkg> / yarn add
To install a package with opam is easy:
opam install PACKAGE
But opam does not modify the app.opam
file during the
installation. It has to be done by hand. This is as simple as adding
the name of the package in the depends
field.
npm link / yarn link
npm link
in the opam world is opam pin
. Its usage is well
described in the official documentation. You can find it there:
npm upgrade / yarn upgrade
In this case, there is a direct equivalent in opam. And it is easy to remember.
opam upgrade PACKAGE
opam upgrade
is also able to upgrade all the packages of the local
switch if no package name is given.
There is one big difference with npm though. opam stores a local copy
of the opam repository. Like apt-get
does in Debian. So if we often
want to update this copy before to request an upgrade.
opam update && opam upgrade PACKAGE
Extra package.json concepts
dev dependencies
There is no strict equivalent of the dev-dependencies
field of
package.json
. But it is possible to achieve the same thing in two
ways.
- By marking with
{dev}
the package independs
- By using a
dev.opam
file, as I described there.
lock files
Lock files are also not common yet in the opam world. But I talked about their usage on this page. It can be summarize to:
- Using
opam lock
to generate the lock file when needed (basically after eachopam install
oropam upgrade
). - Adding
--locked
to all theopam install . --deps-only
andopam switch create .
commands.
scripts
This one doesn't exist in the opam world. The closer we get is the
equivalent of yarn exec
. Which is opam exec
.
Instead of writing a package.json
with a script field like this:
"scripts": { "release": "make release" }
We run opam exec
like this:
opam exec -- make release
Libraries
In javascript packages published on npm, the convention is to have one library per package. This is not the case in opam. One opam package can provide multiple libraries. And there is a tool called ocamlfind that is used to get the information related to all those libraries. When using a modern build system like dune, there will be almost no reason to use ocamlfind by hand.
There is no official way that I know off to get the list of all the libraries installed by a package. The easiest way to get a good approximation is something like this:
cat $(opam var re:lib)/META | grep package
Which tells you than the package re
comes with the libraries re
,
re.emacs
, re.glob
, re.pcre
, re.perl
, re.posix
and re.str
.
Summary
Installation
- Follow https://opam.ocaml.org/doc/2.0/Install.html
- run
opam init -a
Usage
Starting a new project
This section does the same as: yarn init
.
Create app.opam
file with this content:
opam-version: "2.0" name: "my-app" authors: "Louis" homepage: "https://github.com/khady/example" maintainer: "ex@ample.com" dev-repo: "git+ssh://git@github.com:khady/example.git" bug-reports: "https://github.com/khady/example/issues" version: "0.1" build: [ [ "dune" "subst" ] {pinned} [ "dune" "build" "-p" name "-j" jobs ] ] depends: [ "dune" {build} "opam-lock" {dev} ]
opam switch create . 4.07.0 --deps-only
Updating the list of packages available
Opam is like apt-get
, it requires the execution of a command to get
the list of new packages available.
opam update
Adding a dependency
This section does the same as: yarn add
.
- Add a line in the
depends
field ofapp.opam
:
depends: [ "dune" {build} "owl" ]
- Install the new dependency:
opam install . --deps-only
Upgrading a dependency
This section does the same as: yarn upgrade
.
opam upgrade [package]
Removing a dependency
This section does the same as: yarn remove
.
- Execute
opam remove
to uninstall the package:
opam remove owl
- Remove the corresponding line from the
depends
field inapp.opam
Installing all the dependencies of project
This section does the same as: yarn install
.
If there is a _opam
folder (a local switch) in the project:
opam install . --deps-only
Otherwise:
opam switch create . 4.07.0 --deps-only