# How to get pleasant documentation for a library using Dune?

**URL:** <https://discuss.ocaml.org/t/how-to-get-pleasant-documentation-for-a-library-using-dune/7121>\
**Category:** Learning\
**Tags:** odoc, dune\
**Created:** [January 20, 2021, 11:01am UTC](https://discuss.ocaml.org/t/how-to-get-pleasant-documentation-for-a-library-using-dune/7121 "2021-01-20T11:01:31Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [January 20, 2021, 11:01am UTC](https://discuss.ocaml.org/t/how-to-get-pleasant-documentation-for-a-library-using-dune/7121/1 "2021-01-20T11:01:31Z")

</div>

I’m working to publish a small library using Dune. The documentation automatically generated by `dune build @doc` looks fairly unpleasant to me, as I don’t see an easy way to explain what the library is about. I’m creating this topic in case I am missing something simple, and to get other people to share their library-documentation practices or examples.

## Problem description

For the sake of the example let’s imagine that the library is called `Foo` and contains three modules `A`, `B` and `C`. I’m using the standard dune approach of wrapped modules, so I get three compilation units `Foo.A`, `Foo.B`, `Foo.C`. Each module has a `.mli` file with documentation comments.

When I run `dune build @doc`, dune generates an `index.html` file with basically no content, pointing to a `foo/index.html` file with basically no content, pointing to a `foo/Foo/index.html` looking like this:

> Up – foo » Foo
> 
> # Module `Foo`
> 
> `module A : sig ... end`
> 
> `module B : sig ... end`
> 
> `module C : sig ... end`

It’s easy to skip the first two pages, and use the third page as a landing page for the documentation of my library. However, this landing page is not very pleasant:

1. It should explain what the library is about.
2. It should briefly describe what each module does, so that users know which module they want to look at first. (Note: this one is in fact solved, see below.)

My problem is: what should I change in my Dune setup to be able to do this?

I have read the [dune documentation on documentation](https://dune.readthedocs.io/en/stable/documentation.html), but I could not find an answer to this question.

## Rough ideas

Roughly I see two ways to get what I want, that I have not tried yet:

1. I could write my own landing page for the library as a separate `doc.mld` file, use the `(documentation)` stanza to get it included in the built documentation, and use this as the entry point into my library.
2. In could write my own `foo.ml` module instead of using Dune’s default wrapped-module scaffolding, inserting my own `module A = Foo__A` aliases, with documentation comments in the standard style. Then I suppose that `foo/Foo/index.html` would get this content in the way I expect.

They feel a bit complex to me, and (2) involves the tedious work of redoing the wrapping logic myself. I guess that (1) is not so bad, and I would be inclined to do this if it was documented somewhere as the recommended approach.

(Maybe there is some odoc option that would help solve this problem?)

## Examples from other people?

Do you have a library built using dune with nice documentation? If so, can you show the documentation and the corresponding sources (in particular dune setup)?

---

<div class="post-metadata">

**Author:** ![tmattio](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/tmattio/32/1606_2.png) [@tmattio](https://discuss.ocaml.org/u/tmattio)\
**Post date:** [January 20, 2021, 11:31am UTC](https://discuss.ocaml.org/t/how-to-get-pleasant-documentation-for-a-library-using-dune/7121/2 "2021-01-20T11:31:11Z")

</div>

I think the documentation of [Streaming](https://odis-labs.github.io/streaming/streaming/index.html) is a great example of the option 1 you describe.

The corresponding Dune setup can be found [here](https://github.com/odis-labs/streaming/blob/master/streaming/dune)

That’s also the approach we took for [Opium’s documentation](https://rgrinberg.github.io/opium/opium/index.html), although the index page is certainly not as impressive as Streaming’s.

---

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [January 20, 2021, 12:52pm UTC](https://discuss.ocaml.org/t/how-to-get-pleasant-documentation-for-a-library-using-dune/7121/3 "2021-01-20T12:52:19Z")

</div>

Thanks! It looks like these systems rely on an undocumented feature of the `(documentation)` stanza (or odoc), which is that a user-provided `index.mld` file will implicitly replace the automatically-generated `index.mld` file, giving a reasonably natural result.

The opium documentation also [uses](https://github.com/rgrinberg/opium/blob/2a89e35/opium/doc/index.mld#L72-L74) the `{!modules: modulename ...}` markup directive, which is a way to include the module index within this manually-written landing page without having to duplicate the markup. Streaming¹ uses [inline html](https://github.com/odis-labs/streaming/blob/ee5d82a/streaming/index.mld#L32-L68) instead to get a nicer-looking result, but it is too much effort. Maybe there is a better way, or the tools could be improved to make this easier.

¹: I’m ashamed to admit that I wasn’t aware of this very nice library [Streaming](https://odis-labs.github.io/streaming/streaming/index.html), am I consuming the wrong sources of information on the OCaml ecosystem?

Finally, the Opium documentation manifestly has a short synopsis for each module in its listing, which corresponds to my “It should briefly describe what each module does” requirement. I believe that this comes from the first line of the first documentation comment of the module. There are module-global documentation comments in the library I’m working on, but they do not include such first-line headers.

Once I have the impression of understanding what is a good way to do this, I may try to contribute better documentation in `dune`.

---

<div class="post-metadata">

**Author:** ![Drup](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/drup/32/35_2.png) [@Drup](https://discuss.ocaml.org/u/Drup)\
**Post date:** [January 20, 2021, 1:17pm UTC](https://discuss.ocaml.org/t/how-to-get-pleasant-documentation-for-a-library-using-dune/7121/4 "2021-01-20T13:17:50Z")

</div>

> [@gasche](#):
>
> It looks like these systems rely on an undocumented feature of the `(documentation)` stanza (or odoc), which is that a user-provided `index.mld` file will implicitly replace the automatically-generated `index.mld` file, giving a reasonably natural result.

I confirm this feature is here to stay, is the right one to customize your index page, and in the future will benefit from good support from odoc directly.

> [@gasche](#):
>
> The opium documentation also [uses](https://github.com/rgrinberg/opium/blob/2a89e35/opium/doc/index.mld#L72-L74) the `{!modules: modulename ...}` markup directive, which is a way to include the module index within this manually-written landing page without having to duplicate the markup. Streaming¹ uses [inline html](https://github.com/odis-labs/streaming/blob/ee5d82a/streaming/index.mld#L32-L68) instead to get a nicer-looking result, but it is too much effort. Maybe there is a better way, or the tools could be improved to make this easier.

I would strongly advise to use the `modules` markup directive, and to suggests output improvements on odoc’s bug instead of hacking HTML together. We could absolutely add the synopsis of the module here, for instance.

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [January 20, 2021, 2:22pm UTC](https://discuss.ocaml.org/t/how-to-get-pleasant-documentation-for-a-library-using-dune/7121/5 "2021-01-20T14:22:08Z")

</div>

> [@gasche](#):
>
> which is that a user-provided `index.mld` file will implicitly replace the automatically-generated `index.mld` file, giving a reasonably natural result.

This is also the correct way to customize the landing page of your package for `odig` generated doc sets, see [here](https://erratique.ch/software/odig/doc/packaging.html#odoc_api_and_manual) for more information.

> [@Drup](#):
>
> I confirm this feature is here to stay, is the right one to customize your index page, and in the future will benefit from good support from odoc directly.

There’s an open issue about that [here](https://github.com/ocaml/odoc/issues/297).

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [January 20, 2021, 2:55pm UTC](https://discuss.ocaml.org/t/how-to-get-pleasant-documentation-for-a-library-using-dune/7121/6 "2021-01-20T14:55:28Z")

</div>

> [@gasche](#):
>
> ¹: I’m ashamed to admit that I wasn’t aware of this very nice library [Streaming](https://odis-labs.github.io/streaming/streaming/index.html), am I consuming the wrong sources of information on the OCaml ecosystem?

You are in the right place, Streaming was announced[1] here 🙂

[1] [[ANN] First release of streaming](https://discuss.ocaml.org/t/ann-first-release-of-streaming/5961)

---

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [January 21, 2021, 4:47pm UTC](https://discuss.ocaml.org/t/how-to-get-pleasant-documentation-for-a-library-using-dune/7121/7 "2021-01-21T16:47:09Z")

</div>

I sent a PR to better document the use of `index.mld` in the Dune documentation: [dune#4146](https://github.com/ocaml/dune/pull/4146).
