blob: 82bdea61a3a5d83c300bf20feffe4e5289e27ea0 [file] [log] [blame]
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +00001"""Script to generate doxygen documentation.
2"""
3
4import re
5import os
6import os.path
7import sys
8import shutil
9import gzip
10import tarfile
11
12TARGZ_DEFAULT_COMPRESSION_LEVEL = 9
13
14def make_tarball(tarball_path, sources, base_dir, prefix_dir=''):
15 """Parameters:
16 tarball_path: output path of the .tar.gz file
17 sources: list of sources to include in the tarball, relative to the current directory
18 base_dir: if a source file is in a sub-directory of base_dir, then base_dir is stripped
19 from path in the tarball.
20 prefix_dir: all files stored in the tarball be sub-directory of prefix_dir. Set to ''
21 to make them child of root.
22 """
23 base_dir = os.path.normpath( os.path.abspath( base_dir ) )
24 def archive_name( path ):
25 """Makes path relative to base_dir."""
26 path = os.path.normpath( os.path.abspath( path ) )
27 common_path = os.path.commonprefix( (base_dir, path) )
28 archive_name = path[len(common_path):]
29 if os.path.isabs( archive_name ):
30 archive_name = archive_name[1:]
31 return os.path.join( prefix_dir, archive_name )
32 def visit(tar, dirname, names):
33 for name in names:
34 path = os.path.join(dirname, name)
35 if os.path.isfile(path):
36 path_in_tar = archive_name(path)
37 tar.add(path, path_in_tar )
38 compression = TARGZ_DEFAULT_COMPRESSION_LEVEL
39 fileobj = gzip.GzipFile( tarball_path, 'wb', compression )
40 tar = tarfile.TarFile(os.path.splitext(tarball_path)[0], 'w', fileobj)
41 for source in sources:
42 source_path = source
43 if os.path.isdir( source ):
44 os.path.walk(source_path, visit, tar)
45 else:
46 path_in_tar = archive_name(source_path)
47 tar.add(source_path, path_in_tar ) # filename, arcname
48 tar.close()
49
50
51def find_program(filename):
52 """find a program in folders path_lst, and sets env[var]
53 @param env: environmentA
54 @param filename: name of the program to search for
55 @param path_list: list of directories to search for filename
56 @param var: environment value to be checked for in env or os.environ
57 @return: either the value that is referenced with [var] in env or os.environ
58 or the first occurrence filename or '' if filename could not be found
59"""
60 paths = os.environ.get('PATH', '').split(os.pathsep)
61 suffixes = ('win32' in sys.platform ) and '.exe .com .bat .cmd' or ''
62 for name in [filename+ext for ext in suffixes.split()]:
63 for directory in paths:
64 full_path = os.path.join(directory, name)
65 if os.path.isfile(full_path):
66 return full_path
67 return ''
68
69def do_subst_in_file(targetfile, sourcefile, dict):
70 """Replace all instances of the keys of dict with their values.
71 For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
72 then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
73 """
74 try:
75 f = open(sourcefile, 'rb')
76 contents = f.read()
77 f.close()
78 except:
79 print "Can't read source file %s"%sourcefile
80 raise
81 for (k,v) in dict.items():
82 v = v.replace('\\','\\\\')
83 contents = re.sub(k, v, contents)
84 try:
85 f = open(targetfile, 'wb')
86 f.write(contents)
87 f.close()
88 except:
89 print "Can't write target file %s"%targetfile
90 raise
91
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +000092def run_doxygen(doxygen_path, config_file, working_dir, is_silent):
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +000093 config_file = os.path.abspath( config_file )
94 doxygen_path = doxygen_path
95 old_cwd = os.getcwd()
96 try:
97 os.chdir( working_dir )
98 cmd = [doxygen_path, config_file]
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +000099 print 'Running:', ' '.join( cmd )
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000100 try:
101 import subprocess
102 except:
103 if os.system( ' '.join( cmd ) ) != 0:
104 print 'Documentation generation failed'
105 return False
106 else:
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +0000107 if is_silent:
108 process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
109 else:
110 process = subprocess.Popen( cmd )
111 stdout, _ = process.communicate()
112 if process.returncode:
113 print 'Documentation generation failed:'
114 print stdout
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000115 return False
116 return True
117 finally:
118 os.chdir( old_cwd )
119
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +0000120def build_doc( options, make_release=False ):
121 if make_release:
122 options.make_tarball = True
123 options.with_dot = True
124 options.with_html_help = True
125 options.with_uml_look = True
126 options.open = False
127 options.silent = True
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000128
129 version = open('version','rt').read().strip()
130 output_dir = '../build/doxygen' # relative to doc/doxyfile location.
131 top_dir = os.path.abspath( '.' )
132 html_output_dirname = 'jsoncpp-api-html-' + version
133 tarball_path = os.path.join( 'dist', html_output_dirname + '.tar.gz' )
134 warning_log_path = os.path.join( output_dir, '../jsoncpp-doxygen-warning.log' )
135 def yesno( bool ):
136 return bool and 'YES' or 'NO'
137 subst_keys = {
138 '%JSONCPP_VERSION%': version,
139 '%DOC_TOPDIR%': '',
140 '%TOPDIR%': top_dir,
141 '%HTML_OUTPUT%': os.path.join( output_dir, html_output_dirname ),
142 '%HAVE_DOT%': yesno(options.with_dot),
143 '%DOT_PATH%': os.path.split(options.dot_path)[0],
144 '%HTML_HELP%': yesno(options.with_html_help),
145 '%UML_LOOK%': yesno(options.with_uml_look),
146 '%WARNING_LOG_PATH%': warning_log_path
147 }
148
149 full_output_dir = os.path.join( 'doc', output_dir )
150 if os.path.isdir( full_output_dir ):
151 print 'Deleting directory:', full_output_dir
152 shutil.rmtree( full_output_dir )
153 if not os.path.isdir( full_output_dir ):
154 os.makedirs( full_output_dir )
155
156 do_subst_in_file( 'doc/doxyfile', 'doc/doxyfile.in', subst_keys )
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +0000157 ok = run_doxygen( options.doxygen_path, 'doc/doxyfile', 'doc', is_silent=options.silent )
158 if not options.silent:
159 print open(os.path.join('doc', warning_log_path), 'rb').read()
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000160 index_path = os.path.abspath(os.path.join(subst_keys['%HTML_OUTPUT%'], 'index.html'))
161 print 'Generated documentation can be found in:'
162 print index_path
163 if options.open:
164 import webbrowser
165 webbrowser.open( 'file://' + index_path )
166 if options.make_tarball:
167 print 'Generating doc tarball to', tarball_path
168 tarball_sources = [
169 full_output_dir,
170 'README.txt',
171 'version'
172 ]
173 tarball_basedir = os.path.join( full_output_dir, html_output_dirname )
174 make_tarball( tarball_path, tarball_sources, tarball_basedir, html_output_dirname )
175
Baptiste Lepilleur1f4847c2010-02-23 07:57:38 +0000176def main():
177 usage = """%prog
178 Generates doxygen documentation in build/doxygen.
179 Optionaly makes a tarball of the documentation to dist/.
180
181 Must be started in the project top directory.
182 """
183 from optparse import OptionParser
184 parser = OptionParser(usage=usage)
185 parser.allow_interspersed_args = False
186 parser.add_option('--with-dot', dest="with_dot", action='store_true', default=False,
187 help="""Enable usage of DOT to generate collaboration diagram""")
188 parser.add_option('--dot', dest="dot_path", action='store', default=find_program('dot'),
189 help="""Path to GraphViz dot tool. Must be full qualified path. [Default: %default]""")
190 parser.add_option('--doxygen', dest="doxygen_path", action='store', default=find_program('doxygen'),
191 help="""Path to Doxygen tool. [Default: %default]""")
192 parser.add_option('--with-html-help', dest="with_html_help", action='store_true', default=False,
193 help="""Enable generation of Microsoft HTML HELP""")
194 parser.add_option('--no-uml-look', dest="with_uml_look", action='store_false', default=True,
195 help="""Generates DOT graph without UML look [Default: False]""")
196 parser.add_option('--open', dest="open", action='store_true', default=False,
197 help="""Open the HTML index in the web browser after generation""")
198 parser.add_option('--tarball', dest="make_tarball", action='store_true', default=False,
199 help="""Generates a tarball of the documentation in dist/ directory""")
200 parser.add_option('-s', '--silent', dest="silent", action='store_true', default=False,
201 help="""Hides doxygen output""")
202 parser.enable_interspersed_args()
203 options, args = parser.parse_args()
204 build_doc( options )
205
Baptiste Lepilleur57ee0e32010-02-22 04:16:10 +0000206if __name__ == '__main__':
207 main()