1*44844408SAndroid Build Coastguard Worker"""Script to generate doxygen documentation. 2*44844408SAndroid Build Coastguard Worker""" 3*44844408SAndroid Build Coastguard Workerfrom __future__ import print_function 4*44844408SAndroid Build Coastguard Workerfrom __future__ import unicode_literals 5*44844408SAndroid Build Coastguard Workerfrom devtools import tarball 6*44844408SAndroid Build Coastguard Workerfrom contextlib import contextmanager 7*44844408SAndroid Build Coastguard Workerimport subprocess 8*44844408SAndroid Build Coastguard Workerimport traceback 9*44844408SAndroid Build Coastguard Workerimport re 10*44844408SAndroid Build Coastguard Workerimport os 11*44844408SAndroid Build Coastguard Workerimport sys 12*44844408SAndroid Build Coastguard Workerimport shutil 13*44844408SAndroid Build Coastguard Worker 14*44844408SAndroid Build Coastguard Worker@contextmanager 15*44844408SAndroid Build Coastguard Workerdef cd(newdir): 16*44844408SAndroid Build Coastguard Worker """ 17*44844408SAndroid Build Coastguard Worker http://stackoverflow.com/questions/431684/how-do-i-cd-in-python 18*44844408SAndroid Build Coastguard Worker """ 19*44844408SAndroid Build Coastguard Worker prevdir = os.getcwd() 20*44844408SAndroid Build Coastguard Worker os.chdir(newdir) 21*44844408SAndroid Build Coastguard Worker try: 22*44844408SAndroid Build Coastguard Worker yield 23*44844408SAndroid Build Coastguard Worker finally: 24*44844408SAndroid Build Coastguard Worker os.chdir(prevdir) 25*44844408SAndroid Build Coastguard Worker 26*44844408SAndroid Build Coastguard Workerdef find_program(*filenames): 27*44844408SAndroid Build Coastguard Worker """find a program in folders path_lst, and sets env[var] 28*44844408SAndroid Build Coastguard Worker @param filenames: a list of possible names of the program to search for 29*44844408SAndroid Build Coastguard Worker @return: the full path of the filename if found, or '' if filename could not be found 30*44844408SAndroid Build Coastguard Worker""" 31*44844408SAndroid Build Coastguard Worker paths = os.environ.get('PATH', '').split(os.pathsep) 32*44844408SAndroid Build Coastguard Worker suffixes = ('win32' in sys.platform) and '.exe .com .bat .cmd' or '' 33*44844408SAndroid Build Coastguard Worker for filename in filenames: 34*44844408SAndroid Build Coastguard Worker for name in [filename+ext for ext in suffixes.split(' ')]: 35*44844408SAndroid Build Coastguard Worker for directory in paths: 36*44844408SAndroid Build Coastguard Worker full_path = os.path.join(directory, name) 37*44844408SAndroid Build Coastguard Worker if os.path.isfile(full_path): 38*44844408SAndroid Build Coastguard Worker return full_path 39*44844408SAndroid Build Coastguard Worker return '' 40*44844408SAndroid Build Coastguard Worker 41*44844408SAndroid Build Coastguard Workerdef do_subst_in_file(targetfile, sourcefile, dict): 42*44844408SAndroid Build Coastguard Worker """Replace all instances of the keys of dict with their values. 43*44844408SAndroid Build Coastguard Worker For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'}, 44*44844408SAndroid Build Coastguard Worker then all instances of %VERSION% in the file will be replaced with 1.2345 etc. 45*44844408SAndroid Build Coastguard Worker """ 46*44844408SAndroid Build Coastguard Worker with open(sourcefile, 'r') as f: 47*44844408SAndroid Build Coastguard Worker contents = f.read() 48*44844408SAndroid Build Coastguard Worker for (k,v) in list(dict.items()): 49*44844408SAndroid Build Coastguard Worker v = v.replace('\\','\\\\') 50*44844408SAndroid Build Coastguard Worker contents = re.sub(k, v, contents) 51*44844408SAndroid Build Coastguard Worker with open(targetfile, 'w') as f: 52*44844408SAndroid Build Coastguard Worker f.write(contents) 53*44844408SAndroid Build Coastguard Worker 54*44844408SAndroid Build Coastguard Workerdef getstatusoutput(cmd): 55*44844408SAndroid Build Coastguard Worker """cmd is a list. 56*44844408SAndroid Build Coastguard Worker """ 57*44844408SAndroid Build Coastguard Worker try: 58*44844408SAndroid Build Coastguard Worker process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 59*44844408SAndroid Build Coastguard Worker output, _ = process.communicate() 60*44844408SAndroid Build Coastguard Worker status = process.returncode 61*44844408SAndroid Build Coastguard Worker except: 62*44844408SAndroid Build Coastguard Worker status = -1 63*44844408SAndroid Build Coastguard Worker output = traceback.format_exc() 64*44844408SAndroid Build Coastguard Worker return status, output 65*44844408SAndroid Build Coastguard Worker 66*44844408SAndroid Build Coastguard Workerdef run_cmd(cmd, silent=False): 67*44844408SAndroid Build Coastguard Worker """Raise exception on failure. 68*44844408SAndroid Build Coastguard Worker """ 69*44844408SAndroid Build Coastguard Worker info = 'Running: %r in %r' %(' '.join(cmd), os.getcwd()) 70*44844408SAndroid Build Coastguard Worker print(info) 71*44844408SAndroid Build Coastguard Worker sys.stdout.flush() 72*44844408SAndroid Build Coastguard Worker if silent: 73*44844408SAndroid Build Coastguard Worker status, output = getstatusoutput(cmd) 74*44844408SAndroid Build Coastguard Worker else: 75*44844408SAndroid Build Coastguard Worker status, output = subprocess.call(cmd), '' 76*44844408SAndroid Build Coastguard Worker if status: 77*44844408SAndroid Build Coastguard Worker msg = 'Error while %s ...\n\terror=%d, output="""%s"""' %(info, status, output) 78*44844408SAndroid Build Coastguard Worker raise Exception(msg) 79*44844408SAndroid Build Coastguard Worker 80*44844408SAndroid Build Coastguard Workerdef assert_is_exe(path): 81*44844408SAndroid Build Coastguard Worker if not path: 82*44844408SAndroid Build Coastguard Worker raise Exception('path is empty.') 83*44844408SAndroid Build Coastguard Worker if not os.path.isfile(path): 84*44844408SAndroid Build Coastguard Worker raise Exception('%r is not a file.' %path) 85*44844408SAndroid Build Coastguard Worker if not os.access(path, os.X_OK): 86*44844408SAndroid Build Coastguard Worker raise Exception('%r is not executable by this user.' %path) 87*44844408SAndroid Build Coastguard Worker 88*44844408SAndroid Build Coastguard Workerdef run_doxygen(doxygen_path, config_file, working_dir, is_silent): 89*44844408SAndroid Build Coastguard Worker assert_is_exe(doxygen_path) 90*44844408SAndroid Build Coastguard Worker config_file = os.path.abspath(config_file) 91*44844408SAndroid Build Coastguard Worker with cd(working_dir): 92*44844408SAndroid Build Coastguard Worker cmd = [doxygen_path, config_file] 93*44844408SAndroid Build Coastguard Worker run_cmd(cmd, is_silent) 94*44844408SAndroid Build Coastguard Worker 95*44844408SAndroid Build Coastguard Workerdef build_doc(options, make_release=False): 96*44844408SAndroid Build Coastguard Worker if make_release: 97*44844408SAndroid Build Coastguard Worker options.make_tarball = True 98*44844408SAndroid Build Coastguard Worker options.with_dot = True 99*44844408SAndroid Build Coastguard Worker options.with_html_help = True 100*44844408SAndroid Build Coastguard Worker options.with_uml_look = True 101*44844408SAndroid Build Coastguard Worker options.open = False 102*44844408SAndroid Build Coastguard Worker options.silent = True 103*44844408SAndroid Build Coastguard Worker 104*44844408SAndroid Build Coastguard Worker version = open('version', 'rt').read().strip() 105*44844408SAndroid Build Coastguard Worker output_dir = 'dist/doxygen' # relative to doc/doxyfile location. 106*44844408SAndroid Build Coastguard Worker if not os.path.isdir(output_dir): 107*44844408SAndroid Build Coastguard Worker os.makedirs(output_dir) 108*44844408SAndroid Build Coastguard Worker top_dir = os.path.abspath('.') 109*44844408SAndroid Build Coastguard Worker html_output_dirname = 'jsoncpp-api-html-' + version 110*44844408SAndroid Build Coastguard Worker tarball_path = os.path.join('dist', html_output_dirname + '.tar.gz') 111*44844408SAndroid Build Coastguard Worker warning_log_path = os.path.join(output_dir, '../jsoncpp-doxygen-warning.log') 112*44844408SAndroid Build Coastguard Worker html_output_path = os.path.join(output_dir, html_output_dirname) 113*44844408SAndroid Build Coastguard Worker def yesno(bool): 114*44844408SAndroid Build Coastguard Worker return bool and 'YES' or 'NO' 115*44844408SAndroid Build Coastguard Worker subst_keys = { 116*44844408SAndroid Build Coastguard Worker '%JSONCPP_VERSION%': version, 117*44844408SAndroid Build Coastguard Worker '%DOC_TOPDIR%': '', 118*44844408SAndroid Build Coastguard Worker '%TOPDIR%': top_dir, 119*44844408SAndroid Build Coastguard Worker '%HTML_OUTPUT%': os.path.join('..', output_dir, html_output_dirname), 120*44844408SAndroid Build Coastguard Worker '%HAVE_DOT%': yesno(options.with_dot), 121*44844408SAndroid Build Coastguard Worker '%DOT_PATH%': os.path.split(options.dot_path)[0], 122*44844408SAndroid Build Coastguard Worker '%HTML_HELP%': yesno(options.with_html_help), 123*44844408SAndroid Build Coastguard Worker '%UML_LOOK%': yesno(options.with_uml_look), 124*44844408SAndroid Build Coastguard Worker '%WARNING_LOG_PATH%': os.path.join('..', warning_log_path) 125*44844408SAndroid Build Coastguard Worker } 126*44844408SAndroid Build Coastguard Worker 127*44844408SAndroid Build Coastguard Worker if os.path.isdir(output_dir): 128*44844408SAndroid Build Coastguard Worker print('Deleting directory:', output_dir) 129*44844408SAndroid Build Coastguard Worker shutil.rmtree(output_dir) 130*44844408SAndroid Build Coastguard Worker if not os.path.isdir(output_dir): 131*44844408SAndroid Build Coastguard Worker os.makedirs(output_dir) 132*44844408SAndroid Build Coastguard Worker 133*44844408SAndroid Build Coastguard Worker do_subst_in_file('doc/doxyfile', options.doxyfile_input_path, subst_keys) 134*44844408SAndroid Build Coastguard Worker run_doxygen(options.doxygen_path, 'doc/doxyfile', 'doc', is_silent=options.silent) 135*44844408SAndroid Build Coastguard Worker if not options.silent: 136*44844408SAndroid Build Coastguard Worker print(open(warning_log_path, 'r').read()) 137*44844408SAndroid Build Coastguard Worker index_path = os.path.abspath(os.path.join('doc', subst_keys['%HTML_OUTPUT%'], 'index.html')) 138*44844408SAndroid Build Coastguard Worker print('Generated documentation can be found in:') 139*44844408SAndroid Build Coastguard Worker print(index_path) 140*44844408SAndroid Build Coastguard Worker if options.open: 141*44844408SAndroid Build Coastguard Worker import webbrowser 142*44844408SAndroid Build Coastguard Worker webbrowser.open('file://' + index_path) 143*44844408SAndroid Build Coastguard Worker if options.make_tarball: 144*44844408SAndroid Build Coastguard Worker print('Generating doc tarball to', tarball_path) 145*44844408SAndroid Build Coastguard Worker tarball_sources = [ 146*44844408SAndroid Build Coastguard Worker output_dir, 147*44844408SAndroid Build Coastguard Worker 'README.md', 148*44844408SAndroid Build Coastguard Worker 'LICENSE', 149*44844408SAndroid Build Coastguard Worker 'NEWS.txt', 150*44844408SAndroid Build Coastguard Worker 'version' 151*44844408SAndroid Build Coastguard Worker ] 152*44844408SAndroid Build Coastguard Worker tarball_basedir = os.path.join(output_dir, html_output_dirname) 153*44844408SAndroid Build Coastguard Worker tarball.make_tarball(tarball_path, tarball_sources, tarball_basedir, html_output_dirname) 154*44844408SAndroid Build Coastguard Worker return tarball_path, html_output_dirname 155*44844408SAndroid Build Coastguard Worker 156*44844408SAndroid Build Coastguard Workerdef main(): 157*44844408SAndroid Build Coastguard Worker usage = """%prog 158*44844408SAndroid Build Coastguard Worker Generates doxygen documentation in build/doxygen. 159*44844408SAndroid Build Coastguard Worker Optionally makes a tarball of the documentation to dist/. 160*44844408SAndroid Build Coastguard Worker 161*44844408SAndroid Build Coastguard Worker Must be started in the project top directory. 162*44844408SAndroid Build Coastguard Worker """ 163*44844408SAndroid Build Coastguard Worker from optparse import OptionParser 164*44844408SAndroid Build Coastguard Worker parser = OptionParser(usage=usage) 165*44844408SAndroid Build Coastguard Worker parser.allow_interspersed_args = False 166*44844408SAndroid Build Coastguard Worker parser.add_option('--with-dot', dest="with_dot", action='store_true', default=False, 167*44844408SAndroid Build Coastguard Worker help="""Enable usage of DOT to generate collaboration diagram""") 168*44844408SAndroid Build Coastguard Worker parser.add_option('--dot', dest="dot_path", action='store', default=find_program('dot'), 169*44844408SAndroid Build Coastguard Worker help="""Path to GraphViz dot tool. Must be full qualified path. [Default: %default]""") 170*44844408SAndroid Build Coastguard Worker parser.add_option('--doxygen', dest="doxygen_path", action='store', default=find_program('doxygen'), 171*44844408SAndroid Build Coastguard Worker help="""Path to Doxygen tool. [Default: %default]""") 172*44844408SAndroid Build Coastguard Worker parser.add_option('--in', dest="doxyfile_input_path", action='store', default='doc/doxyfile.in', 173*44844408SAndroid Build Coastguard Worker help="""Path to doxygen inputs. [Default: %default]""") 174*44844408SAndroid Build Coastguard Worker parser.add_option('--with-html-help', dest="with_html_help", action='store_true', default=False, 175*44844408SAndroid Build Coastguard Worker help="""Enable generation of Microsoft HTML HELP""") 176*44844408SAndroid Build Coastguard Worker parser.add_option('--no-uml-look', dest="with_uml_look", action='store_false', default=True, 177*44844408SAndroid Build Coastguard Worker help="""Generates DOT graph without UML look [Default: False]""") 178*44844408SAndroid Build Coastguard Worker parser.add_option('--open', dest="open", action='store_true', default=False, 179*44844408SAndroid Build Coastguard Worker help="""Open the HTML index in the web browser after generation""") 180*44844408SAndroid Build Coastguard Worker parser.add_option('--tarball', dest="make_tarball", action='store_true', default=False, 181*44844408SAndroid Build Coastguard Worker help="""Generates a tarball of the documentation in dist/ directory""") 182*44844408SAndroid Build Coastguard Worker parser.add_option('-s', '--silent', dest="silent", action='store_true', default=False, 183*44844408SAndroid Build Coastguard Worker help="""Hides doxygen output""") 184*44844408SAndroid Build Coastguard Worker parser.enable_interspersed_args() 185*44844408SAndroid Build Coastguard Worker options, args = parser.parse_args() 186*44844408SAndroid Build Coastguard Worker build_doc(options) 187*44844408SAndroid Build Coastguard Worker 188*44844408SAndroid Build Coastguard Workerif __name__ == '__main__': 189*44844408SAndroid Build Coastguard Worker main() 190