mkapiref.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. #!/usr/bin/env python
  2. #
  3. # aria2 - The high speed download utility
  4. #
  5. # Copyright (C) 2013 Tatsuhiro Tsujikawa
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. #
  21. # In addition, as a special exception, the copyright holders give
  22. # permission to link the code of portions of this program with the
  23. # OpenSSL library under certain conditions as described in each
  24. # individual source file, and distribute linked combinations
  25. # including the two.
  26. # You must obey the GNU General Public License in all respects
  27. # for all of the code used other than OpenSSL. If you modify
  28. # file(s) with this exception, you may extend this exception to your
  29. # version of the file(s), but you are not obligated to do so. If you
  30. # do not wish to do so, delete this exception statement from your
  31. # version. If you delete this exception statement from all source
  32. # files in the program, then also delete it here.
  33. #
  34. # Generates API reference from C++ source code.
  35. from __future__ import print_function
  36. import re, sys, argparse
  37. class FunctionDoc:
  38. def __init__(self, name, content, domain):
  39. self.name = name
  40. self.content = content
  41. self.domain = domain
  42. def write(self, out):
  43. print('''.. {}:: {}'''.format(self.domain, self.name))
  44. print()
  45. for line in self.content:
  46. print(' {}'.format(line))
  47. class TypedefDoc:
  48. def __init__(self, name, content):
  49. self.name = name
  50. self.content = content
  51. def write(self, out):
  52. print('''.. type:: {}'''.format(self.name))
  53. print()
  54. for line in self.content:
  55. print(' {}'.format(line))
  56. class StructDoc:
  57. def __init__(self, name, content, domain, members, member_domain):
  58. self.name = name
  59. self.content = content
  60. self.domain = domain
  61. self.members = members
  62. self.member_domain = member_domain
  63. def write(self, out):
  64. if self.name:
  65. print('''.. {}:: {}'''.format(self.domain, self.name))
  66. print()
  67. for line in self.content:
  68. print(' {}'.format(line))
  69. print()
  70. for name, content in self.members:
  71. print(''' .. {}:: {}'''.format(\
  72. 'function' if name.endswith(')') else self.member_domain,
  73. name))
  74. print()
  75. for line in content:
  76. print(''' {}'''.format(line))
  77. print()
  78. class MacroDoc:
  79. def __init__(self, name, content):
  80. self.name = name
  81. self.content = content
  82. def write(self, out):
  83. print('''.. macro:: {}'''.format(self.name))
  84. print()
  85. for line in self.content:
  86. print(' {}'.format(line))
  87. def make_api_ref(infiles):
  88. macros = []
  89. enums = []
  90. types = []
  91. functions = []
  92. for infile in infiles:
  93. while True:
  94. line = infile.readline()
  95. if not line:
  96. break
  97. elif line == '/**\n':
  98. line = infile.readline()
  99. doctype = line.split()[1]
  100. if doctype == '@function':
  101. functions.append(process_function('function', infile))
  102. if doctype == '@functypedef':
  103. types.append(process_function('c:type', infile))
  104. elif doctype == '@typedef':
  105. types.append(process_typedef(infile))
  106. elif doctype in ['@class', '@struct', '@union']:
  107. types.append(process_struct(infile))
  108. elif doctype == '@enum':
  109. enums.append(process_enum(infile))
  110. elif doctype == '@macro':
  111. macros.append(process_macro(infile))
  112. alldocs = [('Macros', macros),
  113. ('Enums', enums),
  114. ('Types (classes, structs, unions and typedefs)', types),
  115. ('Functions', functions)]
  116. for title, docs in alldocs:
  117. if not docs:
  118. continue
  119. print(title)
  120. print('-'*len(title))
  121. for doc in docs:
  122. doc.write(sys.stdout)
  123. print()
  124. print()
  125. def process_macro(infile):
  126. content = read_content(infile)
  127. line = infile.readline()
  128. macro_name = line.split()[1]
  129. return MacroDoc(macro_name, content)
  130. def process_enum(infile):
  131. members = []
  132. enum_name = None
  133. content = read_content(infile)
  134. while True:
  135. line = infile.readline()
  136. if not line:
  137. break
  138. elif re.match(r'\s*/\*\*\n', line):
  139. member_content = read_content(infile)
  140. line = infile.readline()
  141. items = line.split()
  142. member_name = items[0].rstrip(',')
  143. if len(items) >= 3:
  144. member_content.insert(0, '(``{}``) '\
  145. .format(items[2].rstrip(',')))
  146. members.append((member_name, member_content))
  147. elif line.startswith('}'):
  148. if not enum_name:
  149. enum_name = line.rstrip().split()[1]
  150. enum_name = re.sub(r';$', '', enum_name)
  151. break
  152. elif not enum_name:
  153. m = re.match(r'^\s*enum\s+([\S]+)\s*{\s*', line)
  154. if m:
  155. enum_name = m.group(1)
  156. return StructDoc(enum_name, content, 'type', members, 'c:macro')
  157. def process_struct(infile):
  158. members = []
  159. domain = 'type'
  160. struct_name = None
  161. content = read_content(infile)
  162. while True:
  163. line = infile.readline()
  164. if not line:
  165. break
  166. elif re.match(r'\s*/\*\*\n', line):
  167. member_content = read_content(infile)
  168. line = infile.readline()
  169. member_name = line.rstrip().rstrip(';')
  170. member_name = re.sub(r'\)\s*=\s*0', ')', member_name)
  171. member_name = re.sub(r' virtual ', '', member_name)
  172. members.append((member_name, member_content))
  173. elif line.startswith('}') or\
  174. (line.startswith('typedef ') and line.endswith(';\n')):
  175. if not struct_name:
  176. if line.startswith('}'):
  177. index = 1
  178. else:
  179. index = 3
  180. struct_name = line.rstrip().split()[index]
  181. struct_name = re.sub(r';$', '', struct_name)
  182. break
  183. elif not struct_name:
  184. m = re.match(r'^\s*(struct|class)\s+([\S]+)\s*(?:{|;)', line)
  185. if m:
  186. domain = m.group(1)
  187. if domain == 'struct':
  188. domain = 'type'
  189. struct_name = m.group(2)
  190. if line.endswith(';\n'):
  191. break
  192. return StructDoc(struct_name, content, domain, members, 'member')
  193. def process_function(domain, infile):
  194. content = read_content(infile)
  195. func_proto = []
  196. while True:
  197. line = infile.readline()
  198. if not line:
  199. break
  200. elif line == '\n':
  201. break
  202. else:
  203. func_proto.append(line)
  204. func_proto = ''.join(func_proto)
  205. func_proto = re.sub(r';\n$', '', func_proto)
  206. func_proto = re.sub(r'\s+', ' ', func_proto)
  207. return FunctionDoc(func_proto, content, domain)
  208. def process_typedef(infile):
  209. content = read_content(infile)
  210. lines = []
  211. while True:
  212. line = infile.readline()
  213. if not line:
  214. break
  215. elif line == '\n':
  216. break
  217. else:
  218. lines.append(line)
  219. typedef = ''.join(lines)
  220. typedef = re.sub(r';\n$', '', typedef)
  221. typedef = re.sub(r'\s+', ' ', typedef)
  222. return TypedefDoc(typedef.split()[-1], content)
  223. def read_content(infile):
  224. content = []
  225. while True:
  226. line = infile.readline()
  227. if not line:
  228. break
  229. if re.match(r'\s*\*/\n', line):
  230. break
  231. else:
  232. content.append(transform_content(line.rstrip()))
  233. return content
  234. def arg_repl(matchobj):
  235. return '*{}*'.format(matchobj.group(1).replace('*', '\\*'))
  236. def transform_content(content):
  237. content = re.sub(r'^\s+\* ?', '', content)
  238. content = re.sub(r'\|([^\s|]+)\|', arg_repl, content)
  239. content = re.sub(r':enum:', ':macro:', content)
  240. return content
  241. if __name__ == '__main__':
  242. parser = argparse.ArgumentParser(description="Generate API reference")
  243. parser.add_argument('--header', type=argparse.FileType('rb', 0),
  244. help='header inserted at the top of the page')
  245. parser.add_argument('files', nargs='+', type=argparse.FileType('rb', 0),
  246. help='source file')
  247. args = parser.parse_args()
  248. if args.header:
  249. print(args.header.read())
  250. for infile in args.files:
  251. make_api_ref(args.files)