diff options
Diffstat (limited to 'doc/mkapiref.py')
-rwxr-xr-x | doc/mkapiref.py | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/doc/mkapiref.py b/doc/mkapiref.py new file mode 100755 index 0000000..df59fbc --- /dev/null +++ b/doc/mkapiref.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# nghttp2 - HTTP/2 C Library +# +# Copyright (c) 2020 nghttp2 contributors +# Copyright (c) 2020 ngtcp2 contributors +# Copyright (c) 2012 Tatsuhiro Tsujikawa +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Generates API reference from C source code. + +import re, sys, argparse, os.path + +class FunctionDoc: + def __init__(self, name, content, domain, filename): + self.name = name + self.content = content + self.domain = domain + if self.domain == 'function': + self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1) + self.filename = filename + + def write(self, out): + out.write('.. {}:: {}\n'.format(self.domain, self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + +class StructDoc: + def __init__(self, name, content, members, member_domain): + self.name = name + self.content = content + self.members = members + self.member_domain = member_domain + + def write(self, out): + if self.name: + out.write('.. type:: {}\n'.format(self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + out.write('\n') + for name, content in self.members: + out.write(' .. {}:: {}\n'.format(self.member_domain, name)) + out.write('\n') + for line in content: + out.write(' {}\n'.format(line)) + out.write('\n') + +class EnumDoc: + def __init__(self, name, content, members): + self.name = name + self.content = content + self.members = members + + def write(self, out): + if self.name: + out.write('.. type:: {}\n'.format(self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + out.write('\n') + for name, content in self.members: + out.write(' .. enum:: {}\n'.format(name)) + out.write('\n') + for line in content: + out.write(' {}\n'.format(line)) + out.write('\n') + +class MacroDoc: + def __init__(self, name, content): + self.name = name + self.content = content + + def write(self, out): + out.write('''.. macro:: {}\n'''.format(self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + +class MacroSectionDoc: + def __init__(self, content): + self.content = content + + def write(self, out): + out.write('\n') + c = ' '.join(self.content).strip() + out.write(c) + out.write('\n') + out.write('-' * len(c)) + out.write('\n\n') + +class TypedefDoc: + def __init__(self, name, content): + self.name = name + self.content = content + + def write(self, out): + out.write('''.. type:: {}\n'''.format(self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + +def make_api_ref(infile): + macros = [] + enums = [] + types = [] + functions = [] + while True: + line = infile.readline() + if not line: + break + elif line == '/**\n': + line = infile.readline() + doctype = line.split()[1] + if doctype == '@function': + functions.append(process_function('function', infile)) + elif doctype == '@functypedef': + types.append(process_function('type', infile)) + elif doctype == '@struct' or doctype == '@union': + types.append(process_struct(infile)) + elif doctype == '@enum': + enums.append(process_enum(infile)) + elif doctype == '@macro': + macros.append(process_macro(infile)) + elif doctype == '@macrosection': + macros.append(process_macrosection(infile)) + elif doctype == '@typedef': + types.append(process_typedef(infile)) + return macros, enums, types, functions + +def output( + title, indexfile, macrosfile, enumsfile, typesfile, funcsdir, + macros, enums, types, functions): + indexfile.write(''' +{title} +{titledecoration} + +.. toctree:: + :maxdepth: 1 + + {macros} + {enums} + {types} +'''.format( + title=title, titledecoration='='*len(title), + macros=os.path.splitext(os.path.basename(macrosfile.name))[0], + enums=os.path.splitext(os.path.basename(enumsfile.name))[0], + types=os.path.splitext(os.path.basename(typesfile.name))[0], +)) + + for doc in functions: + indexfile.write(' {}\n'.format(doc.funcname)) + + macrosfile.write(''' +Macros +====== +''') + for doc in macros: + doc.write(macrosfile) + + enumsfile.write(''' +Enums +===== +''') + for doc in enums: + doc.write(enumsfile) + + typesfile.write(''' +Types (structs, unions and typedefs) +==================================== +''') + for doc in types: + doc.write(typesfile) + + for doc in functions: + with open(os.path.join(funcsdir, doc.funcname + '.rst'), 'w') as f: + f.write(''' +{funcname} +{secul} + +Synopsis +-------- + +*#include <nghttp2/{filename}>* + +'''.format(funcname=doc.funcname, secul='='*len(doc.funcname), + filename=doc.filename)) + doc.write(f) + +def process_macro(infile): + content = read_content(infile) + line = infile.readline() + macro_name = line.split()[1] + return MacroDoc(macro_name, content) + +def process_macrosection(infile): + content = read_content(infile) + return MacroSectionDoc(content) + +def process_typedef(infile): + content = read_content(infile) + typedef = infile.readline() + typedef = re.sub(r';\n$', '', typedef) + typedef = re.sub(r'typedef ', '', typedef) + return TypedefDoc(typedef, content) + +def process_enum(infile): + members = [] + enum_name = None + content = read_content(infile) + while True: + line = infile.readline() + if not line: + break + elif re.match(r'\s*/\*\*\n', line): + member_content = read_content(infile) + line = infile.readline() + items = line.split() + member_name = items[0].rstrip(',') + if len(items) >= 3: + member_content.insert(0, '(``{}``) '\ + .format(' '.join(items[2:]).rstrip(','))) + members.append((member_name, member_content)) + elif line.startswith('}'): + enum_name = line.rstrip().split()[1] + enum_name = re.sub(r';$', '', enum_name) + break + return EnumDoc(enum_name, content, members) + +def process_struct(infile): + members = [] + struct_name = None + content = read_content(infile) + while True: + line = infile.readline() + if not line: + break + elif re.match(r'\s*/\*\*\n', line): + member_content = read_content(infile) + line = infile.readline() + member_name = line.rstrip().rstrip(';') + members.append((member_name, member_content)) + elif line.startswith('}') or\ + (line.startswith('typedef ') and line.endswith(';\n')): + if line.startswith('}'): + index = 1 + else: + index = 3 + struct_name = line.rstrip().split()[index] + struct_name = re.sub(r';$', '', struct_name) + break + return StructDoc(struct_name, content, members, 'member') + +def process_function(domain, infile): + content = read_content(infile) + func_proto = [] + while True: + line = infile.readline() + if not line: + break + elif line == '\n': + break + else: + func_proto.append(line) + func_proto = ''.join(func_proto) + func_proto = re.sub(r';\n$', '', func_proto) + func_proto = re.sub(r'\s+', ' ', func_proto) + func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto) + func_proto = re.sub(r'typedef ', '', func_proto) + filename = os.path.basename(infile.name) + return FunctionDoc(func_proto, content, domain, filename) + +def read_content(infile): + content = [] + while True: + line = infile.readline() + if not line: + break + if re.match(r'\s*\*/\n', line): + break + else: + content.append(transform_content(line.rstrip())) + return content + +def arg_repl(matchobj): + return '*{}*'.format(matchobj.group(1).replace('*', '\\*')) + +def transform_content(content): + content = re.sub(r'^\s+\* ?', '', content) + content = re.sub(r'\|([^\s|]+)\|', arg_repl, content) + return content + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Generate API reference") + parser.add_argument('--title', default='API Reference', + help='title of index page') + parser.add_argument('index', type=argparse.FileType('w'), + help='index output file') + parser.add_argument('macros', type=argparse.FileType('w'), + help='macros section output file. The filename should be macros.rst') + parser.add_argument('enums', type=argparse.FileType('w'), + help='enums section output file. The filename should be enums.rst') + parser.add_argument('types', type=argparse.FileType('w'), + help='types section output file. The filename should be types.rst') + parser.add_argument('funcsdir', + help='functions doc output dir') + parser.add_argument('files', nargs='+', type=argparse.FileType('r'), + help='source file') + args = parser.parse_args() + macros = [] + enums = [] + types = [] + funcs = [] + for infile in args.files: + m, e, t, f = make_api_ref(infile) + macros.extend(m) + enums.extend(e) + types.extend(t) + funcs.extend(f) + funcs.sort(key=lambda x: x.funcname) + output( + args.title, + args.index, args.macros, args.enums, args.types, args.funcsdir, + macros, enums, types, funcs) |