Hey there! I’ve recently just started working with Cake Solutions, Scala and SBT. One of my first tasks was to improve the documentation in Specs2 Spring.
SBT and DocBook
As part of our Specs2 Spring project, we wanted to ensure users had access to Spec2 Spring’s documentation in a format they know and love, we figured we’d use DocBook, the multiformat-generating, markup language.
Fortunately a SBT plugin currently exists for DocBook, however it currently doesn’t have a few features we need.
We have forked the SBT DocBook plugin, because we needed to add support for XInclude, syntax highlighting, ePUB and many other improvements. Find it here.
The Setup
Using it is pretty straight-forward. You should have a directory structure similar to this:
MyProject
build.sbt
src/
main/
java/
scala/
docbook/
main.xml
chapter1.xml
...
project/
plugins.sbt
Simply ensure all DocBook files are in the src/main/docbook directory. Note that if you have multiple DocBook files, the main file must be named main.xml. On the other hand, if there is just one file, feel free to name it anything you like.
Include the plugin in your project/plugins.sbt file as follows:
resolvers += ScalaToolsSnapshots
addSbtPlugin("de.undercouch" % "sbt-docbook-plugin" % "0.2-SNAPSHOT")
That’s it!
Run it!
Generating documents in the format of your choice is pretty easy:
$ sbt html
$ sbt pdf
$ sbt xhtml
$ sbt manpage
$ sbt eclipse-help
Checkout the readme for more options.
How have we done it?
Robust documentation is key to getting the community interested and involved in any project and modular documentation is key to the sanity of the project developers. An easy way to produce modular documentation is by using XInclude, sadly the plugin it uses, Saxon 6.5.3, for processing XSLT, cannot handle XInclude.
After extensive googling & hacking, I eventually turned to Xerces2 to resolve XInclude. The code looks a little something like this:
private def transformDocBook(src: File, dst: File, styleSheet: String,
cp: Classpath, log: Logger) {
transform(src, dst, log) {
//write output to temporary file. this avoids a bug in Saxon
//that caused spaces in the output filename to be converted to
//an escaped sequence (%20)
val temp = File.createTempFile("sbt-docbook-plugin-", ".fo")
val code = Fork.java(None, Seq[String](
"-cp", cp.files.mkString(File.pathSeparator),
"-Djavax.xml.parsers.DocumentBuilderFactory="+
"org.apache.xerces.jaxp.DocumentBuilderFactoryImpl",
"-Djavax.xml.parsers.SAXParserFactory=" +
"org.apache.xerces.jaxp.SAXParserFactoryImpl",
"-Dorg.apache.xerces.xni.parser.XMLParserConfiguration=" +
"org.apache.xerces.parsers.XIncludeParserConfiguration",
"com.icl.saxon.StyleSheet",
"-o", temp.toString,
src.toString, styleSheet
), log)
//copy temporary file to real output file
temp #> dst !
//delete temporary file (if this files it will be deleted on exit)
if (!temp.delete()) {
temp.deleteOnExit()
}
code
}
}
So what’s going on here? Well Fork.java makes it possible for us to run a separate java process, "-cp" sets up it’s classpath, cp.files.mkString(File.pathSeparator) ensures that the correct path separator is used for your file system, the first 2 -D options set Xerces as Saxon’s xml parser, the last -D option turns on the XInclude feature and -o set’s Saxon’s output file or directory!
Maintaining Specs2 Spring and any other documentation has been made a little easier!
The next focus will be on improving the plugin’s output for documentation split into multiple files, in particular EPUB..

Pingback: This week in #Scala (27/01/2012) « Cake Solutions Team Blog