diff --git a/doc/tutorials/dune-package-management/dependencies.md b/doc/tutorials/dune-package-management/dependencies.md new file mode 100644 index 000000000000..b40d849cc814 --- /dev/null +++ b/doc/tutorials/dune-package-management/dependencies.md @@ -0,0 +1,141 @@ +# 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'll use the popular `fmt` library as an example, but any +library on the OPAM repository 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 re-lock 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 as usual via the `libraries` +stanza. + +:::: + +This will allow us to use `Fmt` 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 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 prettyprinting: + +```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 you can use the regular Dune dependency syntax that is +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 will ensure that the `fmt` package to be installed 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. +::: diff --git a/doc/tutorials/dune-package-management/dependencies/constraints b/doc/tutorials/dune-package-management/dependencies/constraints new file mode 100644 index 000000000000..8457e2fa0843 --- /dev/null +++ b/doc/tutorials/dune-package-management/dependencies/constraints @@ -0,0 +1,8 @@ +(lang dune 3.17) +(name test) + +(package + (name test) + (depends + (ocaml (>= 4.14)) + (fmt (and (>= 0.6) (< 1.0))))) diff --git a/doc/tutorials/dune-package-management/dependencies/dune b/doc/tutorials/dune-package-management/dependencies/dune new file mode 100644 index 000000000000..eb7ff88832b8 --- /dev/null +++ b/doc/tutorials/dune-package-management/dependencies/dune @@ -0,0 +1,3 @@ +(executable + (public_name test) + (libraries fmt)) diff --git a/doc/tutorials/dune-package-management/dependencies/dune-project b/doc/tutorials/dune-package-management/dependencies/dune-project new file mode 100644 index 000000000000..9dd0e2eddce8 --- /dev/null +++ b/doc/tutorials/dune-package-management/dependencies/dune-project @@ -0,0 +1,8 @@ +(lang dune 3.17) +(name test) + +(package + (name test) + (depends + (ocaml (>= 4.14)) + fmt)) diff --git a/doc/tutorials/dune-package-management/dependencies/test.ml b/doc/tutorials/dune-package-management/dependencies/test.ml new file mode 100644 index 000000000000..1583eb1d32f2 --- /dev/null +++ b/doc/tutorials/dune-package-management/dependencies/test.ml @@ -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 diff --git a/doc/tutorials/dune-package-management/index.md b/doc/tutorials/dune-package-management/index.md new file mode 100644 index 000000000000..16e0676062ba --- /dev/null +++ b/doc/tutorials/dune-package-management/index.md @@ -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 +::: diff --git a/doc/tutorials/dune-package-management/pinning.md b/doc/tutorials/dune-package-management/pinning.md new file mode 100644 index 000000000000..6ad7aba01fc0 --- /dev/null +++ b/doc/tutorials/dune-package-management/pinning.md @@ -0,0 +1,38 @@ +# Pinning projects + +When Dune is looking up packages it uses OPAM repository to look up packages. +But in cases where e.g. the package is not released it is also possible to +specify the source of the packages directory. 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` name 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 OPAM repository. + +```sh +$ dune pkg lock +TODO +``` + +The next build will check out the sources from that repository instead of +downloading the release tarball: + +```sh +$ dune exec ./test.exe +TODO +``` diff --git a/doc/tutorials/dune-package-management/pinning/dune-project b/doc/tutorials/dune-package-management/pinning/dune-project new file mode 100644 index 000000000000..85196454ceed --- /dev/null +++ b/doc/tutorials/dune-package-management/pinning/dune-project @@ -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)) diff --git a/doc/tutorials/dune-package-management/repos.md b/doc/tutorials/dune-package-management/repos.md new file mode 100644 index 000000000000..ec6d917f3081 --- /dev/null +++ b/doc/tutorials/dune-package-management/repos.md @@ -0,0 +1,53 @@ +## Custom repositories + +In normal usage 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 OPAM repository with patched software to allow usage in Dune at + [ocaml-dune/opam-overlays](https://github.com/ocaml-dune/opam-overlays) + +This list is built-in but can be configured. To change the repositories to be +used 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 +::: + +:::: + +The `repository` stanza defines a new repository `specific-upstream` with an +URL. In this case we want to select a specific revision of the OPAM repository, +instead of always using the most recent one. + +The `lock_dir` stanza defines the configuration for a specific lock directory; +amongst others it specifies the repositories to be used for the lock directory. + +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 version of the OCaml compiler is older. + +:::{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. +::: diff --git a/doc/tutorials/dune-package-management/repos/dune-workspace b/doc/tutorials/dune-package-management/repos/dune-workspace new file mode 100644 index 000000000000..601b59b1cd49 --- /dev/null +++ b/doc/tutorials/dune-package-management/repos/dune-workspace @@ -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")) diff --git a/doc/tutorials/dune-package-management/setup.md b/doc/tutorials/dune-package-management/setup.md new file mode 100644 index 000000000000..678a6072fe95 --- /dev/null +++ b/doc/tutorials/dune-package-management/setup.md @@ -0,0 +1,109 @@ +# Setting up Package Management with Dune + +The idea of package management with Dune has been to be as unobtrusive as +possible. Thus most projects can easily be built with just the minimum of +changes. + +In this tutorial we'll create a simple project to use the OCaml package +management for the very first time. + +## Declare dependencies + +The best way to work with the package management is to declare your +dependencies in the `dune-project` file. + +::::{dropdown} `dune-project` +:icon: file-code + +:::{literalinclude} setup/dune-project +:language: dune +:emphasize-lines: 6-7 +::: + +We define a project called `test` and define that to build it we need an OCaml +compiler that is at least version 4.14. + +This is exactly the same information that is used to generate OPAM files using +the `generate_opam_files` stanza as described in +{doc}`/howto/opam-file-generation`. + +:::: + +::::{dropdown} `test.ml` +:icon: file-code + +:::{literalinclude} setup/test.ml +:language: ocaml +::: + +To show that the build works, this simple program will be built and executed. + +:::: + + +::::{dropdown} `dune` +:icon: file-code + +:::{literalinclude} setup/dune +:language: dune +::: + +To declare our module an executable we need a little bit of configuration; we +just define the module as an executable. + +:::: + +After our project skeleton is set-up, we can proceed to the next step. + +## Locking dependencies + +After declaring the dependencies, you will need to tell Dune which package +versions to use for your project. This is done by creating a lock directory. +This is easily done with a new Dune command: + +```sh +$ dune pkg lock +Solution for dune.lock: +- ocaml.5.2.0 +- ocaml-base-compiler.5.2.0 +- ocaml-config.3 +``` + +This will update all the required OPAM repositories, use the newest version of +each and try to find a set of packages and versions that satisfy the +constraints that your project dependencies declare. + +:::{note} +The versions that get locked might be different from this tutorial, as we only +specified the lower bound of `ocaml`; bar any additional configuration Dune +will pick the newest possible version for each dependency. +::: + +## Build project + +The build the project you can just use the regular Dune commands to build your +project. + +```sh +dune build +``` + +This will download, build and install all your locked dependencies and then use +those to build your project. This means that the first time building will take +longer than usual, as the dependencies need to be built first. Subsequent +builds where no dependency needs to be updated will be just as fast as before. + +We can show that the package has been built successfully and works as expected: + +```sh +$ dune exec ./test.exe +Hello, OCaml, Rust! +``` + +## Conclusion + +In this section we learned how to set up a Dune project that picks a compiler +and installs it without need for any tooling. + +In the next section {doc}`dependencies` we will look on how to add third party +dependencies. diff --git a/doc/tutorials/dune-package-management/setup/dune b/doc/tutorials/dune-package-management/setup/dune new file mode 100644 index 000000000000..e422d9fbae2a --- /dev/null +++ b/doc/tutorials/dune-package-management/setup/dune @@ -0,0 +1,2 @@ +(executable + (public_name test)) diff --git a/doc/tutorials/dune-package-management/setup/dune-project b/doc/tutorials/dune-package-management/setup/dune-project new file mode 100644 index 000000000000..ac137b377f81 --- /dev/null +++ b/doc/tutorials/dune-package-management/setup/dune-project @@ -0,0 +1,7 @@ +(lang dune 3.17) +(name test) + +(package + (name test) + (depends + (ocaml (>= 4.14)))) diff --git a/doc/tutorials/dune-package-management/setup/test.ml b/doc/tutorials/dune-package-management/setup/test.ml new file mode 100644 index 000000000000..60c76c598a75 --- /dev/null +++ b/doc/tutorials/dune-package-management/setup/test.ml @@ -0,0 +1,5 @@ +let langs = ["OCaml"; "Rust"] + +let () = + let s = String.concat ", " langs in + Format.printf "Hello, %s!\n" s diff --git a/doc/tutorials/index.md b/doc/tutorials/index.md index 2ce22aa1c624..1c6322e96cbb 100644 --- a/doc/tutorials/index.md +++ b/doc/tutorials/index.md @@ -7,4 +7,5 @@ These tutorials are hands-on lessons to learn about Dune. :maxdepth: 1 developing-with-dune/index +dune-package-management/index :::