Sep 6

Verifying Code Examples in your Documentation

Category: Programming, XML, XSLT, docbook

One of the more annoying aspects of writing developer documentation is that it is very easy for your code examples to get out of date, develop typos, etc(As those reading previous articles may have noticed). In this article I use docbook, xslt, and automake to generate code examples that are guaranteed to compile.

Defining the Example

The first step is to define your example in a way that can be used to generate both code and documentation. For documentation I want to be able to define what portions of the code to skip and what to include. To that end I use a custom XML format. Let’s say I have the following header file:

#ifndef WHATEVER_H
#define WHATEVER_H
class my_class{
public:
    my_class();
 
    int status() const;
private:
    int _status;
};
 
#endif

I’m going to translate that code into this xml which indicates what portions of the document to skip in documentation:

<example>
<code><![CDATA[
#ifndef WHATEVER_H
#define WHATEVER_H
class my_class{
public:
    my_class();
 
    int status() const;
private:]]></code><skip><![CDATA[
    int _status;
]]></skip><code><![CDATA[
};
#endif
]]></code>
</example>

Using XSLT to Generate the Code

Now I can use a simple xslt proc to generate an actual .hpp file from the XML. The following simply echo’s the contents of any code or skip tag into a text document:

<?xml version="1.0" encoding="ISO-8859-1"?>
 
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes"/>
<xsl:template match="//code">
<xsl:value-of select="."/>
</xsl:template>
 
<xsl:template match="//skip">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>

Using XSLT to Generate the Documentation

So now I need to generate documentation from the example XML. In this case I’m generating docbook, but obviously you can apply this technique to another format if you prefer. I want to produce something like the following:

<?xml version="1.0"?>
<programlisting><![CDATA[
#ifndef WHATEVER_H
#define WHATEVER_H
class my_class{
public:
    my_class();
 
    int status() const;
private:
    ...
};
 
#endif
 
]]></programlisting>

The following XSLT code copies the code data, but replaces the skip portions with ...:

<?xml version="1.0" encoding="ISO-8859-1"?>
 
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" cdata-section-elements="programlisting"/>
<xsl:template match="/">
<programlisting>
<xsl:apply-templates/>
</programlisting>
</xsl:template>
 
<xsl:template match="//code"><xsl:value-of select="." disable-output-escaping="no"/></xsl:template>
 
<xsl:template match="//skip"><![CDATA[...]]></xsl:template>
</xsl:stylesheet>

Integrating with automake


In this case I’m integrating with automake, but obviously you can do a similar thing with just make if you prefer. Basically I want to do the following:

  1. Generate documentation xml file for each example.
  2. Generate cpp and hpp file for each example.
  3. Attempt to compile and link the cpp file.

I’m going to set things up to always generate the xml documentation, but to only try to compile the files on make check rather than make. The following rules should do the trick:

#add all examples to programs built on make check
check_PROGRAMS = $(examples)
 
#clean up all generated files
clean-local:
    rm -rf $(examples:=.cpp) $(examples:=.hpp) 
    rm -rf $(examples:=.cpp.doc.xml) $(examples:=.hpp.doc.xml)
 
#always build the documentation
all: $(examples:=.cpp.doc.xml) $(examples:=.hpp.doc.xml)
 
#assume each example cpp has an associated header
#make the build dependent on the source xml, the header
#and the xslt script
#add an empty main to the cpp file so it will link properly
%.cpp: %.cpp.xml %.hpp $(top_srcdir)/create_code.xsl
    xsltproc $(top_srcdir)/create_code.xsl $*.cpp.xml > $@
    echo "int main(){return 0;}" >> $@
%.hpp: %.hpp.xml $(top_srcdir)/create_code.xsl
    xsltproc $(top_srcdir)/create_code.xsl $*.hpp.xml > $@
%.doc.xml: %.xml $(top_srcdir)/create_db.xsl
    xsltproc $(top_srcdir)/scripts/create_db.xsl $*.xml > $@

Updating your Makefile.am

Assuming my_example.hpp.xml and my_example.cpp.xml you should define the following:

examples = my_example
 
my_example_SOURCES=my_example.cpp

Now make will build my_example.hpp.doc.xml and my_example.cpp.doc.xml and make check will build a program called my_example from my_example.cpp.

Inserting the code into Docbook

The final step in all of this is to insert the generated documentation into your docbook file. This is fairly straight forward with xinclude (NOTE: if using xsltproc you will need to use the --xinclude option):

!DOCTYPE article
  PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
  "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
<article xmlns:xi="http://www.w3.org/2001/XInclude">
 
    <articleinfo>
        <title>Lib Graph User's Guide</title>
        <author>
            <firstname>Michael</firstname>
            <surname>Smit</surname>
        </author>
    </articleinfo>
 
    ....
    <xi:include href="location/of/example/my_example.hpp.doc.xml"/>
    <xi:include href="location/of/example/my_example.cpp.doc.xml"/>
    ....
</article>

The Author

Michael Smit is a software engineer in Seattle, Washington who works for amazon

Comments are off for this post

Comments are closed.