Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: dune pkg tutorial #10920

Merged
merged 1 commit into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading