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