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.
5
6import cStringIO
7import fnmatch
8import optparse
9import os
10import re
11import sys
12
13VALID_CHANNELS = ('stable', 'beta', 'dev')
14
15ROOT_FILE_CONTENTS = """.. _pepper_%(channel)s_index:
16
17:orphan:
18
19.. DO NOT EDIT! This document is auto-generated by doxygen/rst_index.py.
20
21########################################
22Pepper API Reference (%(channel_title)s)
23########################################
24
25This page lists the API for Pepper %(version)s. Apps that use this API can
26run in Chrome %(version)s or higher.
27
28:ref:`Pepper C API Reference <pepper_%(channel)s_c_index>`
29===========================================================
30
31:ref:`Pepper C++ API Reference <pepper_%(channel)s_cpp_index>`
32===============================================================
33
34"""
35
36C_FILE_CONTENTS = """.. _pepper_%(channel)s_c_index:
37
38.. DO NOT EDIT! This document is auto-generated by doxygen/rst_index.py.
39
40##########################################
41Pepper C API Reference (%(channel_title)s)
42##########################################
43
44This page lists the C API for Pepper %(version)s. Apps that use this API can
45run in Chrome %(version)s or higher.
46
47`Interfaces <group___interfaces.html>`_
48=======================================
49%(interfaces)s
50
51`Structures <group___structs.html>`_
52====================================
53%(structures)s
54
55`Functions <group___functions.html>`_
56=====================================
57
58`Enums <group___enums.html>`_
59=============================
60
61`Typedefs <group___typedefs.html>`_
62===================================
63
64`Macros <globals_defs.html>`_
65=============================
66
67Files
68=====
69%(files)s
70"""
71
72C_INTERFACE_WILDCARDS =  ['struct_p_p_p__*', 'struct_p_p_b__*']
73
74C_STRUCT_WILDCARDS =  ['struct_p_p__*', 'union_p_p__*']
75
76CPP_FILE_CONTENTS = """.. _pepper_%(channel)s_cpp_index:
77
78.. DO NOT EDIT! This document is auto-generated by doxygen/rst_index.py.
79
80############################################
81Pepper C++ API Reference (%(channel_title)s)
82############################################
83
84This page lists the C++ API for Pepper %(version)s. Apps that use this API can
85run in Chrome %(version)s or higher.
86
87`Classes <inherits.html>`_
88==========================
89%(classes)s
90
91Files
92=====
93%(files)s
94"""
95
96CPP_CLASSES_WILDCARDS = ['classpp_1_1*.html']
97CPP_CLASSES_EXCLUDES = ['*-members*']
98
99FILE_WILDCARDS = ['*_8h.html']
100
101
102def GetName(filename):
103  filename = os.path.splitext(filename)[0]
104  out = ''
105  if filename.startswith('struct_p_p_b__'):
106    mangle = filename[7:]  # skip "struct_"
107  elif filename.startswith('struct_p_p_p__'):
108    mangle = filename[7:]  # skip "struct_"
109  elif filename.startswith('struct_p_p__'):
110    mangle = filename[7:]  # skip "struct_"
111  elif filename.startswith('union_p_p__'):
112    mangle = filename[6:]  # skip "union_"
113  elif filename.startswith('classpp_1_1_'):
114    mangle = filename[12:]
115  elif filename.startswith('classpp_1_1ext_1_1_'):
116    out = 'Ext::'      # maybe 'ext::' ?
117    mangle = filename[19:]
118  elif filename.startswith('classpp_1_1internal_1_1_'):
119    out = 'Internal::' # maybe 'internal::'
120    mangle = filename[24:]
121  elif filename.startswith('structpp_1_1internal_1_1_'):
122    out = 'Internal::'
123    mangle = filename[25:]
124  elif filename.endswith('_8h'):
125    return filename[:-3].replace('__', '_') + '.h'
126  else:
127    print 'No match: ' + filename
128  cap = True
129  for c in mangle:
130    if c == '_':
131      if cap:
132        # If cap is True, we've already read one underscore. The second means
133        # that we should insert a literal underscore.
134        cap = False
135      else:
136        cap = True
137        continue
138    if cap:
139      c = c.upper()
140      cap = False
141    out += c
142
143  # Strip trailing version number (e.g. PPB_Audio_1_1 -> PPB_Audio)
144  return re.sub(r'_\d_\d$', '', out)
145
146
147def GetPath(filepath):
148  if os.path.exists(filepath):
149    return filepath
150  raise OSError('Couldnt find: ' + filepath)
151
152
153def MakeReSTListFromFiles(path, matches, excludes=None):
154  dir_files = os.listdir(path)
155  good_files = []
156  for match in matches:
157    good_files.extend(fnmatch.filter(dir_files, match))
158
159  if excludes:
160    for exclude in excludes:
161      good_files = [filename for filename in good_files
162                    if not fnmatch.fnmatch(filename, exclude)]
163
164  good_files.sort()
165  return '\n'.join('  * `%s <%s>`_\n' % (GetName(f), f) for f in good_files)
166
167
168def MakeTitleCase(s):
169  return s[0].upper() + s[1:]
170
171
172def GenerateRootIndex(channel, version, out_filename):
173  channel_title = MakeTitleCase(channel)
174
175  # Use StringIO so we don't write out a partial file on error.
176  output = cStringIO.StringIO()
177  output.write(ROOT_FILE_CONTENTS % vars())
178
179  with open(out_filename, 'w') as f:
180    f.write(output.getvalue())
181
182
183def GenerateCIndex(root_dir, channel, version, out_filename):
184  interfaces = MakeReSTListFromFiles(root_dir, C_INTERFACE_WILDCARDS)
185  structures = MakeReSTListFromFiles(root_dir, C_STRUCT_WILDCARDS)
186  files = MakeReSTListFromFiles(root_dir, FILE_WILDCARDS)
187  channel_title = MakeTitleCase(channel)
188
189  # Use StringIO so we don't write out a partial file on error.
190  output = cStringIO.StringIO()
191  output.write(C_FILE_CONTENTS % vars())
192
193  with open(out_filename, 'w') as f:
194    f.write(output.getvalue())
195
196
197def GenerateCppIndex(root_dir, channel, version, out_filename):
198  classes = MakeReSTListFromFiles(root_dir, CPP_CLASSES_WILDCARDS,
199                                  CPP_CLASSES_EXCLUDES)
200  files = MakeReSTListFromFiles(root_dir, FILE_WILDCARDS)
201  channel_title = MakeTitleCase(channel)
202
203  # Use StringIO so we don't write out a partial file on error.
204  output = cStringIO.StringIO()
205  output.write(CPP_FILE_CONTENTS % vars())
206
207  with open(out_filename, 'w') as f:
208    f.write(output.getvalue())
209
210
211def main(argv):
212  usage = 'Usage: %prog [options] <--root|--c|--cpp> directory'
213  parser = optparse.OptionParser(usage=usage)
214  parser.add_option('--channel', help='pepper channel (stable, beta, dev)')
215  parser.add_option('--version', help='pepper version (e.g. 32, 33, 34, etc.)')
216  parser.add_option('--root', help='Generate root API index',
217                    action='store_true', default=False)
218  parser.add_option('--c', help='Generate C API index', action='store_true',
219                    default=False)
220  parser.add_option('--cpp', help='Generate C++ API index', action='store_true',
221                    default=False)
222  parser.add_option('-o', '--output', help='output file.')
223  options, files = parser.parse_args(argv)
224
225  if len(files) != 1:
226    parser.error('Expected one directory')
227
228  if not options.output:
229    parser.error('Need output file')
230
231  if options.channel not in VALID_CHANNELS:
232    parser.error('Expected channel to be one of %s' % ', '.join(VALID_CHANNELS))
233
234  if sum((options.c, options.cpp, options.root)) != 1:
235    parser.error('Exactly one of --c/--cpp/--root flags is required.')
236
237  root_dir = files[0]
238
239  if options.c:
240    GenerateCIndex(root_dir, options.channel, options.version, options.output)
241  elif options.cpp:
242    GenerateCppIndex(root_dir, options.channel, options.version, options.output)
243  elif options.root:
244    GenerateRootIndex(options.channel, options.version, options.output)
245  else:
246    assert(False)
247  return 0
248
249
250if __name__ == '__main__':
251  try:
252    rtn = main(sys.argv[1:])
253  except KeyboardInterrupt:
254    sys.stderr.write('%s: interrupted\n' % os.path.basename(__file__))
255    rtn = 1
256  sys.exit(rtn)
257