Imported Upstream version 1.0.0
[platform/upstream/nghttp2.git] / doc / mkapiref.py
1 #!/usr/bin/env python
2 # nghttp2 - HTTP/2 C Library
3
4 # Copyright (c) 2012 Tatsuhiro Tsujikawa
5
6 # Permission is hereby granted, free of charge, to any person obtaining
7 # a copy of this software and associated documentation files (the
8 # "Software"), to deal in the Software without restriction, including
9 # without limitation the rights to use, copy, modify, merge, publish,
10 # distribute, sublicense, and/or sell copies of the Software, and to
11 # permit persons to whom the Software is furnished to do so, subject to
12 # the following conditions:
13
14 # The above copyright notice and this permission notice shall be
15 # included in all copies or substantial portions of the Software.
16
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 # Generates API reference from C source code.
26 from __future__ import print_function # At least python 2.6 is required
27 import re, sys, argparse, os.path
28
29 class FunctionDoc:
30     def __init__(self, name, content, domain):
31         self.name = name
32         self.content = content
33         self.domain = domain
34         if self.domain == 'function':
35             self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1)
36
37     def write(self, out):
38         out.write('.. {}:: {}\n'.format(self.domain, self.name))
39         out.write('\n')
40         for line in self.content:
41             out.write('    {}\n'.format(line))
42
43 class StructDoc:
44     def __init__(self, name, content, members, member_domain):
45         self.name = name
46         self.content = content
47         self.members = members
48         self.member_domain = member_domain
49
50     def write(self, out):
51         if self.name:
52             out.write('.. type:: {}\n'.format(self.name))
53             out.write('\n')
54             for line in self.content:
55                 out.write('    {}\n'.format(line))
56             out.write('\n')
57             for name, content in self.members:
58                 out.write('    .. {}:: {}\n'.format(self.member_domain, name))
59                 out.write('\n')
60                 for line in content:
61                     out.write('        {}\n'.format(line))
62             out.write('\n')
63
64 class MacroDoc:
65     def __init__(self, name, content):
66         self.name = name
67         self.content = content
68
69     def write(self, out):
70         out.write('''.. macro:: {}\n'''.format(self.name))
71         out.write('\n')
72         for line in self.content:
73             out.write('    {}\n'.format(line))
74
75 def make_api_ref(infiles):
76     macros = []
77     enums = []
78     types = []
79     functions = []
80     for infile in infiles:
81         while True:
82             line = infile.readline()
83             if not line:
84                 break
85             elif line == '/**\n':
86                 line = infile.readline()
87                 doctype = line.split()[1]
88                 if doctype == '@function':
89                     functions.append(process_function('function', infile))
90                 elif doctype == '@functypedef':
91                     types.append(process_function('type', infile))
92                 elif doctype == '@struct' or doctype == '@union':
93                     types.append(process_struct(infile))
94                 elif doctype == '@enum':
95                     enums.append(process_enum(infile))
96                 elif doctype == '@macro':
97                     macros.append(process_macro(infile))
98     return macros, enums, types, functions
99
100     alldocs = [('Macros', macros),
101                ('Enums', enums),
102                ('Types (structs, unions and typedefs)', types),
103                ('Functions', functions)]
104
105 def output(
106         indexfile, macrosfile, enumsfile, typesfile, funcsdir,
107         macros, enums, types, functions):
108     indexfile.write('''
109 API Reference
110 =============
111
112 .. toctree::
113    :maxdepth: 1
114
115    macros
116    enums
117    types
118 ''')
119
120     for doc in functions:
121         indexfile.write('   {}\n'.format(doc.funcname))
122
123     macrosfile.write('''
124 Macros
125 ======
126 ''')
127     for doc in macros:
128         doc.write(macrosfile)
129
130     enumsfile.write('''
131 Enums
132 =====
133 ''')
134     for doc in enums:
135         doc.write(enumsfile)
136
137     typesfile.write('''
138 Types (structs, unions and typedefs)
139 ====================================
140 ''')
141     for doc in types:
142         doc.write(typesfile)
143
144     for doc in functions:
145         with open(os.path.join(funcsdir, doc.funcname + '.rst'), 'w') as f:
146             f.write('''
147 {funcname}
148 {secul}
149
150 Synopsis
151 --------
152
153 *#include <nghttp2/nghttp2.h>*
154
155 '''.format(funcname=doc.funcname, secul='='*len(doc.funcname)))
156             doc.write(f)
157
158 def process_macro(infile):
159     content = read_content(infile)
160     line = infile.readline()
161     macro_name = line.split()[1]
162     return MacroDoc(macro_name, content)
163
164 def process_enum(infile):
165     members = []
166     enum_name = None
167     content = read_content(infile)
168     while True:
169         line = infile.readline()
170         if not line:
171             break
172         elif re.match(r'\s*/\*\*\n', line):
173             member_content = read_content(infile)
174             line = infile.readline()
175             items = line.split()
176             member_name = items[0]
177             if len(items) >= 3:
178                 member_content.insert(0, '(``{}``) '\
179                                       .format(' '.join(items[2:]).rstrip(',')))
180             members.append((member_name, member_content))
181         elif line.startswith('}'):
182             enum_name = line.rstrip().split()[1]
183             enum_name = re.sub(r';$', '', enum_name)
184             break
185     return StructDoc(enum_name, content, members, 'macro')
186
187 def process_struct(infile):
188     members = []
189     struct_name = None
190     content = read_content(infile)
191     while True:
192         line = infile.readline()
193         if not line:
194             break
195         elif re.match(r'\s*/\*\*\n', line):
196             member_content = read_content(infile)
197             line = infile.readline()
198             member_name = line.rstrip().rstrip(';')
199             members.append((member_name, member_content))
200         elif line.startswith('}') or\
201                 (line.startswith('typedef ') and line.endswith(';\n')):
202             if line.startswith('}'):
203                 index = 1
204             else:
205                 index = 3
206             struct_name = line.rstrip().split()[index]
207             struct_name = re.sub(r';$', '', struct_name)
208             break
209     return StructDoc(struct_name, content, members, 'member')
210
211 def process_function(domain, infile):
212     content = read_content(infile)
213     func_proto = []
214     while True:
215         line = infile.readline()
216         if not line:
217             break
218         elif line == '\n':
219             break
220         else:
221             func_proto.append(line)
222     func_proto = ''.join(func_proto)
223     func_proto = re.sub(r';\n$', '', func_proto)
224     func_proto = re.sub(r'\s+', ' ', func_proto)
225     func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
226     return FunctionDoc(func_proto, content, domain)
227
228 def read_content(infile):
229     content = []
230     while True:
231         line = infile.readline()
232         if not line:
233             break
234         if re.match(r'\s*\*/\n', line):
235             break
236         else:
237             content.append(transform_content(line.rstrip()))
238     return content
239
240 def arg_repl(matchobj):
241     return '*{}*'.format(matchobj.group(1).replace('*', '\\*'))
242
243 def transform_content(content):
244     content = re.sub(r'^\s+\* ?', '', content)
245     content = re.sub(r'\|([^\s|]+)\|', arg_repl, content)
246     content = re.sub(r':enum:', ':macro:', content)
247     return content
248
249 if __name__ == '__main__':
250     parser = argparse.ArgumentParser(description="Generate API reference")
251     parser.add_argument('index', type=argparse.FileType('w'),
252                         help='index output file')
253     parser.add_argument('macros', type=argparse.FileType('w'),
254                         help='macros section output file.  The filename should be macros.rst')
255     parser.add_argument('enums', type=argparse.FileType('w'),
256                         help='enums section output file.  The filename should be enums.rst')
257     parser.add_argument('types', type=argparse.FileType('w'),
258                         help='types section output file.  The filename should be types.rst')
259     parser.add_argument('funcsdir',
260                         help='functions doc output dir')
261     parser.add_argument('files', nargs='+', type=argparse.FileType('r'),
262                         help='source file')
263     args = parser.parse_args()
264     macros = []
265     enums = []
266     types = []
267     funcs = []
268     for infile in args.files:
269         m, e, t, f = make_api_ref(args.files)
270         macros.extend(m)
271         enums.extend(e)
272         types.extend(t)
273         funcs.extend(f)
274     funcs.sort(key=lambda x: x.funcname)
275     output(
276         args.index, args.macros, args.enums, args.types, args.funcsdir,
277         macros, enums, types, funcs)