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