How to have nested libraries? Confused about dune etc
Asked Answered
T

1

10

I have an OCaml project using dune

Following advice in basic tutorials I have a dir structure like:

bin/
    cli.ml
    dune
lib/
    dune
    ...
    <various>.ml

The number of files in my lib dir is growing and I would like to have another level of namespacing.

I want sub dirs like:

lib/
    utils/
        dune
        ...
        <various>.ml
    some_other_domain/
        dune
        ...
        <various>.ml
    dune
    ...
    <various>.ml

And I want to be able to open them like Lib.Utils.Whatever

I assume this must be possible?

I tried making a dune file under lib/utils like:

(library
  (name utils)
  (libraries ...))

...but open Lib.Utils.Whatever doesn't seem to work.

I found the subdir stanza ...but if I add that to lib/dune and define utils as a subdir library then I don't get the namespacing... I have to open Utils rather than open Lib.Utils

Taurine answered 9/5, 2021 at 21:1 Comment(0)
N
8

It's actually a bit strange to call them "nested libraries" since you want to call them with Lib.Utils.Whatever. Utils, here, is a sub-module of Lib. Here's what I was able to do if it can help you:

.
├── bin
│   ├── cli.ml
│   └── dune
├── dune-project
├── lib
│   ├── dune
│   ├── lib.ml
│   ├── suba
│   │   └── suba.ml
│   └── subb
│       └── subb.ml

With

bin/cli.ml

let () =
  Lib.Suba.a ();
  Lib.Subb.b ()

bin/dune

(executable
 (name cli)
 (libraries lib)
)

lib/dune

(include_subdirs unqualified)

(library
 (name lib)
  (modules suba subb)
)

(If you include your modules like this, you'll have to use them with these exact names, another way of gaining control is to add the following file and remove the (modules suba subb) line:

lib/lib.ml

(* here you can give the name you want -- say A -- and use it in bin with Lib.A *)
module Suba = Suba
module Subb = Subb

(to summarise:

  • either your dune file is containing (modules suba subb)
    • if sub-directory is containing multiple files, you need to put all the files you're using inside your (modules ...) stanza or the compiler won't be able to use them
  • or a lib.ml file where each module you want to export should be included with module MyName = AModule (and only the ones you want to export)
    • with this solution, the modules you don't want to export don't need to be explicitly included in the lib.ml file, the compiler will use them if needed )

sub{a|b}/{a|b}.ml

let {a|b} () = Format.eprintf "{A|B}@."

suba.ml and subb.ml are used as sub-modules of lib and can be used with Lib.Suba.a() as you can see in cli.ml


Notice that this forbids you from giving the exact same name to two files since the directories will all be flatten in the parent directory so you can't have something like:

.
├── bin
│   ├── cli.ml
│   └── dune
├── dune-project
├── lib
│   ├── dune
│   ├── lib.ml
│   ├── suba
│   │   └── lib.ml
│   └── subb
│       └── lib.ml

because (include_subdirs unqualified) will make it look like

.
├── bin
│   ├── cli.ml
│   └── dune
├── dune-project
├── lib
│   ├── dune
│   ├── lib.ml
│   ├── lib.ml
|   └── lib.ml

and dune won't be able to know which lib.ml file to use.


[EDIT] If you want one directory per library you just have to remove the dune file at the root of lib and create one for each subdirectories:

.
├── bin
│   ├── cli.ml
│   └── dune
├── dune-project
├── lib
│   ├── suba
│   │   ├── dune
│   │   └── suba.ml
│   └── subb
│       ├── dune
│       └── subb.ml

The only changes are:

bin/cli.ml

let () =
  Suba.a (); (* no more Lib.(...) *)
  Subb.b ()

bin/dune

(executable
 (name cli)
 (libraries suba subb)
)

lib/dune has been removed

lib/sub{a|b}/dune

(library
 (name sub{a|b})
)

And in that case, multiple files inside different directories can have the same name.

Nub answered 10/5, 2021 at 8:45 Comment(4)
Thank you, the limitation is clearer to me now. I guess as compiled language the bin/lib distinction is important, whereas in Python I'm used to "packages" just as namespaces with unlimited nesting. It appears what I want is this feature request github.com/ocaml/dune/issues/1084 ... For now, instead of a single Lib I have made multiple Myproject_<whatever> libs in the root of my project dir. That way I can live with the single level of nesting and the naming is not too bad.Taurine
You don't have to put your libs directories outside of your lib directory, you just have to create one dune file in each subdirectory to declare it as its own library. I can edit my answer if that's not clear for you but just try to cut/paste al you library directories inside your lib directory and it should workNub
Ah that is good to know, it keeps the root dir more tidy that way, thanks 👍Taurine
apparently (include_subdirs qualified) got released in Dune 3.7.0 dune.readthedocs.io/en/stable/dune-files.html#include-subdirs the docs don't give examples to clarify but I think it does what I originally wantedTaurine

© 2022 - 2024 — McMap. All rights reserved.