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