blob: 6f7b889bc3c749dcb39bd10e490b2c1cedc2d7c6 [file] [log] [blame]
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +00001"""Script to generate doxygen documentation.
2"""
Christopher Dunnbd1e8952014-11-19 23:30:47 -06003from __future__ import print_function
4from devtools import tarball
Christopher Dunnff5abe72015-01-24 15:54:08 -06005from contextlib import contextmanager
6import subprocess
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +00007import re
8import os
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +00009import sys
10import shutil
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000011
Christopher Dunnff5abe72015-01-24 15:54:08 -060012@contextmanager
13def cd(newdir):
14 """
15 http://stackoverflow.com/questions/431684/how-do-i-cd-in-python
16 """
17 prevdir = os.getcwd()
18 os.chdir(newdir)
19 try:
20 yield
21 finally:
22 os.chdir(prevdir)
23
Baptiste Lepilleur64ba0622010-02-24 23:08:47 +000024def find_program(*filenames):
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000025 """find a program in folders path_lst, and sets env[var]
Baptiste Lepilleur64ba0622010-02-24 23:08:47 +000026 @param filenames: a list of possible names of the program to search for
27 @return: the full path of the filename if found, or '' if filename could not be found
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000028"""
29 paths = os.environ.get('PATH', '').split(os.pathsep)
Christopher Dunn494950a2015-01-24 15:29:52 -060030 suffixes = ('win32' in sys.platform) and '.exe .com .bat .cmd' or ''
Baptiste Lepilleur64ba0622010-02-24 23:08:47 +000031 for filename in filenames:
32 for name in [filename+ext for ext in suffixes.split()]:
33 for directory in paths:
34 full_path = os.path.join(directory, name)
35 if os.path.isfile(full_path):
36 return full_path
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000037 return ''
38
39def do_subst_in_file(targetfile, sourcefile, dict):
40 """Replace all instances of the keys of dict with their values.
41 For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
42 then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
43 """
Christopher Dunnff5abe72015-01-24 15:54:08 -060044 with open(sourcefile, 'rb') as f:
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000045 contents = f.read()
Christopher Dunnbd1e8952014-11-19 23:30:47 -060046 for (k,v) in list(dict.items()):
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000047 v = v.replace('\\','\\\\')
48 contents = re.sub(k, v, contents)
Christopher Dunnff5abe72015-01-24 15:54:08 -060049 with open(targetfile, 'wb') as f:
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000050 f.write(contents)
Christopher Dunnff5abe72015-01-24 15:54:08 -060051
52def getstatusoutput(cmd):
53 """cmd is a list.
54 """
55 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
56 output, _ = process.communicate()
57 status = process.returncode
58 return status, output
59
60def run_cmd(cmd, silent=False):
61 print('Running:', repr(' '.join(cmd)), 'in', repr(os.getcwd()))
62 sys.stdout.flush()
63 if silent:
64 status, output = getstatusoutput(cmd)
65 else:
66 status, output = os.system(' '.join(cmd)), ''
67 if status:
68 msg = 'error=%d, output="""\n%s\n"""' %(status, output)
69 print(msg)
70 #raise Exception(msg)
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000071
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +000072def run_doxygen(doxygen_path, config_file, working_dir, is_silent):
Christopher Dunn494950a2015-01-24 15:29:52 -060073 config_file = os.path.abspath(config_file)
Christopher Dunnff5abe72015-01-24 15:54:08 -060074 with cd(working_dir):
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000075 cmd = [doxygen_path, config_file]
Christopher Dunnff5abe72015-01-24 15:54:08 -060076 run_cmd(cmd, is_silent)
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000077
Christopher Dunn494950a2015-01-24 15:29:52 -060078def build_doc(options, make_release=False):
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +000079 if make_release:
80 options.make_tarball = True
81 options.with_dot = True
82 options.with_html_help = True
83 options.with_uml_look = True
84 options.open = False
85 options.silent = True
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000086
87 version = open('version','rt').read().strip()
Baptiste Lepilleur64ba0622010-02-24 23:08:47 +000088 output_dir = 'dist/doxygen' # relative to doc/doxyfile location.
Christopher Dunn494950a2015-01-24 15:29:52 -060089 if not os.path.isdir(output_dir):
90 os.makedirs(output_dir)
91 top_dir = os.path.abspath('.')
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000092 html_output_dirname = 'jsoncpp-api-html-' + version
Christopher Dunn494950a2015-01-24 15:29:52 -060093 tarball_path = os.path.join('dist', html_output_dirname + '.tar.gz')
94 warning_log_path = os.path.join(output_dir, '../jsoncpp-doxygen-warning.log')
95 html_output_path = os.path.join(output_dir, html_output_dirname)
96 def yesno(bool):
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000097 return bool and 'YES' or 'NO'
98 subst_keys = {
99 '%JSONCPP_VERSION%': version,
100 '%DOC_TOPDIR%': '',
101 '%TOPDIR%': top_dir,
Christopher Dunn494950a2015-01-24 15:29:52 -0600102 '%HTML_OUTPUT%': os.path.join('..', output_dir, html_output_dirname),
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000103 '%HAVE_DOT%': yesno(options.with_dot),
104 '%DOT_PATH%': os.path.split(options.dot_path)[0],
105 '%HTML_HELP%': yesno(options.with_html_help),
106 '%UML_LOOK%': yesno(options.with_uml_look),
Christopher Dunn494950a2015-01-24 15:29:52 -0600107 '%WARNING_LOG_PATH%': os.path.join('..', warning_log_path)
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000108 }
109
Christopher Dunn494950a2015-01-24 15:29:52 -0600110 if os.path.isdir(output_dir):
Christopher Dunnbd1e8952014-11-19 23:30:47 -0600111 print('Deleting directory:', output_dir)
Christopher Dunn494950a2015-01-24 15:29:52 -0600112 shutil.rmtree(output_dir)
113 if not os.path.isdir(output_dir):
114 os.makedirs(output_dir)
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000115
Christopher Dunn494950a2015-01-24 15:29:52 -0600116 do_subst_in_file('doc/doxyfile', 'doc/doxyfile.in', subst_keys)
Christopher Dunnff5abe72015-01-24 15:54:08 -0600117 run_doxygen(options.doxygen_path, 'doc/doxyfile', 'doc', is_silent=options.silent)
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +0000118 if not options.silent:
Christopher Dunnbd1e8952014-11-19 23:30:47 -0600119 print(open(warning_log_path, 'rb').read())
Christopher Dunn9dd7eea2014-07-05 12:37:27 -0700120 index_path = os.path.abspath(os.path.join('doc', subst_keys['%HTML_OUTPUT%'], 'index.html'))
Christopher Dunnbd1e8952014-11-19 23:30:47 -0600121 print('Generated documentation can be found in:')
122 print(index_path)
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000123 if options.open:
124 import webbrowser
Christopher Dunn494950a2015-01-24 15:29:52 -0600125 webbrowser.open('file://' + index_path)
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000126 if options.make_tarball:
Christopher Dunnbd1e8952014-11-19 23:30:47 -0600127 print('Generating doc tarball to', tarball_path)
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000128 tarball_sources = [
Baptiste Lepilleur64ba0622010-02-24 23:08:47 +0000129 output_dir,
Christopher Dunnf4bc0bf2015-01-24 15:43:23 -0600130 'README.md',
Baptiste Lepilleur7469f1d2010-04-20 21:35:19 +0000131 'LICENSE',
Baptiste Lepilleur130730f2010-03-13 11:14:49 +0000132 'NEWS.txt',
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000133 'version'
134 ]
Christopher Dunn494950a2015-01-24 15:29:52 -0600135 tarball_basedir = os.path.join(output_dir, html_output_dirname)
136 tarball.make_tarball(tarball_path, tarball_sources, tarball_basedir, html_output_dirname)
Baptiste Lepilleur64ba0622010-02-24 23:08:47 +0000137 return tarball_path, html_output_dirname
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000138
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +0000139def main():
140 usage = """%prog
141 Generates doxygen documentation in build/doxygen.
142 Optionaly makes a tarball of the documentation to dist/.
143
144 Must be started in the project top directory.
145 """
146 from optparse import OptionParser
147 parser = OptionParser(usage=usage)
148 parser.allow_interspersed_args = False
149 parser.add_option('--with-dot', dest="with_dot", action='store_true', default=False,
150 help="""Enable usage of DOT to generate collaboration diagram""")
151 parser.add_option('--dot', dest="dot_path", action='store', default=find_program('dot'),
152 help="""Path to GraphViz dot tool. Must be full qualified path. [Default: %default]""")
153 parser.add_option('--doxygen', dest="doxygen_path", action='store', default=find_program('doxygen'),
154 help="""Path to Doxygen tool. [Default: %default]""")
155 parser.add_option('--with-html-help', dest="with_html_help", action='store_true', default=False,
156 help="""Enable generation of Microsoft HTML HELP""")
157 parser.add_option('--no-uml-look', dest="with_uml_look", action='store_false', default=True,
158 help="""Generates DOT graph without UML look [Default: False]""")
159 parser.add_option('--open', dest="open", action='store_true', default=False,
160 help="""Open the HTML index in the web browser after generation""")
161 parser.add_option('--tarball', dest="make_tarball", action='store_true', default=False,
162 help="""Generates a tarball of the documentation in dist/ directory""")
163 parser.add_option('-s', '--silent', dest="silent", action='store_true', default=False,
164 help="""Hides doxygen output""")
165 parser.enable_interspersed_args()
166 options, args = parser.parse_args()
Christopher Dunn494950a2015-01-24 15:29:52 -0600167 build_doc(options)
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +0000168
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000169if __name__ == '__main__':
170 main()