Difference between revisions of "OCaml Doc : Documenting code in OCaml"

From Xen
m (Removed superfluous <nowiki></nowiki> tag pairs.)
Line 20: Line 20:
 
From a xen-api.hg chroot environment, execute the following commands:
 
From a xen-api.hg chroot environment, execute the following commands:
   
<pre><nowiki>
+
<pre>
 
cd /myrepos/xen-api.hg
 
cd /myrepos/xen-api.hg
 
make doc
 
make doc
</nowiki></pre>
+
</pre>
   
   
This will create the source documentation in <code><nowiki>xen-api.hg/ocaml/doc</nowiki></code> which you can point a web browser towards.
+
This will create the source documentation in <code>xen-api.hg/ocaml/doc</code> which you can point a web browser towards.
   
This documentation can be extended by documentation for the packages in <code><nowiki>xen-api-libs.hg</nowiki></code>. To do this, first build the documentation in <code><nowiki>xen-api.hg</nowiki></code>, and then simply do <code><nowiki>make doc</nowiki></code> in <code><nowiki>/myrepos/xen-api-libs.hg</nowiki></code>.
+
This documentation can be extended by documentation for the packages in <code>xen-api-libs.hg</code>. To do this, first build the documentation in <code>xen-api.hg</code>, and then simply do <code>make doc</code> in <code>/myrepos/xen-api-libs.hg</code>.
   
 
== Documentation Guidelines ==
 
== 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: <code><nowiki>(** this is an OCamldoc description *)</nowiki></code>.
+
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: <code>(** this is an OCamldoc description *)</code>.
   
 
Below is a list of best practices for adding OCamldoc descriptions to OCaml code.
 
Below is a list of best practices for adding OCamldoc descriptions to OCaml code.
   
* Use an interface file (<code><nowiki>.mli</nowiki></code>) for each implementation file (<code><nowiki>.ml</nowiki></code>) 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.
+
* Use an interface file (<code>.mli</code>) for each implementation file (<code>.ml</code>) 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 <code><nowiki>mli</nowiki></code> 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.
+
* Always start the <code>mli</code> 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 <code><nowiki>mli</nowiki></code> file into logical sections. The order of elements in the eventual documentation will be the same. The order does not need to match the <code><nowiki>ml</nowiki></code> file.
+
* Structure the elements of your <code>mli</code> file into logical sections. The order of elements in the eventual documentation will be the same. The order does not need to match the <code>ml</code> file.
* Use descriptive level 2 or 3 headings above each section: <code><nowiki>(** \{2 This is a level 2 heading\} *)</nowiki></code>.
+
* Use descriptive level 2 or 3 headings above each section: <code>(** \{2 This is a level 2 heading\} *)</code>.
 
* Separate elements by blank lines and put the description immediately above the element it is associated with.
 
