"""Script to generate doxygen documentation. """ from __future__ import print_function from devtools import tarball from contextlib import contextmanager import subprocess import traceback import re import os import sys import shutil @contextmanager def cd(newdir): """ http://stackoverflow.com/questions/431684/how-do-i-cd-in-python """ prevdir = os.getcwd() os.chdir(newdir) try: yield finally: os.chdir(prevdir) def find_program(*filenames): """find a program in folders path_lst, and sets env[var] @param filenames: a list of possible names of the program to search for @return: the full path of the filename if found, 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 filename in filenames: 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. """ with open(sourcefile, 'rb') as f: contents = f.read() for (k,v) in list(dict.items()): v = v.replace('\\','\\\\') contents = re.sub(k, v, contents) with open(targetfile, 'wb') as f: f.write(contents) def getstatusoutput(cmd): """cmd is a list. """ try: process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output, _ = process.communicate() status = process.returncode except: status = -1 output = traceback.format_exc() return status, output def run_cmd(cmd, silent=False): """Raise exception on failure. """ info = 'Running: %r in %r' %(' '.join(cmd), os.getcwd()) print(info) sys.stdout.flush() if silent: status, output = getstatusoutput(cmd) else: status, output = os.system(' '.join(cmd)), '' if status: msg = 'Error while %s ...\n\terror=%d, output="""%s"""' %(info, status, output) raise Exception(msg) def assert_is_exe(path): if not path: raise Exception('path is empty.') if not os.path.isfile(path): raise Exception('%r is not a file.' %path) if not os.access(path, os.X_OK): raise Exception('%r is not executable by this user.' %path) def run_doxygen(doxygen_path, config_file, working_dir, is_silent): assert_is_exe(doxygen_path) config_file = os.path.abspath(config_file) with cd(working_dir): cmd = [doxygen_path, config_file] run_cmd(cmd, is_silent) def build_doc(options, make_release=False): if make_release: options.make_tarball = True options.with_dot = True options.with_html_help = True options.with_uml_look = True options.open = False options.silent = True version = open('version','rt').read().strip() output_dir = 'dist/doxygen' # relative to doc/doxyfile location. if not os.path.isdir(output_dir): os.makedirs(output_dir) 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') html_output_path = os.path.join(output_dir, html_output_dirname) 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%': os.path.join('..', warning_log_path) } if os.path.isdir(output_dir): print('Deleting directory:', output_dir) shutil.rmtree(output_dir) if not os.path.isdir(output_dir): os.makedirs(output_dir) do_subst_in_file('doc/doxyfile', 'doc/doxyfile.in', subst_keys) run_doxygen(options.doxygen_path, 'doc/doxyfile', 'doc', is_silent=options.silent) if not options.silent: print(open(warning_log_path, 'rb').read()) index_path = os.path.abspath(os.path.join('doc', 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 = [ output_dir, 'README.md', 'LICENSE', 'NEWS.txt', 'version' ] tarball_basedir = os.path.join(output_dir, html_output_dirname) tarball.make_tarball(tarball_path, tarball_sources, tarball_basedir, html_output_dirname) return tarball_path, html_output_dirname 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.add_option('-s', '--silent', dest="silent", action='store_true', default=False, help="""Hides doxygen output""") parser.enable_interspersed_args() options, args = parser.parse_args() build_doc(options) if __name__ == '__main__': main()