2 # -*- coding: utf-8 -*-
3 # nghttp2 - HTTP/2 C Library
5 # Copyright (c) 2020 nghttp2 contributors
6 # Copyright (c) 2020 ngtcp2 contributors
7 # Copyright (c) 2012 Tatsuhiro Tsujikawa
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:
17 # The above copyright notice and this permission notice shall be
18 # included in all copies or substantial portions of the Software.
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.
28 # Generates API reference from C source code.
30 import re, sys, argparse, os.path
33 def __init__(self, name, content, domain, filename):
35 self.content = content
37 if self.domain == 'function':
38 self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1)
39 self.filename = filename
42 out.write('.. {}:: {}\n'.format(self.domain, self.name))
44 for line in self.content:
45 out.write(' {}\n'.format(line))
48 def __init__(self, name, content, members, member_domain):
50 self.content = content
51 self.members = members
52 self.member_domain = member_domain
56 out.write('.. type:: {}\n'.format(self.name))
58 for line in self.content:
59 out.write(' {}\n'.format(line))
61 for name, content in self.members:
62 out.write(' .. {}:: {}\n'.format(self.member_domain, name))
65 out.write(' {}\n'.format(line))
69 def __init__(self, name, content, members):
71 self.content = content
72 self.members = members
76 out.write('.. type:: {}\n'.format(self.name))
78 for line in self.content:
79 out.write(' {}\n'.format(line))
81 for name, content in self.members:
82 out.write(' .. enum:: {}\n'.format(name))
85 out.write(' {}\n'.format(line))
89 def __init__(self, name, content):
91 self.content = content
94 out.write('''.. macro:: {}\n'''.format(self.name))
96 for line in self.content:
97 out.write(' {}\n'.format(line))
99 class MacroSectionDoc:
100 def __init__(self, content):
101 self.content = content
103 def write(self, out):
105 c = ' '.join(self.content).strip()
108 out.write('-' * len(c))
112 def __init__(self, name, content):
114 self.content = content
116 def write(self, out):
117 out.write('''.. type:: {}\n'''.format(self.name))
119 for line in self.content:
120 out.write(' {}\n'.format(line))
122 def make_api_ref(infile):
128 line = infile.readline()
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
151 title, indexfile, macrosfile, enumsfile, typesfile, funcsdir,
152 macros, enums, types, functions):
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],
170 for doc in functions:
171 indexfile.write(' {}\n'.format(doc.funcname))
178 doc.write(macrosfile)
188 Types (structs, unions and typedefs)
189 ====================================
194 for doc in functions:
195 with open(os.path.join(funcsdir, doc.funcname + '.rst'), 'w') as f:
203 *#include <nghttp2/{filename}>*
205 '''.format(funcname=doc.funcname, secul='='*len(doc.funcname),
206 filename=doc.filename))
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)
215 def process_macrosection(infile):
216 content = read_content(infile)
217 return MacroSectionDoc(content)
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)
226 def process_enum(infile):
229 content = read_content(infile)
231 line = infile.readline()
234 elif re.match(r'\s*/\*\*\n', line):
235 member_content = read_content(infile)
236 line = infile.readline()
238 member_name = items[0].rstrip(',')
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)
247 return EnumDoc(enum_name, content, members)
249 def process_struct(infile):
252 content = read_content(infile)
254 line = infile.readline()
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('}'):
268 struct_name = line.rstrip().split()[index]
269 struct_name = re.sub(r';$', '', struct_name)
271 return StructDoc(struct_name, content, members, 'member')
273 def process_function(domain, infile):
274 content = read_content(infile)
277 line = infile.readline()
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)
292 def read_content(infile):
295 line = infile.readline()
298 if re.match(r'\s*\*/\n', line):
301 content.append(transform_content(line.rstrip()))
304 def arg_repl(matchobj):
305 return '*{}*'.format(matchobj.group(1).replace('*', '\\*'))
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)
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'),
329 args = parser.parse_args()
334 for infile in args.files:
335 m, e, t, f = make_api_ref(infile)
340 funcs.sort(key=lambda x: x.funcname)
343 args.index, args.macros, args.enums, args.types, args.funcsdir,
344 macros, enums, types, funcs)