Skip to content

Developing an OCaml project with Dune and Nix

In this tutorial, you will learn how to initialize an OCaml project that can be developed using dune.

  1. Make dune executable available:

    Terminal window
    nix shell nixpkgs#dune_3
  2. Scaffold a new project using dune:

    Terminal window
    dune init proj 'package_name' 'your-new-project'
  3. Enter the project directory:

    Terminal window
    cd 'your-new-project'
  1. Initialize the template from the root directory of the project:

    Terminal window
    nix flake init github:akirak/flake-templates#ocaml-dune
  2. Open flake.nix and set the pname and version of the package to be built, e.g.:

    default = ocamlPackages.buildDunePackage {
    pname = throw "Name your OCaml package";
    pname = "hello";
    version = throw "Version your OCaml package";
    version = "0.1";
    duneVersion = "3";
    src = self.outPath;
  3. Add .envrc:

    Terminal window
    use flake
  4. Allow direnv:

    Terminal window
    direnv allow

Once the Nix shell is enabled, you can use dune inside the project directory, as you would do in a normal OCaml project.

To add library dependencies to your OCaml project, follow this instruction. In this example, we will be adding cmdliner as a new dependency.

  1. Add the libraries to propagatedBuildInputs (or checkInputs if it's a test dependency) attribute to your package:

    default = ocamlPackages.buildDunePackage {
    ...
    src = self.outPath;
    propagatedBuildInputs = with ocamlPackages; [
    base
    core
    core_unix
    cmdliner
    ];
    };
  2. Add them to depends field of your package in the dune-project file:

    (package
    (name hello)
    (allow_empty)
    (depends
    (ocaml (>= 4.08.0))
    (dune (>= 3.16))
    (cmdliner (>= 1.3.0))
    (alcotest :with-test)))
  3. Also add them to libraries field of a suitable stanza in the relevant dune file.

    (library
    (public_name hello)
    (name hello)
    (libraries unix)
    (libraries unix cmdliner)
    )

To add dependencies for testing that are not required for production, follow this example. We will be adding alcotest and qcheck as test dependencies.

  1. In flake.nix, set doCheck to true in the arguments to buildDunePackage:

    default = ocamlPackages.buildDunePackage {
    ...
    doCheck = true;
    };
  2. Also in the arguments, add checkInputs field which should be a list of test-only dependencies:

    default = ocamlPackages.buildDunePackage {
    ...
    doCheck = true;
    checkInputs = with ocamlPackages; [
    alcotest
    qcheck-core
    ];
    };
  3. If your dune-project file contains one or more package entries, add the dependencies to depends field of relevant package. The dependencies should have :with-test option:

    (package
    (name hello)
    (allow_empty)
    (depends
    (ocaml (>= 4.08.0))
    (dune (>= 3.16))
    (cmdliner (>= 1.3.0))
    ; test-only dependency
    (alcotest :with-test)
    ; test-only dependency with a version constraint
    (qcheck-core (and :with-test (>= 0.19)))
    ...
    ))
  4. Also add the dependencies to the dune file for testing (typically test/dune):

    (test
    (name test_hello)
    (libraries
    alcotest
    qcheck-core)
    )

Generating documentation for the dependencies

Section titled “Generating documentation for the dependencies”

To generate documentation of the dependencies, use odig.

  1. To generate a documentation, use a justfile recipe bundled in the template:

    Terminal window
    just odig-odoc
  2. To search through the documentation, you can run a local instance of sherlodoc or open a generated HTML file directly.

To run sherlodoc, follow this instruction.

  1. Enable sherlodoc in the flake.nix:

    devShells = eachSystem (
    { pkgs, ocamlPackages, ... }:
    {
    default = pkgs.mkShell {
    inputsFrom = [ self.packages.${pkgs.system}.default ];
    buildInputs = (
    with ocamlPackages;
    [
    ocaml-lsp
    ocamlformat
    ocp-indent
    ...
    # (sherlodoc.override { enableServe = true; })
    (sherlodoc.override { enableServe = true; })
    ]
    )
    # ++ lib.optional pkgs.stdenv.isLinux pkgs.inotify-tools
    ;
    };
    }
    );
  2. Run sherlodoc index on *.odocl files. The template contains a justfile recipe, so you can just run:

    Terminal window
    just sherlodoc-index
  3. To browse the documentation, use a justfile recipe:

    Terminal window
    just sherlodoc-serve

For a detailed description, consult the documentation of sherlodoc.