Skip to content

Commit

Permalink
doc: dune pkg tutorial (#10920)
Browse files Browse the repository at this point in the history
Signed-off-by: Marek Kubica <marek@tarides.com>
  • Loading branch information
Leonidas-from-XIV authored Sep 23, 2024
1 parent dedfb76 commit d170703
Show file tree
Hide file tree
Showing 15 changed files with 437 additions and 0 deletions.
140 changes: 140 additions & 0 deletions doc/tutorials/dune-package-management/dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Managing Dependencies

The OCaml ecosystem has a wealth of third-party packages that are available for
use. In this section we will look into how to use them with Dune.

## Adding Dependencies

Much like in regular projects, to add a library we need to add a dependency to
it. For simplicity we will use the popular `fmt` library as an example, but any
package from the [package repository](https://ocaml.org/packages) can be used.

First we update the `dune-project` file to add a dependeny on the opam package.

::::{dropdown} `dune-project`
:icon: file-code

:::{literalinclude} dependencies/dune-project
:language: dune
:emphasize-lines: 8
:::

::::

After this change to our project dependencies, we need to relock dependencies
to update our lock directory with the new packages.

```sh
$ dune pkg lock
Solution for dune.lock:
- base-unix.base
- fmt.0.9.0
- ocaml.5.2.0
- ocaml-base-compiler.5.2.0
- ocaml-config.3
- ocamlbuild.0.15.0+dune
- ocamlfind.1.9.6+dune
- topkg.1.0.7
```

You can see a lot of new dependencies, among these `fmt`.

:::{note}
The list of packages being output includes all dependencies of your project,
including transitive dependencies.
:::

This will take care of installing the dependencies, but we still need to add it to
our build as a library, as usual:

::::{dropdown} `dune`
:icon: file-code

:::{literalinclude} dependencies/dune
:language: dune
:emphasize-lines: 3
:::

Adding a library dependency to our `dune` file via the `libraries` stanza.

::::

This will allow us to use the `Fmt` module in our OCaml code.

::::{dropdown} `test.ml`
:icon: file-code

:::{literalinclude} dependencies/test.ml
:language: ocaml
:emphasize-lines: 4
:::

We update the code to define an `Fmt.t` pretty-printer for the list of strings
and then use it to print the value.

::::

To build it we just call `build` again.

```sh
$ dune build
```

which will download and install the new dependencies and build our project as
before.

As we see, the code works and uses `fmt` to do the pretty-printing:

```sh
$ dune exec ./test.exe
Hello, OCaml, Rust!
```

### Dependency Constraints

Packages are often only compatible with some versions of dependencies. To
specify a version range, use the regular Dune dependency syntax
used for opam dependencies in the `dune-project` file.

::::{dropdown} `dune-project`
:icon: file-code

:::{literalinclude} dependencies/constraints
:language: dune
:emphasize-lines: 7-8
:::

::::

This ensures the `fmt` package to install will be compatible with
our request. These constraints will be taken into account the next time the
package is locked:

```sh
$ dune pkg lock
Solution for dune.lock:
- base-unix.base
- fmt.0.9.0
- ocaml.5.2.0
- ocaml-base-compiler.5.2.0
- ocaml-config.3
- ocamlbuild.0.15.0+dune
- ocamlfind.1.9.6+dune
- topkg.1.0.7
```

The version of `fmt` picked is indeed between `0.6` and `1.0`.

## Removing Dependencies

Given all dependencies are defined in the `dune-project` file, removing a
dependency means to remove the dependency from the `depends` field of your
`dune-project` and relocking the project.

The new lock directory will not depend on the package anymore, and in future
builds, the package will not be accessible as `library` anymore.

:::{note}
The removed dependency might still be part of the lock directory if some other
dependency of your project depends on it.
:::
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(lang dune 3.17)
(name test)

(package
(name test)
(depends
(ocaml (>= 4.14))
(fmt (and (>= 0.6) (< 1.0)))))
3 changes: 3 additions & 0 deletions doc/tutorials/dune-package-management/dependencies/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(executable
(public_name test)
(libraries fmt))
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(lang dune 3.17)
(name test)

(package
(name test)
(depends
(ocaml (>= 4.14))
fmt))
5 changes: 5 additions & 0 deletions doc/tutorials/dune-package-management/dependencies/test.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let langs = ["OCaml"; "Rust"]

let () =
let pp_langs = Fmt.(list ~sep:(any ", ") string) in
Format.printf "Hello, %a!\n" pp_langs langs
27 changes: 27 additions & 0 deletions doc/tutorials/dune-package-management/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
author: Marek Kubica
---

OCaml Package Management With Dune
==================================

:::{warning}
Dune Package Management is not final yet and details are still subject to
change.
:::

In this tutorial we will be looking at how to use Dune for managing project
dependencies. This enables users to install the compiler as well as third-party
dependencies using a single tool which takes care of building code and
dependencies.

To get started you only need Dune. Head to {doc}`setup` to begin the setup.

:::{toctree}
:hidden:
:maxdepth: 1
setup
dependencies
pinning
repos
:::
50 changes: 50 additions & 0 deletions doc/tutorials/dune-package-management/pinning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Pinning Projects

When Dune is looking up packages to lock, it uses the (pre)configured OCaml
package repositories. However it is also possible to manually specify the
sources for packages; for example, if the package is not released in a package
repository. This is called "pinning."

## Installing Packages From a Pin

To pin a package, a new `pin` has to be declared in the `dune-project` file.

::::{dropdown} `dune-project`
:icon: file-code

:::{literalinclude} pinning/dune-project
:language: dune
:emphasize-lines: 4-6
:::

This will create a pin on the `fmt` package and use the specified Git repository
URL to retrieve the sources.

::::

The next time the package is locked, Dune will use this repository instead of
the information from the selected package repositories.

```sh
$ dune pkg lock
Solution for dune.lock:
- base-unix.base
- fmt.dev
- ocaml.5.0.0
- ocaml-base-compiler.5.0.0
- ocaml-config.3
- ocamlbuild.0.15.0+dune
- ocamlfind.1.9.6+dune
- topkg.1.0.7
```

Unlike previously, the version of the `fmt` library that is picked is `dev`, to
signify a development version.

The next build will check out the sources from that repository instead of
downloading the release tarball:

```sh
$ dune exec ./test.exe
Hello, OCaml, Rust!
```
12 changes: 12 additions & 0 deletions doc/tutorials/dune-package-management/pinning/dune-project
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(lang dune 3.17)
(name test)

(pin
(url "git+https://github.com/dbuenzli/fmt.git")
(package (name fmt)))

(package
(name test)
(depends
(ocaml (>= 4.14))
fmt))
52 changes: 52 additions & 0 deletions doc/tutorials/dune-package-management/repos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Custom Repositories

By default when locking package versions, Dune looks up packages from two
sources:

1. The upstream, community maintained `opam-repository` at
[ocaml/opam-repository](https://github.com/ocaml/opam-repository) for most
packages
2. An overlay repository with patched software to allow usage in Dune at
[ocaml-dune/opam-overlays](https://github.com/ocaml-dune/opam-overlays)

To change the presets, the `dune-workspace` file has to be edited (and created
if it didn't exist):

::::{dropdown} `dune-workspace`
:icon: file-code

:::{literalinclude} repos/dune-workspace
:language: dune
:::

::::

In this case, we want to select a specific revision of the community repository
instead of always using the most recent one, as it would do by default. We
define a new repository and configure the lock directory to use this
repository.

When relocking the dependencies, the list of packages that are found as
dependencies changes accordingly:

```sh
$ dune pkg lock
Solution for dune.lock:
- base-unix.base
- fmt.0.9.0
- ocaml.5.0.0
- ocaml-base-compiler.5.0.0
- ocaml-config.3
- ocamlbuild.0.15.0+dune
- ocamlfind.1.9.6+dune
- topkg.1.0.
```

Compared to before, the OCaml compiler version is older, which shows
that we did indeed pick an older version of the package repository for locking.

:::{note}
This feature can also be used to make sure the locked dependencies are
reproducible, as fixing all the package repository versions will lead to
equivalent locking results.
:::
8 changes: 8 additions & 0 deletions doc/tutorials/dune-package-management/repos/dune-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(lang dune 3.17)

(lock_dir
(repositories overlay specific-upstream))

(repository
(name specific-upstream)
(source "git+https://github.com/ocaml/opam-repository.git#00ac3727bc4ac0eabd3c89e69c1660d6b63a3d48"))
Loading

0 comments on commit d170703

Please sign in to comment.