How to use OCaml libraries in a dev shell

I am having some trouble setting up a dev shell for OCaml development with flakes. My issue is that OCaml doesn’t seem to recognize that a given library (OUnit) is installed. I use direnv with use flake to setup dev shells.

my flake.nix
{
  description = "laziness";
  
  inputs = {
    nixpkgs.url = github:nixos/nixpkgs;
    utils.url = github:numtide/flake-utils;
  };
  
  outputs = { nixpkgs, utils, ... }:
    utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs { inherit system; };
        ocmlpkgs = with pkgs.ocamlPackages; [
          ounit
          findlib
          merlin
          utop
        ];
      in {
        devShell = pkgs.mkShell {
          buildInputs = with pkgs; [
            ocaml
          ] ++ ocmlpkgs;
        };
    });
}

When I enter the environment (either with Emacs’ direnv integration, or simply by going in the directory of this project and letting direnv directly load everything), it complains when I do open OUnit with Unbound module OUnit.

How do you test that it does not work ? with your flake the module if found:

$  cat foo.ml                            
open OUnit;;
                                                                                                                                                                                             
$  ocamlfind ocamlc -package oUnit foo.ml && echo success
success

I complains about it in my editor, and when I load it in the top level.
This works:

$ echo "open OUnit;;" > foo.ml
$ ocamlfind ocamlc -package oUnit foo.ml && echo success
success
$

But not

$ utop foo.ml
File "foo.ml", line 1, characters 5-10:
1 | open OUnit;;
         ^^^^^
Error: Unbound module OUnit
Hint: Did you mean Unit?
$

consider using dune top dune utop instead of utop (it’s the same thing, but dune adds all the required flags)

I am not familiar with dune. Does that require me to setup a dune workspace or something alike? I have tried following the instructions of the dune documentation by doing #use_output "dune ocaml top";; in a utop session, but that still doesn’t work. And, indeed, when trying to do something like dune ocaml top in a shell, it prints nothing.
Maybe it’s my project file that is wrong? Dune refused to run dune ocaml top without it.

dune-project
(lang dune 3.4)

(name laziness)

(generate_opam_files true)

(package
 (name laziness)
 (depends ocaml dune))

I simply copied the result of dune init project laziness removing the parts that weren’t useful (summary, description, …). It also automatically generated an empty laziness.opam (I don’t know what is its purpose). I also tried adding ounit to the depends instruction, but that didn’t change anything.

Sorry I should have said dune utop, not dune top.

To test in a nix-shell with dune_3, utop, findlib and ounit I did:
dune init proj bar
inside bar, I modified lib/dune as follows:

--- lib/dune.old	2022-08-10 20:47:52.511157426 +0200
+++ lib/dune	2022-08-10 20:46:47.185280863 +0200
@@ -1,3 +1,4 @@
 (library
  (name bar)
+ (libraries oUnit)
  )

and then dune utop:

───────────────┬─────────────────────────────────────────────────────────────┬───────────────
               β”‚ Welcome to utop version 2.9.1 (using OCaml version 4.13.1)! β”‚               
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               

Type #utop_help for help about using utop.

─( 20:49:51 )─< command 0 >───────────────────────────────────────────────────{ counter: 0 }─
utop # open OUnit;;
─( 20:49:51 )─< command 1 >───────────────────────────────────────────────────{ counter: 0 }─
utop # 
1 Like

I’ve been using this for quite a while and it works fine, but sometimes it’s a bit awkward because I don’t use dune as my building system, so I really end up setting up all the dune files and everything just to have a working utop… Do you know a way to achieve the same result without relying on a specific build system?

no idea sorry. nix-shell populates $OCAMLPATH, you need to teach your favourite toplevel implementation to look for dependencies there.