* 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 [http://caml.inria.fr/pub/docs/manual-ocaml/manual029.html the manual]
 
* Use OCamldoc's mark-up features to apply formatting, make numbered lists etc. See [http://caml.inria.fr/pub/docs/manual-ocaml/manual029.html the manual]
Line 45: Line 45:
 
== Custom OCamldoc Generator and Structure ==
 
== 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 <code><nowiki>make doc</nowiki></code> 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 <code><nowiki>make doc</nowiki></code> (which can be time consuming) in order to change the layout of the documentation. The core of the generator is the module <code><nowiki>ocaml/doc/odoc_json.ml</nowiki></code> in <code><nowiki>xen-api.hg</nowiki></code>. The other files in <code><nowiki>ocaml/doc/</nowiki></code> are used to render the web interface.
+
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 <code>make doc</code> 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 <code>make doc</code> (which can be time consuming) in order to change the layout of the documentation. The core of the generator is the module <code>ocaml/doc/odoc_json.ml</code> in <code>xen-api.hg</code>. The other files in <code>ocaml/doc/</code> are used to render the web interface.
   
Our code documentation is divided into sections for executables, libraries and packages. The <code><nowiki>make doc</nowiki></code> command creates a directory with JSON files in <code><nowiki>ocaml/doc/content</nowiki></code> for each such component. The main page of the documentation gives an overview of the components for which documentation has been generated.
+
Our code documentation is divided into sections for executables, libraries and packages. The <code>make doc</code> command creates a directory with JSON files in <code>ocaml/doc/content</code> 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.
 
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.
Line 53: Line 53:
 
== Registering Executables and Libraries ==
 
== Registering Executables and Libraries ==
   
In order to generate code documentation for an executable in <code><nowiki>xen-api.hg</nowiki></code>, edit the <code><nowiki>OMakefile</nowiki></code> that has the <code><nowiki>OCamlProgram</nowiki></code> command for this component. On a new line right below the <code><nowiki>OCamlProgram</nowiki></code> line, add a new line for the <code><nowiki>OCamlDocProgram</nowiki></code> command, having two parameters:
+
In order to generate code documentation for an executable in <code>xen-api.hg</code>, edit the <code>OMakefile</code> that has the <code>OCamlProgram</code> command for this component. On a new line right below the <code>OCamlProgram</code> line, add a new line for the <code>OCamlDocProgram</code> command, having two parameters:
 
# The name of the executable.
 
# The name of the executable.
 
# A list of module names (separated by spaces) the executable comprises of.
 
# A list of module names (separated by spaces) the executable comprises of.
Line 59: Line 59:
 
Note that these parameters are usually identical to the one of the associated {nl:OCamlProgram} command.
 
Note that these parameters are usually identical to the one of the associated {nl:OCamlProgram} command.
   
For example, for the <code><nowiki>xapi</nowiki></code> executable, the file <code><nowiki>ocaml/xapi/OMakefile</nowiki></code> contains the following lines:
+
For example, for the <code>xapi</code> executable, the file <code>ocaml/xapi/OMakefile</code> contains the following lines:
   
<pre><nowiki>
+
<pre>
 
OCamlProgram(xapi, $(XAPI_MODULES))
 
OCamlProgram(xapi, $(XAPI_MODULES))
 
OCamlDocProgram(xapi, $(XAPI_MODULES))
 
OCamlDocProgram(xapi, $(XAPI_MODULES))
</nowiki></pre>
+
</pre>
   
   
Similarly, documentation for an OCaml library is enabled by adding a <code><nowiki>OCamlDocLibrary</nowiki></code> after an <code><nowiki>OCamlLibrary</nowiki></code> line.
+
Similarly, documentation for an OCaml library is enabled by adding a <code>OCamlDocLibrary</code> after an <code>OCamlLibrary</code> line.
   
The <code><nowiki>xen-api-libs.hg</nowiki></code> build system uses regular makefiles, rather than omake. The above therefore does not apply, and a little more work is needed. See the <code><nowiki>Makefile</nowiki></code> in <code><nowiki>stdext</nowiki></code> for an example.
+
The <code>xen-api-libs.hg</code> build system uses regular makefiles, rather than omake. The above therefore does not apply, and a little more work is needed. See the <code>Makefile</code> in <code>stdext</code> for an example.
   
 
== Common Pitfalls ==
 
== Common Pitfalls ==
Line 80: Line 80:
 
'''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.'''
 
'''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 <code><nowiki>mli</nowiki></code> files that looks something like the following:
+
An often made mistake is to have an <code>mli</code> files that looks something like the following:
   
<pre><nowiki>
+
<pre>
 
(** Returns the current amount of free memory in the host. *)
 
(** Returns the current amount of free memory in the host. *)
 
val get_free_memory_kib : xc:Xc.handle -> int64
 
val get_free_memory_kib : xc:Xc.handle -> int64
Line 89: Line 89:
 
(** Returns the total amount of memory available in this host. *)
 
(** Returns the total amount of memory available in this host. *)
 
val get_total_memory_mib : xc:Xc.handle -> int64
 
val get_total_memory_mib : xc:Xc.handle -> int64
</nowiki></pre>
+
</pre>
   
 
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!
 
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!
Line 95: Line 95:
 
The source code should have instead been as follows to generate correct documentation:
 
The source code should have instead been as follows to generate correct documentation:
   
<pre><nowiki>
+
<pre>
 
(** Memory computations *)
 
(** Memory computations *)
   
Line 106: Line 106:
 
(** Returns the total amount of memory available in this host. *)
 
(** Returns the total amount of memory available in this host. *)
 
val get_total_memory_mib : xc:Xc.handle -> int64
 
val get_total_memory_mib : xc:Xc.handle -> int64
</nowiki></pre>
+
</pre>
   
   
Line 113: Line 113:
 
Always make sure to properly close braces when using special OCamldoc formatting features. For example, there is an unclosed brace in the following 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:
   
<pre><nowiki>
+
<pre>
 
(** {2 Garbage Collection of Symbols *)
 
(** {2 Garbage Collection of Symbols *)
</nowiki></pre>
+
</pre>
   
 
This will cause the OCamldoc parse to raise and error, and documentation for this module to be absent.
 
This will cause the OCamldoc parse to raise and error, and documentation for this module to be absent.

Revision as of 21:46, 28 November 2011


OCaml Doc

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.
  • ...?