Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / doc / doxygen / generate_docs.py
blob13316efb03a10f8a14f930658b384b6f7ca8cb16
1 #!/usr/bin/env python
2 # Copyright (c) 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Script to regenerate API docs using doxygen.
7 """
9 import argparse
10 import collections
11 import json
12 import os
13 import shutil
14 import subprocess
15 import sys
16 import tempfile
17 import urllib2
20 if sys.version_info < (2, 7, 0):
21 sys.stderr.write("python 2.7 or later is required run this script\n")
22 sys.exit(1)
25 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
26 DOC_DIR = os.path.dirname(SCRIPT_DIR)
29 ChannelInfo = collections.namedtuple('ChannelInfo', ['branch', 'version'])
32 def Trace(msg):
33 if Trace.verbose:
34 sys.stderr.write(str(msg) + '\n')
36 Trace.verbose = False
39 def GetChannelInfo():
40 url = 'http://omahaproxy.appspot.com/json'
41 u = urllib2.urlopen(url)
42 try:
43 data = json.loads(u.read())
44 finally:
45 u.close()
47 channel_info = {}
48 for os_row in data:
49 osname = os_row['os']
50 if osname not in ('win', 'mac', 'linux'):
51 continue
52 for version_row in os_row['versions']:
53 channel = version_row['channel']
54 # We don't display canary docs.
55 if channel == 'canary':
56 continue
58 version = version_row['version'].split('.')[0] # Major version
59 branch = version_row['true_branch']
60 if branch is None:
61 branch = 'trunk'
63 if channel in channel_info:
64 existing_info = channel_info[channel]
65 if branch != existing_info.branch:
66 sys.stderr.write('Warning: found different branch numbers for '
67 'channel %s: %s vs %s. Using %s.\n' % (
68 channel, branch, existing_info.branch, existing_info.branch))
69 else:
70 channel_info[channel] = ChannelInfo(branch, version)
72 return channel_info
75 def RemoveFile(filename):
76 if os.path.exists(filename):
77 os.remove(filename)
80 def RemoveDir(dirname):
81 if os.path.exists(dirname):
82 shutil.rmtree(dirname)
85 def HasBranchHeads():
86 cmd = ['git', 'for-each-ref', '--format=%(refname)',
87 'refs/remotes/branch-heads']
88 output = subprocess.check_output(cmd).splitlines()
89 return output != []
92 def CheckoutDirectories(dest_dirname, refname, root_path, patterns=None):
93 treeish = '%s:%s' % (refname, root_path)
94 cmd = ['git', 'ls-tree', '--full-tree', '-r', treeish]
95 if patterns:
96 cmd.extend(patterns)
98 Trace('Running \"%s\":' % ' '.join(cmd))
99 output = subprocess.check_output(cmd)
100 for line in output.splitlines():
101 info, rel_filename = line.split('\t')
102 sha = info.split(' ')[2]
104 Trace(' %s %s' % (sha, rel_filename))
106 cmd = ['git', 'show', sha]
107 blob = subprocess.check_output(cmd)
108 filename = os.path.join(dest_dirname, rel_filename)
109 dirname = os.path.dirname(filename)
110 if not os.path.exists(dirname):
111 os.makedirs(dirname)
113 Trace(' writing to %s' % filename)
114 with open(filename, 'w') as f:
115 f.write(blob)
118 def CheckoutPepperDocs(branch, doc_dirname):
119 Trace('Removing directory %s' % doc_dirname)
120 RemoveDir(doc_dirname)
122 if branch == 'master':
123 refname = 'refs/remotes/origin/master'
124 else:
125 refname = 'refs/remotes/branch-heads/%s' % branch
127 Trace('Checking out docs into %s' % doc_dirname)
128 subdirs = ['api', 'generators', 'cpp', 'utility']
129 CheckoutDirectories(doc_dirname, refname, 'ppapi', subdirs)
131 # The IDL generator needs PLY (a python lexing library); check it out into
132 # generators.
133 ply_dirname = os.path.join(doc_dirname, 'generators', 'ply')
134 Trace('Checking out PLY into %s' % ply_dirname)
135 CheckoutDirectories(ply_dirname, refname, 'third_party/ply')
138 def FixPepperDocLinks(doc_dirname):
139 # TODO(binji): We can remove this step when the correct links are in the
140 # stable branch.
141 Trace('Looking for links to fix in Pepper headers...')
142 for root, dirs, filenames in os.walk(doc_dirname):
143 # Don't recurse into .svn
144 if '.svn' in dirs:
145 dirs.remove('.svn')
147 for filename in filenames:
148 header_filename = os.path.join(root, filename)
149 Trace(' Checking file %r...' % header_filename)
150 replacements = {
151 '<a href="/native-client/{{pepperversion}}/devguide/coding/audio">':
152 '<a href="/native-client/devguide/coding/audio.html">',
153 '<a href="/native-client/devguide/coding/audio">':
154 '<a href="/native-client/devguide/coding/audio.html">',
155 '<a href="/native-client/{{pepperversion}}/pepperc/globals_defs"':
156 '<a href="globals_defs.html"',
157 '<a href="../pepperc/ppb__image__data_8h.html">':
158 '<a href="../c/ppb__image__data_8h.html">'}
160 with open(header_filename) as f:
161 lines = []
162 replaced = False
163 for line in f:
164 for find, replace in replacements.iteritems():
165 pos = line.find(find)
166 if pos != -1:
167 Trace(' Found %r...' % find)
168 replaced = True
169 line = line[:pos] + replace + line[pos + len(find):]
170 lines.append(line)
172 if replaced:
173 Trace(' Writing new file.')
174 with open(header_filename, 'w') as f:
175 f.writelines(lines)
178 def GenerateCHeaders(pepper_version, doc_dirname):
179 script = os.path.join(os.pardir, 'generators', 'generator.py')
180 cwd = os.path.join(doc_dirname, 'api')
181 out_dirname = os.path.join(os.pardir, 'c')
182 cmd = [sys.executable, script, '--cgen', '--release', 'M' + pepper_version,
183 '--wnone', '--dstroot', out_dirname]
184 Trace('Generating C Headers for version %s\n %s' % (
185 pepper_version, ' '.join(cmd)))
186 subprocess.check_call(cmd, cwd=cwd)
189 def GenerateDoxyfile(template_filename, out_dirname, doc_dirname, doxyfile):
190 Trace('Writing Doxyfile "%s" (from template %s)' % (
191 doxyfile, template_filename))
193 with open(template_filename) as f:
194 data = f.read()
196 with open(doxyfile, 'w') as f:
197 f.write(data % {
198 'out_dirname': out_dirname,
199 'doc_dirname': doc_dirname,
200 'script_dirname': SCRIPT_DIR})
203 def RunDoxygen(out_dirname, doxyfile):
204 Trace('Removing old output directory %s' % out_dirname)
205 RemoveDir(out_dirname)
207 Trace('Making new output directory %s' % out_dirname)
208 os.makedirs(out_dirname)
210 doxygen = os.environ.get('DOXYGEN', 'doxygen')
211 cmd = [doxygen, doxyfile]
212 Trace('Running Doxygen:\n %s' % ' '.join(cmd))
213 subprocess.check_call(cmd)
216 def RunDoxyCleanup(out_dirname):
217 script = os.path.join(SCRIPT_DIR, 'doxy_cleanup.py')
218 cmd = [sys.executable, script, out_dirname]
219 if Trace.verbose:
220 cmd.append('-v')
221 Trace('Running doxy_cleanup:\n %s' % ' '.join(cmd))
222 subprocess.check_call(cmd)
225 def RunRstIndex(kind, channel, pepper_version, out_dirname, out_rst_filename):
226 assert kind in ('root', 'c', 'cpp')
227 script = os.path.join(SCRIPT_DIR, 'rst_index.py')
228 cmd = [sys.executable, script,
229 '--' + kind,
230 '--channel', channel,
231 '--version', pepper_version,
232 out_dirname,
233 out_rst_filename]
234 Trace('Running rst_index:\n %s' % ' '.join(cmd))
235 subprocess.check_call(cmd)
238 def GetRstName(kind, channel):
239 if channel == 'stable':
240 filename = '%s-api.rst' % kind
241 else:
242 filename = '%s-api-%s.rst' % (kind, channel)
243 return os.path.join(DOC_DIR, filename)
246 def GenerateDocs(root_dirname, channel, pepper_version, branch):
247 Trace('Generating docs for %s (branch %s)' % (channel, branch))
248 pepper_dirname = 'pepper_%s' % channel
249 out_dirname = os.path.join(root_dirname, pepper_dirname)
251 try:
252 svn_dirname = tempfile.mkdtemp(prefix=pepper_dirname)
253 doxyfile_dirname = tempfile.mkdtemp(prefix='%s_doxyfiles' % pepper_dirname)
255 CheckoutPepperDocs(branch, svn_dirname)
256 FixPepperDocLinks(svn_dirname)
257 GenerateCHeaders(pepper_version, svn_dirname)
259 doxyfile_c = ''
260 doxyfile_cpp = ''
262 # Generate Root index
263 rst_index_root = os.path.join(DOC_DIR, pepper_dirname, 'index.rst')
264 RunRstIndex('root', channel, pepper_version, out_dirname, rst_index_root)
266 # Generate C docs
267 out_dirname_c = os.path.join(out_dirname, 'c')
268 doxyfile_c = os.path.join(doxyfile_dirname, 'Doxyfile.c.%s' % channel)
269 doxyfile_c_template = os.path.join(SCRIPT_DIR, 'Doxyfile.c.template')
270 rst_index_c = GetRstName('c', channel)
271 GenerateDoxyfile(doxyfile_c_template, out_dirname_c, svn_dirname,
272 doxyfile_c)
273 RunDoxygen(out_dirname_c, doxyfile_c)
274 RunDoxyCleanup(out_dirname_c)
275 RunRstIndex('c', channel, pepper_version, out_dirname_c, rst_index_c)
277 # Generate C++ docs
278 out_dirname_cpp = os.path.join(out_dirname, 'cpp')
279 doxyfile_cpp = os.path.join(doxyfile_dirname, 'Doxyfile.cpp.%s' % channel)
280 doxyfile_cpp_template = os.path.join(SCRIPT_DIR, 'Doxyfile.cpp.template')
281 rst_index_cpp = GetRstName('cpp', channel)
282 GenerateDoxyfile(doxyfile_cpp_template, out_dirname_cpp, svn_dirname,
283 doxyfile_cpp)
284 RunDoxygen(out_dirname_cpp, doxyfile_cpp)
285 RunDoxyCleanup(out_dirname_cpp)
286 RunRstIndex('cpp', channel, pepper_version, out_dirname_cpp, rst_index_cpp)
287 finally:
288 # Cleanup
289 RemoveDir(svn_dirname)
290 RemoveDir(doxyfile_dirname)
293 def main(argv):
294 parser = argparse.ArgumentParser(description=__doc__)
295 parser.add_argument('-v', '--verbose',
296 help='Verbose output', action='store_true')
297 parser.add_argument('out_directory')
298 options = parser.parse_args(argv)
300 if options.verbose:
301 Trace.verbose = True
303 channel_info = GetChannelInfo()
304 for channel, info in channel_info.iteritems():
305 GenerateDocs(options.out_directory, channel, info.version, info.branch)
307 return 0
310 if __name__ == '__main__':
311 try:
312 rtn = main(sys.argv[1:])
313 except KeyboardInterrupt:
314 sys.stderr.write('%s: interrupted\n' % os.path.basename(__file__))
315 rtn = 1
316 sys.exit(rtn)