Make doc generation work with sphinx v3.3

This commit is contained in:
Tatsuhiro Tsujikawa 2020-12-29 16:29:00 +09:00
parent 79a4f789a1
commit a7ecff657c
2 changed files with 107 additions and 43 deletions

View File

@ -18,7 +18,7 @@ from docutils.parsers.rst import Directive
from sphinx import addnodes from sphinx import addnodes
from sphinx import version_info from sphinx import version_info
from sphinx.roles import XRefRole from sphinx.roles import XRefRole
from sphinx.locale import l_, _ from sphinx.locale import _
from sphinx.domains import Domain, ObjType, Index from sphinx.domains import Domain, ObjType, Index
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.util.nodes import make_refnode from sphinx.util.nodes import make_refnode

View File

@ -1,9 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# nghttp2 - HTTP/2 C Library # nghttp2 - HTTP/2 C Library
#
# Copyright (c) 2020 nghttp2 contributors
# Copyright (c) 2020 ngtcp2 contributors
# Copyright (c) 2012 Tatsuhiro Tsujikawa # Copyright (c) 2012 Tatsuhiro Tsujikawa
#
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including # "Software"), to deal in the Software without restriction, including
@ -25,17 +27,16 @@
# Generates API reference from C source code. # Generates API reference from C source code.
from __future__ import unicode_literals
from __future__ import print_function # At least python 2.6 is required
import re, sys, argparse, os.path import re, sys, argparse, os.path
class FunctionDoc: class FunctionDoc:
def __init__(self, name, content, domain): def __init__(self, name, content, domain, filename):
self.name = name self.name = name
self.content = content self.content = content
self.domain = domain self.domain = domain
if self.domain == 'function': if self.domain == 'function':
self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1) self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1)
self.filename = filename
def write(self, out): def write(self, out):
out.write('.. {}:: {}\n'.format(self.domain, self.name)) out.write('.. {}:: {}\n'.format(self.domain, self.name))
@ -64,6 +65,26 @@ class StructDoc:
out.write(' {}\n'.format(line)) out.write(' {}\n'.format(line))
out.write('\n') 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: class MacroDoc:
def __init__(self, name, content): def __init__(self, name, content):
self.name = name self.name = name
@ -75,50 +96,76 @@ class MacroDoc:
for line in self.content: for line in self.content:
out.write(' {}\n'.format(line)) out.write(' {}\n'.format(line))
def make_api_ref(infiles): 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 = [] macros = []
enums = [] enums = []
types = [] types = []
functions = [] functions = []
for infile in infiles: while True:
while True: line = infile.readline()
if not line:
break
elif line == '/**\n':
line = infile.readline() line = infile.readline()
if not line: doctype = line.split()[1]
break if doctype == '@function':
elif line == '/**\n': functions.append(process_function('function', infile))
line = infile.readline() elif doctype == '@functypedef':
doctype = line.split()[1] types.append(process_function('type', infile))
if doctype == '@function': elif doctype == '@struct' or doctype == '@union':
functions.append(process_function('function', infile)) types.append(process_struct(infile))
elif doctype == '@functypedef': elif doctype == '@enum':
types.append(process_function('type', infile)) enums.append(process_enum(infile))
elif doctype == '@struct' or doctype == '@union': elif doctype == '@macro':
types.append(process_struct(infile)) macros.append(process_macro(infile))
elif doctype == '@enum': elif doctype == '@macrosection':
enums.append(process_enum(infile)) macros.append(process_macrosection(infile))
elif doctype == '@macro': elif doctype == '@typedef':
macros.append(process_macro(infile)) types.append(process_typedef(infile))
return macros, enums, types, functions return macros, enums, types, functions
alldocs = [('Macros', macros),
('Enums', enums),
('Types (structs, unions and typedefs)', types),
('Functions', functions)]
def output( def output(
indexfile, macrosfile, enumsfile, typesfile, funcsdir, title, indexfile, macrosfile, enumsfile, typesfile, funcsdir,
macros, enums, types, functions): macros, enums, types, functions):
indexfile.write(''' indexfile.write('''
API Reference {title}
============= {titledecoration}
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
macros {macros}
enums {enums}
types {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: for doc in functions:
indexfile.write(' {}\n'.format(doc.funcname)) indexfile.write(' {}\n'.format(doc.funcname))
@ -153,9 +200,10 @@ Types (structs, unions and typedefs)
Synopsis Synopsis
-------- --------
*#include <nghttp2/nghttp2.h>* *#include <nghttp2/{filename}>*
'''.format(funcname=doc.funcname, secul='='*len(doc.funcname))) '''.format(funcname=doc.funcname, secul='='*len(doc.funcname),
filename=doc.filename))
doc.write(f) doc.write(f)
def process_macro(infile): def process_macro(infile):
@ -164,6 +212,17 @@ def process_macro(infile):
macro_name = line.split()[1] macro_name = line.split()[1]
return MacroDoc(macro_name, content) 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): def process_enum(infile):
members = [] members = []
enum_name = None enum_name = None
@ -176,7 +235,7 @@ def process_enum(infile):
member_content = read_content(infile) member_content = read_content(infile)
line = infile.readline() line = infile.readline()
items = line.split() items = line.split()
member_name = items[0] member_name = items[0].rstrip(',')
if len(items) >= 3: if len(items) >= 3:
member_content.insert(0, '(``{}``) '\ member_content.insert(0, '(``{}``) '\
.format(' '.join(items[2:]).rstrip(','))) .format(' '.join(items[2:]).rstrip(',')))
@ -185,7 +244,7 @@ def process_enum(infile):
enum_name = line.rstrip().split()[1] enum_name = line.rstrip().split()[1]
enum_name = re.sub(r';$', '', enum_name) enum_name = re.sub(r';$', '', enum_name)
break break
return StructDoc(enum_name, content, members, 'macro') return EnumDoc(enum_name, content, members)
def process_struct(infile): def process_struct(infile):
members = [] members = []
@ -226,7 +285,9 @@ def process_function(domain, infile):
func_proto = re.sub(r';\n$', '', func_proto) func_proto = re.sub(r';\n$', '', func_proto)
func_proto = re.sub(r'\s+', ' ', func_proto) func_proto = re.sub(r'\s+', ' ', func_proto)
func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto) func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
return FunctionDoc(func_proto, content, domain) 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): def read_content(infile):
content = [] content = []
@ -251,6 +312,8 @@ def transform_content(content):
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Generate API reference") 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'), parser.add_argument('index', type=argparse.FileType('w'),
help='index output file') help='index output file')
parser.add_argument('macros', type=argparse.FileType('w'), parser.add_argument('macros', type=argparse.FileType('w'),
@ -269,12 +332,13 @@ if __name__ == '__main__':
types = [] types = []
funcs = [] funcs = []
for infile in args.files: for infile in args.files:
m, e, t, f = make_api_ref(args.files) m, e, t, f = make_api_ref(infile)
macros.extend(m) macros.extend(m)
enums.extend(e) enums.extend(e)
types.extend(t) types.extend(t)
funcs.extend(f) funcs.extend(f)
funcs.sort(key=lambda x: x.funcname) funcs.sort(key=lambda x: x.funcname)
output( output(
args.title,
args.index, args.macros, args.enums, args.types, args.funcsdir, args.index, args.macros, args.enums, args.types, args.funcsdir,
macros, enums, types, funcs) macros, enums, types, funcs)