| """Script to generate doxygen documentation. | |
| """ | |
| from __future__ import print_function | |
| from __future__ import unicode_literals | |
| 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, 'r') as f: | |
| contents = f.read() | |
| for (k,v) in list(dict.items()): | |
| v = v.replace('\\','\\\\') | |
| contents = re.sub(k, v, contents) | |
| with open(targetfile, 'w') 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 = subprocess.call(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', options.doxyfile_input_path, subst_keys) | |
| run_doxygen(options.doxygen_path, 'doc/doxyfile', 'doc', is_silent=options.silent) | |
| if not options.silent: | |
| print(open(warning_log_path, 'r').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. | |
| Optionally 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('--in', dest="doxyfile_input_path", action='store', default='doc/doxyfile.in', | |
| help="""Path to doxygen inputs. [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() |