2020-12-29 08:29:00 +01:00
|
|
|
#!/usr/bin/env python3
|
2015-06-07 06:13:51 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
2014-03-30 12:09:21 +02:00
|
|
|
# nghttp2 - HTTP/2 C Library
|
2020-12-29 08:29:00 +01:00
|
|
|
#
|
|
|
|
# Copyright (c) 2020 nghttp2 contributors
|
|
|
|
# Copyright (c) 2020 ngtcp2 contributors
|
2012-03-13 16:32:52 +01:00
|
|
|
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
2020-12-29 08:29:00 +01:00
|
|
|
#
|
2012-03-13 16:32:52 +01:00
|
|
|
# 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.
|
2015-06-07 06:13:51 +02:00
|
|
|
|
2015-03-31 18:13:10 +02:00
|
|
|
import re, sys, argparse, os.path
|
2012-03-13 16:32:52 +01:00
|
|
|
|
|
|
|
class FunctionDoc:
|
2020-12-29 08:29:00 +01:00
|
|
|
def __init__(self, name, content, domain, filename):
|
2012-03-13 16:32:52 +01:00
|
|
|
self.name = name
|
|
|
|
self.content = content
|
|
|
|
self.domain = domain
|
2015-03-31 18:13:10 +02:00
|
|
|
if self.domain == 'function':
|
|
|
|
self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1)
|
2020-12-29 08:29:00 +01:00
|
|
|
self.filename = filename
|
2012-03-13 16:32:52 +01:00
|
|
|
|
|
|
|
def write(self, out):
|
2015-03-31 18:13:10 +02:00
|
|
|
out.write('.. {}:: {}\n'.format(self.domain, self.name))
|
|
|
|
out.write('\n')
|
2012-03-13 16:32:52 +01:00
|
|
|
for line in self.content:
|
2015-03-31 18:13:10 +02:00
|
|
|
out.write(' {}\n'.format(line))
|
2012-03-13 16:32:52 +01:00
|
|
|
|
|
|
|
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:
|
2015-03-31 18:13:10 +02:00
|
|
|
out.write('.. type:: {}\n'.format(self.name))
|
|
|
|
out.write('\n')
|
2012-03-13 16:32:52 +01:00
|
|
|
for line in self.content:
|
2015-03-31 18:13:10 +02:00
|
|
|
out.write(' {}\n'.format(line))
|
|
|
|
out.write('\n')
|
2012-03-13 16:32:52 +01:00
|
|
|
for name, content in self.members:
|
2015-03-31 18:13:10 +02:00
|
|
|
out.write(' .. {}:: {}\n'.format(self.member_domain, name))
|
|
|
|
out.write('\n')
|
2012-03-13 16:32:52 +01:00
|
|
|
for line in content:
|
2015-03-31 18:13:10 +02:00
|
|
|
out.write(' {}\n'.format(line))
|
|
|
|
out.write('\n')
|
2012-03-13 16:32:52 +01:00
|
|
|
|
2020-12-29 08:29:00 +01:00
|
|
|
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')
|
|
|
|
|
2012-03-13 16:32:52 +01:00
|
|
|
class MacroDoc:
|
|
|
|
def __init__(self, name, content):
|
|
|
|
self.name = name
|
|
|
|
self.content = content
|
|
|
|
|
|
|
|
def write(self, out):
|
2015-03-31 18:13:10 +02:00
|
|
|
out.write('''.. macro:: {}\n'''.format(self.name))
|
|
|
|
out.write('\n')
|
2012-03-13 16:32:52 +01:00
|
|
|
for line in self.content:
|
2015-03-31 18:13:10 +02:00
|
|
|
out.write(' {}\n'.format(line))
|
2012-03-13 16:32:52 +01:00
|
|
|
|
2020-12-29 08:29:00 +01:00
|
|
|
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):
|
2012-03-13 16:32:52 +01:00
|
|
|
macros = []
|
|
|
|
enums = []
|
|
|
|
types = []
|
|
|
|
functions = []
|
2020-12-29 08:29:00 +01:00
|
|
|
while True:
|
|
|
|
line = infile.readline()
|
|
|
|
if not line:
|
|
|
|
break
|
|
|
|
elif line == '/**\n':
|
2012-03-13 16:32:52 +01:00
|
|
|
line = infile.readline()
|
2020-12-29 08:29:00 +01:00
|
|
|
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))
|
2015-03-31 18:13:10 +02:00
|
|
|
return macros, enums, types, functions
|
|
|
|
|
|
|
|
def output(
|
2020-12-29 08:29:00 +01:00
|
|
|
title, indexfile, macrosfile, enumsfile, typesfile, funcsdir,
|
2015-03-31 18:13:10 +02:00
|
|
|
macros, enums, types, functions):
|
|
|
|
indexfile.write('''
|
2020-12-29 08:29:00 +01:00
|
|
|
{title}
|
|
|
|
{titledecoration}
|
2015-03-31 18:13:10 +02:00
|
|
|
|
|
|
|
.. toctree::
|
|
|
|
:maxdepth: 1
|
|
|
|
|
2020-12-29 08:29:00 +01:00
|
|
|
{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],
|
|
|
|
))
|
2015-03-31 18:13:10 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
--------
|
|
|
|
|
2020-12-29 08:29:00 +01:00
|
|
|
*#include <nghttp2/{filename}>*
|
2015-03-31 18:13:10 +02:00
|
|
|
|
2020-12-29 08:29:00 +01:00
|
|
|
'''.format(funcname=doc.funcname, secul='='*len(doc.funcname),
|
|
|
|
filename=doc.filename))
|
2015-03-31 18:13:10 +02:00
|
|
|
doc.write(f)
|
2012-03-13 16:32:52 +01:00
|
|
|
|
|
|
|
def process_macro(infile):
|
|
|
|
content = read_content(infile)
|
|
|
|
line = infile.readline()
|
|
|
|
macro_name = line.split()[1]
|
|
|
|
return MacroDoc(macro_name, content)
|
|
|
|
|
2020-12-29 08:29:00 +01:00
|
|
|
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)
|
|
|
|
|
2012-03-13 16:32:52 +01:00
|
|
|
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()
|
2012-08-03 15:35:55 +02:00
|
|
|
items = line.split()
|
2020-12-29 08:29:00 +01:00
|
|
|
member_name = items[0].rstrip(',')
|
2012-08-03 15:35:55 +02:00
|
|
|
if len(items) >= 3:
|
|
|
|
member_content.insert(0, '(``{}``) '\
|
2013-11-07 16:21:50 +01:00
|
|
|
.format(' '.join(items[2:]).rstrip(',')))
|
2012-03-13 16:32:52 +01:00
|
|
|
members.append((member_name, member_content))
|
|
|
|
elif line.startswith('}'):
|
|
|
|
enum_name = line.rstrip().split()[1]
|
|
|
|
enum_name = re.sub(r';$', '', enum_name)
|
|
|
|
break
|
2020-12-29 08:29:00 +01:00
|
|
|
return EnumDoc(enum_name, content, members)
|
2012-03-13 16:32:52 +01:00
|
|
|
|
|
|
|
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)
|
2015-05-13 16:39:48 +02:00
|
|
|
func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
|
2020-12-29 08:29:00 +01:00
|
|
|
func_proto = re.sub(r'typedef ', '', func_proto)
|
|
|
|
filename = os.path.basename(infile.name)
|
|
|
|
return FunctionDoc(func_proto, content, domain, filename)
|
2012-03-13 16:32:52 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
content = re.sub(r':enum:', ':macro:', content)
|
|
|
|
return content
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
parser = argparse.ArgumentParser(description="Generate API reference")
|
2020-12-29 08:29:00 +01:00
|
|
|
parser.add_argument('--title', default='API Reference',
|
|
|
|
help='title of index page')
|
2015-03-31 18:13:10 +02:00
|
|
|
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')
|
2013-12-18 16:05:54 +01:00
|
|
|
parser.add_argument('files', nargs='+', type=argparse.FileType('r'),
|
2012-03-13 16:32:52 +01:00
|
|
|
help='source file')
|
|
|
|
args = parser.parse_args()
|
2015-03-31 18:13:10 +02:00
|
|
|
macros = []
|
|
|
|
enums = []
|
|
|
|
types = []
|
|
|
|
funcs = []
|
2012-03-13 16:32:52 +01:00
|
|
|
for infile in args.files:
|
2020-12-29 08:29:00 +01:00
|
|
|
m, e, t, f = make_api_ref(infile)
|
2015-03-31 18:13:10 +02:00
|
|
|
macros.extend(m)
|
|
|
|
enums.extend(e)
|
|
|
|
types.extend(t)
|
|
|
|
funcs.extend(f)
|
|
|
|
funcs.sort(key=lambda x: x.funcname)
|
|
|
|
output(
|
2020-12-29 08:29:00 +01:00
|
|
|
args.title,
|
2015-03-31 18:13:10 +02:00
|
|
|
args.index, args.macros, args.enums, args.types, args.funcsdir,
|
|
|
|
macros, enums, types, funcs)
|