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 30import re, sys, argparse, os.path 31 32class 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 47class 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 68class 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 88class 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 99class 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 111class 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 122def 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 150def 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(''' 174Macros 175====== 176''') 177 for doc in macros: 178 doc.write(macrosfile) 179 180 enumsfile.write(''' 181Enums 182===== 183''') 184 for doc in enums: 185 doc.write(enumsfile) 186 187 typesfile.write(''' 188Types (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 200Synopsis 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 209def 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 215def process_macrosection(infile): 216 content = read_content(infile) 217 return MacroSectionDoc(content) 218 219def 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 226def 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 249def 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 273def 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 292def 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 304def arg_repl(matchobj): 305 return '*{}*'.format(matchobj.group(1).replace('*', '\\*')) 306 307def 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 313if __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) 345