OCaml Doc : Documenting code in OCaml

From Xen
Revision as of 13:12, 11 July 2013 by Lars.kurth (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

This page describes techniques specific to the use of OCamldoc within the Xapi codebase and best practices of how to use it. See the official ocamldoc manual for a full explanation of how to use OCamldoc.

Building OCamldoc Documentation

From a xen-api.hg chroot environment, execute the following commands:

cd /myrepos/xen-api.hg
make doc

This will create the source documentation in xen-api.hg/ocaml/doc which you can point a web browser towards.

This documentation can be extended by documentation for the packages in xen-api-libs.hg. To do this, first build the documentation in xen-api.hg, and then simply do make doc in /myrepos/xen-api-libs.hg.

Documentation Guidelines

Code documentation is most useful when it contains descriptions about each module, function, type etc. that is included. Such descriptions are placed in the OCaml source code in the form of special comment blocks: (** this is an OCamldoc description *).

Below is a list of best practices for adding OCamldoc descriptions to OCaml code.

  • Use an interface file (.mli) for each implementation file (.ml) and write all the documentation in there. In the interface file, only include the module elements that the module needs to expose to other modules. Make the interface as simple and clear as possible.
  • Always start the mli file by a description of the module as a whole. The first sentence of this description will be shown in the list of modules, the whole description on the module page.
  • Structure the elements of your mli file into logical sections. The order of elements in the eventual documentation will be the same. The order does not need to match the ml file.
  • Use descriptive level 2 or 3 headings above each section: (** \{2 This is a level 2 heading\} *).
  • Separate elements by blank lines and put the description immediately above the element it is associated with.
  • Use OCamldoc's mark-up features to apply formatting, make numbered lists etc. See the manual

Custom OCamldoc Generator and Structure

We have our own custom OCamldoc generator that fits our needs better than the conventional HTML generator does. Our generator uses JSON as an intermediate format for the storage of code metadata. Calling make doc will generate these JSON files. These files can be conveniently loaded by a JavaScript program and displayed as a website. The benefit is that it is not needed to run make doc (which can be time consuming) in order to change the layout of the documentation. The core of the generator is the module ocaml/doc/odoc_json.ml in xen-api.hg. The other files in ocaml/doc/ are used to render the web interface.

Our code documentation is divided into sections for executables, libraries and packages. The make doc command creates a directory with JSON files in ocaml/doc/content for each such component. The main page of the documentation gives an overview of the components for which documentation has been generated.

Clicking on a component name brings up a list of modules contained in the component with short descriptions, as well as a list of libraries the component depends on. Going down further by clicking on a module name shows an overview of the interface of the module (functions, types, exceptions etc.). This page also lists the other modules that depend on this module, as well as a list of modules that the current module uses. These dependency lists give a better understanding of the ways modules fit together, and additionally allow for convenient navigation through the documentation.

Registering Executables and Libraries

In order to generate code documentation for an executable in xen-api.hg, edit the OMakefile that has the OCamlProgram command for this component. On a new line right below the OCamlProgram line, add a new line for the OCamlDocProgram command, having two parameters:

  1. The name of the executable.
  2. A list of module names (separated by spaces) the executable comprises of.

Note that these parameters are usually identical to the one of the associated {nl:OCamlProgram} command.

For example, for the xapi executable, the file ocaml/xapi/OMakefile contains the following lines:

OCamlProgram(xapi, $(XAPI_MODULES))
OCamlDocProgram(xapi, $(XAPI_MODULES))

Similarly, documentation for an OCaml library is enabled by adding a OCamlDocLibrary after an OCamlLibrary line.

The xen-api-libs.hg build system uses regular makefiles, rather than omake. The above therefore does not apply, and a little more work is needed. See the Makefile in stdext for an example.

Common Pitfalls

OCamldoc Comments Attaching to the Wrong Function

The rules which determine which function an OCamldoc comment is associated with are complex, and this is a common cause of incorrect documentation being generated.

Refer to section 15.2 in the OCamldoc manual for the official description of these rules. Particular attention should be paid to the following rule: A special comment after an element is associated to this element if there is no blank line or comment between the special comment and the element.

An often made mistake is to have an mli files that looks something like the following:

(** Returns the current amount of free memory in the host. *)
val get_free_memory_kib : xc:Xc.handle -> int64
(** Returns the current amount of scrub memory in the host. *)
val get_scrub_memory_kib : xc:Xc.handle -> int64
(** Returns the total amount of memory available in this host. *)
val get_total_memory_mib : xc:Xc.handle -> int64

What will happen is that the first comment is associated to the module as a whole, while the other modules are associate with the functions right above the comment, which is clearly not what was intended!

The source code should have instead been as follows to generate correct documentation:

(** Memory computations *)

(** Returns the current amount of free memory in the host. *)
val get_free_memory_kib : xc:Xc.handle -> int64

(** Returns the current amount of scrub memory in the host. *)
val get_scrub_memory_kib : xc:Xc.handle -> int64

(** Returns the total amount of memory available in this host. *)
val get_total_memory_mib : xc:Xc.handle -> int64

Syntax Error in OCamldoc Comment

Always make sure to properly close braces when using special OCamldoc formatting features. For example, there is an unclosed brace in the following comment:

(** {2 Garbage Collection of Symbols *)

This will cause the OCamldoc parse to raise and error, and documentation for this module to be absent.

Code-Documentation Wish List

  • A search function for modules, functions, etc.
  • A dependency graph that illustrates how executables/libraries link together.
  • Links to the real source code.
  • Static code analysis, e.g. based on OCaml Metrics.
  • ...?