- Documentation generation is no longer handled by SCons. The script doxybuild.py is used to generate the documentation on demand.
- Added file 'version' that contains jsoncpp version number. It is used by both SConstruct and doxybuild.py.
- Updated README.txt with documentation build instruction, and instructions to add a test case.


diff --git a/doxybuild.py b/doxybuild.py
new file mode 100644
index 0000000..445de4b
--- /dev/null
+++ b/doxybuild.py
@@ -0,0 +1,191 @@
+"""Script to generate doxygen documentation.
+"""
+
+import re
+import os
+import os.path
+import sys
+import shutil
+import gzip
+import tarfile
+
+TARGZ_DEFAULT_COMPRESSION_LEVEL = 9
+
+def make_tarball(tarball_path, sources, base_dir, prefix_dir=''):
+    """Parameters:
+    tarball_path: output path of the .tar.gz file
+    sources: list of sources to include in the tarball, relative to the current directory
+    base_dir: if a source file is in a sub-directory of base_dir, then base_dir is stripped
+        from path in the tarball.
+    prefix_dir: all files stored in the tarball be sub-directory of prefix_dir. Set to ''
+        to make them child of root.
+    """
+    base_dir = os.path.normpath( os.path.abspath( base_dir ) )
+    def archive_name( path ):
+        """Makes path relative to base_dir."""
+        path = os.path.normpath( os.path.abspath( path ) )
+        common_path = os.path.commonprefix( (base_dir, path) )
+        archive_name = path[len(common_path):]
+        if os.path.isabs( archive_name ):
+            archive_name = archive_name[1:]
+        return os.path.join( prefix_dir, archive_name )
+    def visit(tar, dirname, names):
+        for name in names:
+            path = os.path.join(dirname, name)
+            if os.path.isfile(path):
+                path_in_tar = archive_name(path)
+                tar.add(path, path_in_tar )
+    compression = TARGZ_DEFAULT_COMPRESSION_LEVEL
+    fileobj = gzip.GzipFile( tarball_path, 'wb', compression )
+    tar = tarfile.TarFile(os.path.splitext(tarball_path)[0], 'w', fileobj)
+    for source in sources:
+        source_path = source
+        if os.path.isdir( source ):
+            os.path.walk(source_path, visit, tar)
+        else:
+            path_in_tar = archive_name(source_path)
+            tar.add(source_path, path_in_tar )      # filename, arcname
+    tar.close()
+
+
+def find_program(filename):
+    """find a program in folders path_lst, and sets env[var]
+    @param env: environmentA
+    @param filename: name of the program to search for
+    @param path_list: list of directories to search for filename
+    @param var: environment value to be checked for in env or os.environ
+    @return: either the value that is referenced with [var] in env or os.environ
+    or the first occurrence filename or '' if filename could not be found
+"""
+    paths = os.environ.get('PATH', '').split(os.pathsep)
+    suffixes = ('win32' in sys.platform ) and '.exe .com .bat .cmd' or ''
+    for name in [filename+ext for ext in suffixes.split()]:
+        for directory in paths:
+            full_path = os.path.join(directory, name)
+            if os.path.isfile(full_path):
+                return full_path
+    return ''
+
+def do_subst_in_file(targetfile, sourcefile, dict):
+    """Replace all instances of the keys of dict with their values.
+    For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
+    then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
+    """
+    try:
+        f = open(sourcefile, 'rb')
+        contents = f.read()
+        f.close()
+    except:
+        print "Can't read source file %s"%sourcefile
+        raise
+    for (k,v) in dict.items():
+        v = v.replace('\\','\\\\') 
+        contents = re.sub(k, v, contents)
+    try:
+        f = open(targetfile, 'wb')
+        f.write(contents)
+        f.close()
+    except:
+        print "Can't write target file %s"%targetfile
+        raise
+
+def run_doxygen(doxygen_path, config_file, working_dir):
+    config_file = os.path.abspath( config_file )
+    doxygen_path = doxygen_path
+    old_cwd = os.getcwd()
+    try:
+        os.chdir( working_dir )
+        cmd = [doxygen_path, config_file]
+        print ' '.join( cmd )
+        try:
+            import subprocess
+        except:
+            if os.system( ' '.join( cmd ) ) != 0:
+                print 'Documentation generation failed'
+                return False
+        else:
+            try:
+                subprocess.check_call( cmd )
+            except subprocess.CalledProcessError:
+                return False
+        return True
+    finally:
+        os.chdir( old_cwd )
+
+def main():
+    usage = """%prog
+    Generates doxygen documentation in build/doxygen.
+    Optionaly makes a tarball of the documentation to dist/.
+
+    Must be started in the project top directory.    
+    """
+    from optparse import OptionParser
+    parser = OptionParser(usage=usage)
+    parser.allow_interspersed_args = False
+    parser.add_option('--with-dot', dest="with_dot", action='store_true', default=False,
+        help="""Enable usage of DOT to generate collaboration diagram""")
+    parser.add_option('--dot', dest="dot_path", action='store', default=find_program('dot'),
+        help="""Path to GraphViz dot tool. Must be full qualified path. [Default: %default]""")
+    parser.add_option('--doxygen', dest="doxygen_path", action='store', default=find_program('doxygen'),
+        help="""Path to Doxygen tool. [Default: %default]""")
+    parser.add_option('--with-html-help', dest="with_html_help", action='store_true', default=False,
+        help="""Enable generation of Microsoft HTML HELP""")
+    parser.add_option('--no-uml-look', dest="with_uml_look", action='store_false', default=True,
+        help="""Generates DOT graph without UML look [Default: False]""")
+    parser.add_option('--open', dest="open", action='store_true', default=False,
+        help="""Open the HTML index in the web browser after generation""")
+    parser.add_option('--tarball', dest="make_tarball", action='store_true', default=False,
+        help="""Generates a tarball of the documentation in dist/ directory""")
+    parser.enable_interspersed_args()
+    options, args = parser.parse_args()
+
+    version = open('version','rt').read().strip()
+    output_dir = '../build/doxygen' # relative to doc/doxyfile location.
+    top_dir = os.path.abspath( '.' )
+    html_output_dirname = 'jsoncpp-api-html-' + version
+    tarball_path = os.path.join( 'dist', html_output_dirname + '.tar.gz' )
+    warning_log_path = os.path.join( output_dir, '../jsoncpp-doxygen-warning.log' )
+    def yesno( bool ):
+        return bool and 'YES' or 'NO'
+    subst_keys = {
+        '%JSONCPP_VERSION%': version,
+        '%DOC_TOPDIR%': '',
+        '%TOPDIR%': top_dir,
+        '%HTML_OUTPUT%': os.path.join( output_dir, html_output_dirname ),
+        '%HAVE_DOT%': yesno(options.with_dot),
+        '%DOT_PATH%': os.path.split(options.dot_path)[0],
+        '%HTML_HELP%': yesno(options.with_html_help),
+        '%UML_LOOK%': yesno(options.with_uml_look),
+        '%WARNING_LOG_PATH%': warning_log_path
+        }
+
+    full_output_dir = os.path.join( 'doc', output_dir )
+    if os.path.isdir( full_output_dir ):
+        print 'Deleting directory:', full_output_dir
+        shutil.rmtree( full_output_dir )
+    if not os.path.isdir( full_output_dir ):
+        os.makedirs( full_output_dir )
+
+    do_subst_in_file( 'doc/doxyfile', 'doc/doxyfile.in', subst_keys )
+    ok = run_doxygen( options.doxygen_path, 'doc/doxyfile', 'doc' )
+    print open(os.path.join('doc', warning_log_path), 'rb').read()
+    if not ok:
+        print 'Doxygen generation failed'
+    index_path = os.path.abspath(os.path.join(subst_keys['%HTML_OUTPUT%'], 'index.html'))
+    print 'Generated documentation can be found in:'
+    print index_path
+    if options.open:
+        import webbrowser
+        webbrowser.open( 'file://' + index_path )
+    if options.make_tarball:
+        print 'Generating doc tarball to', tarball_path
+        tarball_sources = [
+            full_output_dir,
+            'README.txt',
+            'version'
+            ]
+        tarball_basedir = os.path.join( full_output_dir, html_output_dirname )
+        make_tarball( tarball_path, tarball_sources, tarball_basedir, html_output_dirname )
+
+if __name__ == '__main__':
+    main()