qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio
at jcs-vmm 301 lines 9.7 kB view raw
1# QAPI texi generator 2# 3# This work is licensed under the terms of the GNU LGPL, version 2+. 4# See the COPYING file in the top-level directory. 5"""This script produces the documentation of a qapi schema in texinfo format""" 6 7import re 8from qapi.gen import QAPIGenDoc, QAPISchemaVisitor 9 10 11MSG_FMT = """ 12@deftypefn {type} {{}} {name} 13 14{body}{members}{features}{sections} 15@end deftypefn 16 17""".format 18 19TYPE_FMT = """ 20@deftp {{{type}}} {name} 21 22{body}{members}{features}{sections} 23@end deftp 24 25""".format 26 27EXAMPLE_FMT = """@example 28{code} 29@end example 30""".format 31 32 33def subst_strong(doc): 34 """Replaces *foo* by @strong{foo}""" 35 return re.sub(r'\*([^*\n]+)\*', r'@strong{\1}', doc) 36 37 38def subst_emph(doc): 39 """Replaces _foo_ by @emph{foo}""" 40 return re.sub(r'\b_([^_\n]+)_\b', r'@emph{\1}', doc) 41 42 43def subst_vars(doc): 44 """Replaces @var by @code{var}""" 45 return re.sub(r'@([\w-]+)', r'@code{\1}', doc) 46 47 48def subst_braces(doc): 49 """Replaces {} with @{ @}""" 50 return doc.replace('{', '@{').replace('}', '@}') 51 52 53def texi_example(doc): 54 """Format @example""" 55 # TODO: Neglects to escape @ characters. 56 # We should probably escape them in subst_braces(), and rename the 57 # function to subst_special() or subs_texi_special(). If we do that, we 58 # need to delay it until after subst_vars() in texi_format(). 59 doc = subst_braces(doc).strip('\n') 60 return EXAMPLE_FMT(code=doc) 61 62 63def texi_format(doc): 64 """ 65 Format documentation 66 67 Lines starting with: 68 - |: generates an @example 69 - =: generates @section 70 - ==: generates @subsection 71 - 1. or 1): generates an @enumerate @item 72 - */-: generates an @itemize list 73 """ 74 ret = '' 75 doc = subst_braces(doc) 76 doc = subst_vars(doc) 77 doc = subst_emph(doc) 78 doc = subst_strong(doc) 79 inlist = '' 80 lastempty = False 81 for line in doc.split('\n'): 82 empty = line == '' 83 84 # FIXME: Doing this in a single if / elif chain is 85 # problematic. For instance, a line without markup terminates 86 # a list if it follows a blank line (reaches the final elif), 87 # but a line with some *other* markup, such as a = title 88 # doesn't. 89 # 90 # Make sure to update section "Documentation markup" in 91 # docs/devel/qapi-code-gen.txt when fixing this. 92 if line.startswith('| '): 93 line = EXAMPLE_FMT(code=line[2:]) 94 elif line.startswith('= '): 95 line = '@section ' + line[2:] 96 elif line.startswith('== '): 97 line = '@subsection ' + line[3:] 98 elif re.match(r'^([0-9]*\.) ', line): 99 if not inlist: 100 ret += '@enumerate\n' 101 inlist = 'enumerate' 102 ret += '@item\n' 103 line = line[line.find(' ')+1:] 104 elif re.match(r'^[*-] ', line): 105 if not inlist: 106 ret += '@itemize %s\n' % {'*': '@bullet', 107 '-': '@minus'}[line[0]] 108 inlist = 'itemize' 109 ret += '@item\n' 110 line = line[2:] 111 elif lastempty and inlist: 112 ret += '@end %s\n\n' % inlist 113 inlist = '' 114 115 lastempty = empty 116 ret += line + '\n' 117 118 if inlist: 119 ret += '@end %s\n\n' % inlist 120 return ret 121 122 123def texi_body(doc): 124 """Format the main documentation body""" 125 return texi_format(doc.body.text) 126 127 128def texi_if(ifcond, prefix='\n', suffix='\n'): 129 """Format the #if condition""" 130 if not ifcond: 131 return '' 132 return '%s@b{If:} @code{%s}%s' % (prefix, ', '.join(ifcond), suffix) 133 134 135def texi_enum_value(value, desc, suffix): 136 """Format a table of members item for an enumeration value""" 137 return '@item @code{%s}\n%s%s' % ( 138 value.name, desc, texi_if(value.ifcond, prefix='@*')) 139 140 141def texi_member(member, desc, suffix): 142 """Format a table of members item for an object type member""" 143 typ = member.type.doc_type() 144 membertype = ': ' + typ if typ else '' 145 return '@item @code{%s%s}%s%s\n%s%s' % ( 146 member.name, membertype, 147 ' (optional)' if member.optional else '', 148 suffix, desc, texi_if(member.ifcond, prefix='@*')) 149 150 151def texi_members(doc, what, base=None, variants=None, 152 member_func=texi_member): 153 """Format the table of members""" 154 items = '' 155 for section in doc.args.values(): 156 # TODO Drop fallbacks when undocumented members are outlawed 157 if section.text: 158 desc = texi_format(section.text) 159 elif (variants and variants.tag_member == section.member 160 and not section.member.type.doc_type()): 161 values = section.member.type.member_names() 162 members_text = ', '.join(['@t{"%s"}' % v for v in values]) 163 desc = 'One of ' + members_text + '\n' 164 else: 165 desc = 'Not documented\n' 166 items += member_func(section.member, desc, suffix='') 167 if base: 168 items += '@item The members of @code{%s}\n' % base.doc_type() 169 if variants: 170 for v in variants.variants: 171 when = ' when @code{%s} is @t{"%s"}%s' % ( 172 variants.tag_member.name, v.name, texi_if(v.ifcond, " (", ")")) 173 if v.type.is_implicit(): 174 assert not v.type.base and not v.type.variants 175 for m in v.type.local_members: 176 items += member_func(m, desc='', suffix=when) 177 else: 178 items += '@item The members of @code{%s}%s\n' % ( 179 v.type.doc_type(), when) 180 if not items: 181 return '' 182 return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items) 183 184 185def texi_arguments(doc, boxed_arg_type): 186 if boxed_arg_type: 187 assert not doc.args 188 return ('\n@b{Arguments:} the members of @code{%s}\n' 189 % boxed_arg_type.name) 190 return texi_members(doc, 'Arguments') 191 192 193def texi_features(doc): 194 """Format the table of features""" 195 items = '' 196 for section in doc.features.values(): 197 desc = texi_format(section.text) 198 items += '@item @code{%s}\n%s' % (section.name, desc) 199 if not items: 200 return '' 201 return '\n@b{Features:}\n@table @asis\n%s@end table\n' % (items) 202 203 204def texi_sections(doc, ifcond): 205 """Format additional sections following arguments""" 206 body = '' 207 for section in doc.sections: 208 if section.name: 209 # prefer @b over @strong, so txt doesn't translate it to *Foo:* 210 body += '\n@b{%s:}\n' % section.name 211 if section.name and section.name.startswith('Example'): 212 body += texi_example(section.text) 213 else: 214 body += texi_format(section.text) 215 body += texi_if(ifcond, suffix='') 216 return body 217 218 219def texi_type(typ, doc, ifcond, members): 220 return TYPE_FMT(type=typ, 221 name=doc.symbol, 222 body=texi_body(doc), 223 members=members, 224 features=texi_features(doc), 225 sections=texi_sections(doc, ifcond)) 226 227 228def texi_msg(typ, doc, ifcond, members): 229 return MSG_FMT(type=typ, 230 name=doc.symbol, 231 body=texi_body(doc), 232 members=members, 233 features=texi_features(doc), 234 sections=texi_sections(doc, ifcond)) 235 236 237class QAPISchemaGenDocVisitor(QAPISchemaVisitor): 238 def __init__(self, prefix): 239 self._prefix = prefix 240 self._gen = QAPIGenDoc(self._prefix + 'qapi-doc.texi') 241 self.cur_doc = None 242 243 def write(self, output_dir): 244 self._gen.write(output_dir) 245 246 def visit_enum_type(self, name, info, ifcond, features, members, prefix): 247 doc = self.cur_doc 248 self._gen.add(texi_type('Enum', doc, ifcond, 249 texi_members(doc, 'Values', 250 member_func=texi_enum_value))) 251 252 def visit_object_type(self, name, info, ifcond, features, 253 base, members, variants): 254 doc = self.cur_doc 255 if base and base.is_implicit(): 256 base = None 257 self._gen.add(texi_type('Object', doc, ifcond, 258 texi_members(doc, 'Members', base, variants))) 259 260 def visit_alternate_type(self, name, info, ifcond, features, variants): 261 doc = self.cur_doc 262 self._gen.add(texi_type('Alternate', doc, ifcond, 263 texi_members(doc, 'Members'))) 264 265 def visit_command(self, name, info, ifcond, features, 266 arg_type, ret_type, gen, success_response, boxed, 267 allow_oob, allow_preconfig): 268 doc = self.cur_doc 269 self._gen.add(texi_msg('Command', doc, ifcond, 270 texi_arguments(doc, 271 arg_type if boxed else None))) 272 273 def visit_event(self, name, info, ifcond, features, arg_type, boxed): 274 doc = self.cur_doc 275 self._gen.add(texi_msg('Event', doc, ifcond, 276 texi_arguments(doc, 277 arg_type if boxed else None))) 278 279 def symbol(self, doc, entity): 280 if self._gen._body: 281 self._gen.add('\n') 282 self.cur_doc = doc 283 entity.visit(self) 284 self.cur_doc = None 285 286 def freeform(self, doc): 287 assert not doc.args 288 if self._gen._body: 289 self._gen.add('\n') 290 self._gen.add(texi_body(doc) + texi_sections(doc, None)) 291 292 293def gen_doc(schema, output_dir, prefix): 294 vis = QAPISchemaGenDocVisitor(prefix) 295 vis.visit_begin(schema) 296 for doc in schema.docs: 297 if doc.symbol: 298 vis.symbol(doc, schema.lookup_entity(doc.symbol)) 299 else: 300 vis.freeform(doc) 301 vis.write(output_dir)