1# 2# QAPI helper library 3# 4# Copyright IBM, Corp. 2011 5# Copyright (c) 2013-2018 Red Hat Inc. 6# 7# Authors: 8# Anthony Liguori <aliguori@us.ibm.com> 9# Markus Armbruster <armbru@redhat.com> 10# 11# This work is licensed under the terms of the GNU GPL, version 2. 12# See the COPYING file in the top-level directory. 13 14from __future__ import print_function 15import errno 16import os 17import re 18import string 19import sys 20from collections import OrderedDict 21 22builtin_types = { 23 'null': 'QTYPE_QNULL', 24 'str': 'QTYPE_QSTRING', 25 'int': 'QTYPE_QNUM', 26 'number': 'QTYPE_QNUM', 27 'bool': 'QTYPE_QBOOL', 28 'int8': 'QTYPE_QNUM', 29 'int16': 'QTYPE_QNUM', 30 'int32': 'QTYPE_QNUM', 31 'int64': 'QTYPE_QNUM', 32 'uint8': 'QTYPE_QNUM', 33 'uint16': 'QTYPE_QNUM', 34 'uint32': 'QTYPE_QNUM', 35 'uint64': 'QTYPE_QNUM', 36 'size': 'QTYPE_QNUM', 37 'any': None, # any QType possible, actually 38 'QType': 'QTYPE_QSTRING', 39} 40 41# Are documentation comments required? 42doc_required = False 43 44# Whitelist of commands allowed to return a non-dictionary 45returns_whitelist = [] 46 47# Whitelist of entities allowed to violate case conventions 48name_case_whitelist = [] 49 50enum_types = {} 51struct_types = {} 52union_types = {} 53all_names = {} 54 55# 56# Parsing the schema into expressions 57# 58 59 60def error_path(parent): 61 res = '' 62 while parent: 63 res = ('In file included from %s:%d:\n' % (parent['file'], 64 parent['line'])) + res 65 parent = parent['parent'] 66 return res 67 68 69class QAPIError(Exception): 70 def __init__(self, fname, line, col, incl_info, msg): 71 Exception.__init__(self) 72 self.fname = fname 73 self.line = line 74 self.col = col 75 self.info = incl_info 76 self.msg = msg 77 78 def __str__(self): 79 loc = '%s:%d' % (self.fname, self.line) 80 if self.col is not None: 81 loc += ':%s' % self.col 82 return error_path(self.info) + '%s: %s' % (loc, self.msg) 83 84 85class QAPIParseError(QAPIError): 86 def __init__(self, parser, msg): 87 col = 1 88 for ch in parser.src[parser.line_pos:parser.pos]: 89 if ch == '\t': 90 col = (col + 7) % 8 + 1 91 else: 92 col += 1 93 QAPIError.__init__(self, parser.fname, parser.line, col, 94 parser.incl_info, msg) 95 96 97class QAPISemError(QAPIError): 98 def __init__(self, info, msg): 99 QAPIError.__init__(self, info['file'], info['line'], None, 100 info['parent'], msg) 101 102 103class QAPIDoc(object): 104 class Section(object): 105 def __init__(self, name=None): 106 # optional section name (argument/member or section name) 107 self.name = name 108 # the list of lines for this section 109 self.text = '' 110 111 def append(self, line): 112 self.text += line.rstrip() + '\n' 113 114 class ArgSection(Section): 115 def __init__(self, name): 116 QAPIDoc.Section.__init__(self, name) 117 self.member = None 118 119 def connect(self, member): 120 self.member = member 121 122 def __init__(self, parser, info): 123 # self._parser is used to report errors with QAPIParseError. The 124 # resulting error position depends on the state of the parser. 125 # It happens to be the beginning of the comment. More or less 126 # servicable, but action at a distance. 127 self._parser = parser 128 self.info = info 129 self.symbol = None 130 self.body = QAPIDoc.Section() 131 # dict mapping parameter name to ArgSection 132 self.args = OrderedDict() 133 # a list of Section 134 self.sections = [] 135 # the current section 136 self._section = self.body 137 138 def has_section(self, name): 139 """Return True if we have a section with this name.""" 140 for i in self.sections: 141 if i.name == name: 142 return True 143 return False 144 145 def append(self, line): 146 """Parse a comment line and add it to the documentation.""" 147 line = line[1:] 148 if not line: 149 self._append_freeform(line) 150 return 151 152 if line[0] != ' ': 153 raise QAPIParseError(self._parser, "Missing space after #") 154 line = line[1:] 155 156 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't 157 # recognized, and get silently treated as ordinary text 158 if self.symbol: 159 self._append_symbol_line(line) 160 elif not self.body.text and line.startswith('@'): 161 if not line.endswith(':'): 162 raise QAPIParseError(self._parser, "Line should end with :") 163 self.symbol = line[1:-1] 164 # FIXME invalid names other than the empty string aren't flagged 165 if not self.symbol: 166 raise QAPIParseError(self._parser, "Invalid name") 167 else: 168 self._append_freeform(line) 169 170 def end_comment(self): 171 self._end_section() 172 173 def _append_symbol_line(self, line): 174 name = line.split(' ', 1)[0] 175 176 if name.startswith('@') and name.endswith(':'): 177 line = line[len(name)+1:] 178 self._start_args_section(name[1:-1]) 179 elif name in ('Returns:', 'Since:', 180 # those are often singular or plural 181 'Note:', 'Notes:', 182 'Example:', 'Examples:', 183 'TODO:'): 184 line = line[len(name)+1:] 185 self._start_section(name[:-1]) 186 187 self._append_freeform(line) 188 189 def _start_args_section(self, name): 190 # FIXME invalid names other than the empty string aren't flagged 191 if not name: 192 raise QAPIParseError(self._parser, "Invalid parameter name") 193 if name in self.args: 194 raise QAPIParseError(self._parser, 195 "'%s' parameter name duplicated" % name) 196 if self.sections: 197 raise QAPIParseError(self._parser, 198 "'@%s:' can't follow '%s' section" 199 % (name, self.sections[0].name)) 200 self._end_section() 201 self._section = QAPIDoc.ArgSection(name) 202 self.args[name] = self._section 203 204 def _start_section(self, name=None): 205 if name in ('Returns', 'Since') and self.has_section(name): 206 raise QAPIParseError(self._parser, 207 "Duplicated '%s' section" % name) 208 self._end_section() 209 self._section = QAPIDoc.Section(name) 210 self.sections.append(self._section) 211 212 def _end_section(self): 213 if self._section: 214 text = self._section.text = self._section.text.strip() 215 if self._section.name and (not text or text.isspace()): 216 raise QAPIParseError(self._parser, "Empty doc section '%s'" 217 % self._section.name) 218 self._section = None 219 220 def _append_freeform(self, line): 221 in_arg = isinstance(self._section, QAPIDoc.ArgSection) 222 if (in_arg and self._section.text.endswith('\n\n') 223 and line and not line[0].isspace()): 224 self._start_section() 225 if (in_arg or not self._section.name 226 or not self._section.name.startswith('Example')): 227 line = line.strip() 228 match = re.match(r'(@\S+:)', line) 229 if match: 230 raise QAPIParseError(self._parser, 231 "'%s' not allowed in free-form documentation" 232 % match.group(1)) 233 self._section.append(line) 234 235 def connect_member(self, member): 236 if member.name not in self.args: 237 # Undocumented TODO outlaw 238 self.args[member.name] = QAPIDoc.ArgSection(member.name) 239 self.args[member.name].connect(member) 240 241 def check_expr(self, expr): 242 if self.has_section('Returns') and 'command' not in expr: 243 raise QAPISemError(self.info, 244 "'Returns:' is only valid for commands") 245 246 def check(self): 247 bogus = [name for name, section in self.args.items() 248 if not section.member] 249 if bogus: 250 raise QAPISemError( 251 self.info, 252 "The following documented members are not in " 253 "the declaration: %s" % ", ".join(bogus)) 254 255 256class QAPISchemaParser(object): 257 258 def __init__(self, fp, previously_included=[], incl_info=None): 259 self.fname = fp.name 260 previously_included.append(os.path.abspath(fp.name)) 261 self.incl_info = incl_info 262 self.src = fp.read() 263 if self.src == '' or self.src[-1] != '\n': 264 self.src += '\n' 265 self.cursor = 0 266 self.line = 1 267 self.line_pos = 0 268 self.exprs = [] 269 self.docs = [] 270 self.accept() 271 cur_doc = None 272 273 while self.tok is not None: 274 info = {'file': self.fname, 'line': self.line, 275 'parent': self.incl_info} 276 if self.tok == '#': 277 self.reject_expr_doc(cur_doc) 278 cur_doc = self.get_doc(info) 279 self.docs.append(cur_doc) 280 continue 281 282 expr = self.get_expr(False) 283 if 'include' in expr: 284 self.reject_expr_doc(cur_doc) 285 if len(expr) != 1: 286 raise QAPISemError(info, "Invalid 'include' directive") 287 include = expr['include'] 288 if not isinstance(include, str): 289 raise QAPISemError(info, 290 "Value of 'include' must be a string") 291 incl_fname = os.path.join(os.path.dirname(self.fname), 292 include) 293 self.exprs.append({'expr': {'include': incl_fname}, 294 'info': info}) 295 exprs_include = self._include(include, info, incl_fname, 296 previously_included) 297 if exprs_include: 298 self.exprs.extend(exprs_include.exprs) 299 self.docs.extend(exprs_include.docs) 300 elif "pragma" in expr: 301 self.reject_expr_doc(cur_doc) 302 if len(expr) != 1: 303 raise QAPISemError(info, "Invalid 'pragma' directive") 304 pragma = expr['pragma'] 305 if not isinstance(pragma, dict): 306 raise QAPISemError( 307 info, "Value of 'pragma' must be a dictionary") 308 for name, value in pragma.items(): 309 self._pragma(name, value, info) 310 else: 311 expr_elem = {'expr': expr, 312 'info': info} 313 if cur_doc: 314 if not cur_doc.symbol: 315 raise QAPISemError( 316 cur_doc.info, "Expression documentation required") 317 expr_elem['doc'] = cur_doc 318 self.exprs.append(expr_elem) 319 cur_doc = None 320 self.reject_expr_doc(cur_doc) 321 322 @staticmethod 323 def reject_expr_doc(doc): 324 if doc and doc.symbol: 325 raise QAPISemError( 326 doc.info, 327 "Documentation for '%s' is not followed by the definition" 328 % doc.symbol) 329 330 def _include(self, include, info, incl_fname, previously_included): 331 incl_abs_fname = os.path.abspath(incl_fname) 332 # catch inclusion cycle 333 inf = info 334 while inf: 335 if incl_abs_fname == os.path.abspath(inf['file']): 336 raise QAPISemError(info, "Inclusion loop for %s" % include) 337 inf = inf['parent'] 338 339 # skip multiple include of the same file 340 if incl_abs_fname in previously_included: 341 return None 342 343 try: 344 if sys.version_info[0] >= 3: 345 fobj = open(incl_fname, 'r', encoding='utf-8') 346 else: 347 fobj = open(incl_fname, 'r') 348 except IOError as e: 349 raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname)) 350 return QAPISchemaParser(fobj, previously_included, info) 351 352 def _pragma(self, name, value, info): 353 global doc_required, returns_whitelist, name_case_whitelist 354 if name == 'doc-required': 355 if not isinstance(value, bool): 356 raise QAPISemError(info, 357 "Pragma 'doc-required' must be boolean") 358 doc_required = value 359 elif name == 'returns-whitelist': 360 if (not isinstance(value, list) 361 or any([not isinstance(elt, str) for elt in value])): 362 raise QAPISemError(info, 363 "Pragma returns-whitelist must be" 364 " a list of strings") 365 returns_whitelist = value 366 elif name == 'name-case-whitelist': 367 if (not isinstance(value, list) 368 or any([not isinstance(elt, str) for elt in value])): 369 raise QAPISemError(info, 370 "Pragma name-case-whitelist must be" 371 " a list of strings") 372 name_case_whitelist = value 373 else: 374 raise QAPISemError(info, "Unknown pragma '%s'" % name) 375 376 def accept(self, skip_comment=True): 377 while True: 378 self.tok = self.src[self.cursor] 379 self.pos = self.cursor 380 self.cursor += 1 381 self.val = None 382 383 if self.tok == '#': 384 if self.src[self.cursor] == '#': 385 # Start of doc comment 386 skip_comment = False 387 self.cursor = self.src.find('\n', self.cursor) 388 if not skip_comment: 389 self.val = self.src[self.pos:self.cursor] 390 return 391 elif self.tok in '{}:,[]': 392 return 393 elif self.tok == "'": 394 string = '' 395 esc = False 396 while True: 397 ch = self.src[self.cursor] 398 self.cursor += 1 399 if ch == '\n': 400 raise QAPIParseError(self, 'Missing terminating "\'"') 401 if esc: 402 if ch == 'b': 403 string += '\b' 404 elif ch == 'f': 405 string += '\f' 406 elif ch == 'n': 407 string += '\n' 408 elif ch == 'r': 409 string += '\r' 410 elif ch == 't': 411 string += '\t' 412 elif ch == 'u': 413 value = 0 414 for _ in range(0, 4): 415 ch = self.src[self.cursor] 416 self.cursor += 1 417 if ch not in '0123456789abcdefABCDEF': 418 raise QAPIParseError(self, 419 '\\u escape needs 4 ' 420 'hex digits') 421 value = (value << 4) + int(ch, 16) 422 # If Python 2 and 3 didn't disagree so much on 423 # how to handle Unicode, then we could allow 424 # Unicode string defaults. But most of QAPI is 425 # ASCII-only, so we aren't losing much for now. 426 if not value or value > 0x7f: 427 raise QAPIParseError(self, 428 'For now, \\u escape ' 429 'only supports non-zero ' 430 'values up to \\u007f') 431 string += chr(value) 432 elif ch in '\\/\'"': 433 string += ch 434 else: 435 raise QAPIParseError(self, 436 "Unknown escape \\%s" % ch) 437 esc = False 438 elif ch == '\\': 439 esc = True 440 elif ch == "'": 441 self.val = string 442 return 443 else: 444 string += ch 445 elif self.src.startswith('true', self.pos): 446 self.val = True 447 self.cursor += 3 448 return 449 elif self.src.startswith('false', self.pos): 450 self.val = False 451 self.cursor += 4 452 return 453 elif self.src.startswith('null', self.pos): 454 self.val = None 455 self.cursor += 3 456 return 457 elif self.tok == '\n': 458 if self.cursor == len(self.src): 459 self.tok = None 460 return 461 self.line += 1 462 self.line_pos = self.cursor 463 elif not self.tok.isspace(): 464 raise QAPIParseError(self, 'Stray "%s"' % self.tok) 465 466 def get_members(self): 467 expr = OrderedDict() 468 if self.tok == '}': 469 self.accept() 470 return expr 471 if self.tok != "'": 472 raise QAPIParseError(self, 'Expected string or "}"') 473 while True: 474 key = self.val 475 self.accept() 476 if self.tok != ':': 477 raise QAPIParseError(self, 'Expected ":"') 478 self.accept() 479 if key in expr: 480 raise QAPIParseError(self, 'Duplicate key "%s"' % key) 481 expr[key] = self.get_expr(True) 482 if self.tok == '}': 483 self.accept() 484 return expr 485 if self.tok != ',': 486 raise QAPIParseError(self, 'Expected "," or "}"') 487 self.accept() 488 if self.tok != "'": 489 raise QAPIParseError(self, 'Expected string') 490 491 def get_values(self): 492 expr = [] 493 if self.tok == ']': 494 self.accept() 495 return expr 496 if self.tok not in "{['tfn": 497 raise QAPIParseError(self, 'Expected "{", "[", "]", string, ' 498 'boolean or "null"') 499 while True: 500 expr.append(self.get_expr(True)) 501 if self.tok == ']': 502 self.accept() 503 return expr 504 if self.tok != ',': 505 raise QAPIParseError(self, 'Expected "," or "]"') 506 self.accept() 507 508 def get_expr(self, nested): 509 if self.tok != '{' and not nested: 510 raise QAPIParseError(self, 'Expected "{"') 511 if self.tok == '{': 512 self.accept() 513 expr = self.get_members() 514 elif self.tok == '[': 515 self.accept() 516 expr = self.get_values() 517 elif self.tok in "'tfn": 518 expr = self.val 519 self.accept() 520 else: 521 raise QAPIParseError(self, 'Expected "{", "[", string, ' 522 'boolean or "null"') 523 return expr 524 525 def get_doc(self, info): 526 if self.val != '##': 527 raise QAPIParseError(self, "Junk after '##' at start of " 528 "documentation comment") 529 530 doc = QAPIDoc(self, info) 531 self.accept(False) 532 while self.tok == '#': 533 if self.val.startswith('##'): 534 # End of doc comment 535 if self.val != '##': 536 raise QAPIParseError(self, "Junk after '##' at end of " 537 "documentation comment") 538 doc.end_comment() 539 self.accept() 540 return doc 541 else: 542 doc.append(self.val) 543 self.accept(False) 544 545 raise QAPIParseError(self, "Documentation comment must end with '##'") 546 547 548# 549# Semantic analysis of schema expressions 550# TODO fold into QAPISchema 551# TODO catching name collisions in generated code would be nice 552# 553 554 555def find_base_members(base): 556 if isinstance(base, dict): 557 return base 558 base_struct_define = struct_types.get(base) 559 if not base_struct_define: 560 return None 561 return base_struct_define['data'] 562 563 564# Return the qtype of an alternate branch, or None on error. 565def find_alternate_member_qtype(qapi_type): 566 if qapi_type in builtin_types: 567 return builtin_types[qapi_type] 568 elif qapi_type in struct_types: 569 return 'QTYPE_QDICT' 570 elif qapi_type in enum_types: 571 return 'QTYPE_QSTRING' 572 elif qapi_type in union_types: 573 return 'QTYPE_QDICT' 574 return None 575 576 577# Return the discriminator enum define if discriminator is specified as an 578# enum type, otherwise return None. 579def discriminator_find_enum_define(expr): 580 base = expr.get('base') 581 discriminator = expr.get('discriminator') 582 583 if not (discriminator and base): 584 return None 585 586 base_members = find_base_members(base) 587 if not base_members: 588 return None 589 590 discriminator_type = base_members.get(discriminator) 591 if not discriminator_type: 592 return None 593 594 return enum_types.get(discriminator_type) 595 596 597# Names must be letters, numbers, -, and _. They must start with letter, 598# except for downstream extensions which must start with __RFQDN_. 599# Dots are only valid in the downstream extension prefix. 600valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?' 601 '[a-zA-Z][a-zA-Z0-9_-]*$') 602 603 604def check_name(info, source, name, allow_optional=False, 605 enum_member=False): 606 global valid_name 607 membername = name 608 609 if not isinstance(name, str): 610 raise QAPISemError(info, "%s requires a string name" % source) 611 if name.startswith('*'): 612 membername = name[1:] 613 if not allow_optional: 614 raise QAPISemError(info, "%s does not allow optional name '%s'" 615 % (source, name)) 616 # Enum members can start with a digit, because the generated C 617 # code always prefixes it with the enum name 618 if enum_member and membername[0].isdigit(): 619 membername = 'D' + membername 620 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty' 621 # and 'q_obj_*' implicit type names. 622 if not valid_name.match(membername) or \ 623 c_name(membername, False).startswith('q_'): 624 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name)) 625 626 627def add_name(name, info, meta, implicit=False): 628 global all_names 629 check_name(info, "'%s'" % meta, name) 630 # FIXME should reject names that differ only in '_' vs. '.' 631 # vs. '-', because they're liable to clash in generated C. 632 if name in all_names: 633 raise QAPISemError(info, "%s '%s' is already defined" 634 % (all_names[name], name)) 635 if not implicit and (name.endswith('Kind') or name.endswith('List')): 636 raise QAPISemError(info, "%s '%s' should not end in '%s'" 637 % (meta, name, name[-4:])) 638 all_names[name] = meta 639 640 641def check_if(expr, info): 642 643 def check_if_str(ifcond, info): 644 if not isinstance(ifcond, str): 645 raise QAPISemError( 646 info, "'if' condition must be a string or a list of strings") 647 if ifcond == '': 648 raise QAPISemError(info, "'if' condition '' makes no sense") 649 650 ifcond = expr.get('if') 651 if ifcond is None: 652 return 653 if isinstance(ifcond, list): 654 if ifcond == []: 655 raise QAPISemError(info, "'if' condition [] is useless") 656 for elt in ifcond: 657 check_if_str(elt, info) 658 else: 659 check_if_str(ifcond, info) 660 661 662def check_type(info, source, value, allow_array=False, 663 allow_dict=False, allow_optional=False, 664 allow_metas=[]): 665 global all_names 666 667 if value is None: 668 return 669 670 # Check if array type for value is okay 671 if isinstance(value, list): 672 if not allow_array: 673 raise QAPISemError(info, "%s cannot be an array" % source) 674 if len(value) != 1 or not isinstance(value[0], str): 675 raise QAPISemError(info, 676 "%s: array type must contain single type name" % 677 source) 678 value = value[0] 679 680 # Check if type name for value is okay 681 if isinstance(value, str): 682 if value not in all_names: 683 raise QAPISemError(info, "%s uses unknown type '%s'" 684 % (source, value)) 685 if not all_names[value] in allow_metas: 686 raise QAPISemError(info, "%s cannot use %s type '%s'" % 687 (source, all_names[value], value)) 688 return 689 690 if not allow_dict: 691 raise QAPISemError(info, "%s should be a type name" % source) 692 693 if not isinstance(value, OrderedDict): 694 raise QAPISemError(info, 695 "%s should be a dictionary or type name" % source) 696 697 # value is a dictionary, check that each member is okay 698 for (key, arg) in value.items(): 699 check_name(info, "Member of %s" % source, key, 700 allow_optional=allow_optional) 701 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): 702 raise QAPISemError(info, "Member of %s uses reserved name '%s'" 703 % (source, key)) 704 # Todo: allow dictionaries to represent default values of 705 # an optional argument. 706 check_type(info, "Member '%s' of %s" % (key, source), arg, 707 allow_array=True, 708 allow_metas=['built-in', 'union', 'alternate', 'struct', 709 'enum']) 710 711 712def check_command(expr, info): 713 name = expr['command'] 714 boxed = expr.get('boxed', False) 715 716 args_meta = ['struct'] 717 if boxed: 718 args_meta += ['union', 'alternate'] 719 check_type(info, "'data' for command '%s'" % name, 720 expr.get('data'), allow_dict=not boxed, allow_optional=True, 721 allow_metas=args_meta) 722 returns_meta = ['union', 'struct'] 723 if name in returns_whitelist: 724 returns_meta += ['built-in', 'alternate', 'enum'] 725 check_type(info, "'returns' for command '%s'" % name, 726 expr.get('returns'), allow_array=True, 727 allow_optional=True, allow_metas=returns_meta) 728 729 730def check_event(expr, info): 731 name = expr['event'] 732 boxed = expr.get('boxed', False) 733 734 meta = ['struct'] 735 if boxed: 736 meta += ['union', 'alternate'] 737 check_type(info, "'data' for event '%s'" % name, 738 expr.get('data'), allow_dict=not boxed, allow_optional=True, 739 allow_metas=meta) 740 741 742def check_union(expr, info): 743 name = expr['union'] 744 base = expr.get('base') 745 discriminator = expr.get('discriminator') 746 members = expr['data'] 747 748 # Two types of unions, determined by discriminator. 749 750 # With no discriminator it is a simple union. 751 if discriminator is None: 752 enum_define = None 753 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum'] 754 if base is not None: 755 raise QAPISemError(info, "Simple union '%s' must not have a base" % 756 name) 757 758 # Else, it's a flat union. 759 else: 760 # The object must have a string or dictionary 'base'. 761 check_type(info, "'base' for union '%s'" % name, 762 base, allow_dict=True, allow_optional=True, 763 allow_metas=['struct']) 764 if not base: 765 raise QAPISemError(info, "Flat union '%s' must have a base" 766 % name) 767 base_members = find_base_members(base) 768 assert base_members is not None 769 770 # The value of member 'discriminator' must name a non-optional 771 # member of the base struct. 772 check_name(info, "Discriminator of flat union '%s'" % name, 773 discriminator) 774 discriminator_type = base_members.get(discriminator) 775 if not discriminator_type: 776 raise QAPISemError(info, 777 "Discriminator '%s' is not a member of base " 778 "struct '%s'" 779 % (discriminator, base)) 780 enum_define = enum_types.get(discriminator_type) 781 allow_metas = ['struct'] 782 # Do not allow string discriminator 783 if not enum_define: 784 raise QAPISemError(info, 785 "Discriminator '%s' must be of enumeration " 786 "type" % discriminator) 787 788 # Check every branch; don't allow an empty union 789 if len(members) == 0: 790 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name) 791 for (key, value) in members.items(): 792 check_name(info, "Member of union '%s'" % name, key) 793 794 # Each value must name a known type 795 check_type(info, "Member '%s' of union '%s'" % (key, name), 796 value, allow_array=not base, allow_metas=allow_metas) 797 798 # If the discriminator names an enum type, then all members 799 # of 'data' must also be members of the enum type. 800 if enum_define: 801 if key not in enum_define['data']: 802 raise QAPISemError(info, 803 "Discriminator value '%s' is not found in " 804 "enum '%s'" 805 % (key, enum_define['enum'])) 806 807 808def check_alternate(expr, info): 809 name = expr['alternate'] 810 members = expr['data'] 811 types_seen = {} 812 813 # Check every branch; require at least two branches 814 if len(members) < 2: 815 raise QAPISemError(info, 816 "Alternate '%s' should have at least two branches " 817 "in 'data'" % name) 818 for (key, value) in members.items(): 819 check_name(info, "Member of alternate '%s'" % name, key) 820 821 # Ensure alternates have no type conflicts. 822 check_type(info, "Member '%s' of alternate '%s'" % (key, name), 823 value, 824 allow_metas=['built-in', 'union', 'struct', 'enum']) 825 qtype = find_alternate_member_qtype(value) 826 if not qtype: 827 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use " 828 "type '%s'" % (name, key, value)) 829 conflicting = set([qtype]) 830 if qtype == 'QTYPE_QSTRING': 831 enum_expr = enum_types.get(value) 832 if enum_expr: 833 for v in enum_expr['data']: 834 if v in ['on', 'off']: 835 conflicting.add('QTYPE_QBOOL') 836 if re.match(r'[-+0-9.]', v): # lazy, could be tightened 837 conflicting.add('QTYPE_QNUM') 838 else: 839 conflicting.add('QTYPE_QNUM') 840 conflicting.add('QTYPE_QBOOL') 841 for qt in conflicting: 842 if qt in types_seen: 843 raise QAPISemError(info, "Alternate '%s' member '%s' can't " 844 "be distinguished from member '%s'" 845 % (name, key, types_seen[qt])) 846 types_seen[qt] = key 847 848 849def check_enum(expr, info): 850 name = expr['enum'] 851 members = expr.get('data') 852 prefix = expr.get('prefix') 853 854 if not isinstance(members, list): 855 raise QAPISemError(info, 856 "Enum '%s' requires an array for 'data'" % name) 857 if prefix is not None and not isinstance(prefix, str): 858 raise QAPISemError(info, 859 "Enum '%s' requires a string for 'prefix'" % name) 860 for member in members: 861 check_name(info, "Member of enum '%s'" % name, member, 862 enum_member=True) 863 864 865def check_struct(expr, info): 866 name = expr['struct'] 867 members = expr['data'] 868 869 check_type(info, "'data' for struct '%s'" % name, members, 870 allow_dict=True, allow_optional=True) 871 check_type(info, "'base' for struct '%s'" % name, expr.get('base'), 872 allow_metas=['struct']) 873 874 875def check_keys(expr_elem, meta, required, optional=[]): 876 expr = expr_elem['expr'] 877 info = expr_elem['info'] 878 name = expr[meta] 879 if not isinstance(name, str): 880 raise QAPISemError(info, "'%s' key must have a string value" % meta) 881 required = required + [meta] 882 for (key, value) in expr.items(): 883 if key not in required and key not in optional: 884 raise QAPISemError(info, "Unknown key '%s' in %s '%s'" 885 % (key, meta, name)) 886 if (key == 'gen' or key == 'success-response') and value is not False: 887 raise QAPISemError(info, 888 "'%s' of %s '%s' should only use false value" 889 % (key, meta, name)) 890 if (key == 'boxed' or key == 'allow-oob' or 891 key == 'allow-preconfig') and value is not True: 892 raise QAPISemError(info, 893 "'%s' of %s '%s' should only use true value" 894 % (key, meta, name)) 895 if key == 'if': 896 check_if(expr, info) 897 for key in required: 898 if key not in expr: 899 raise QAPISemError(info, "Key '%s' is missing from %s '%s'" 900 % (key, meta, name)) 901 902 903def check_exprs(exprs): 904 global all_names 905 906 # Populate name table with names of built-in types 907 for builtin in builtin_types.keys(): 908 all_names[builtin] = 'built-in' 909 910 # Learn the types and check for valid expression keys 911 for expr_elem in exprs: 912 expr = expr_elem['expr'] 913 info = expr_elem['info'] 914 doc = expr_elem.get('doc') 915 916 if 'include' in expr: 917 continue 918 919 if not doc and doc_required: 920 raise QAPISemError(info, 921 "Expression missing documentation comment") 922 923 if 'enum' in expr: 924 meta = 'enum' 925 check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix']) 926 enum_types[expr[meta]] = expr 927 elif 'union' in expr: 928 meta = 'union' 929 check_keys(expr_elem, 'union', ['data'], 930 ['base', 'discriminator', 'if']) 931 union_types[expr[meta]] = expr 932 elif 'alternate' in expr: 933 meta = 'alternate' 934 check_keys(expr_elem, 'alternate', ['data'], ['if']) 935 elif 'struct' in expr: 936 meta = 'struct' 937 check_keys(expr_elem, 'struct', ['data'], ['base', 'if']) 938 struct_types[expr[meta]] = expr 939 elif 'command' in expr: 940 meta = 'command' 941 check_keys(expr_elem, 'command', [], 942 ['data', 'returns', 'gen', 'success-response', 943 'boxed', 'allow-oob', 'allow-preconfig', 'if']) 944 elif 'event' in expr: 945 meta = 'event' 946 check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if']) 947 else: 948 raise QAPISemError(expr_elem['info'], 949 "Expression is missing metatype") 950 name = expr[meta] 951 add_name(name, info, meta) 952 if doc and doc.symbol != name: 953 raise QAPISemError(info, "Definition of '%s' follows documentation" 954 " for '%s'" % (name, doc.symbol)) 955 956 # Try again for hidden UnionKind enum 957 for expr_elem in exprs: 958 expr = expr_elem['expr'] 959 960 if 'include' in expr: 961 continue 962 if 'union' in expr and not discriminator_find_enum_define(expr): 963 name = '%sKind' % expr['union'] 964 elif 'alternate' in expr: 965 name = '%sKind' % expr['alternate'] 966 else: 967 continue 968 enum_types[name] = {'enum': name} 969 add_name(name, info, 'enum', implicit=True) 970 971 # Validate that exprs make sense 972 for expr_elem in exprs: 973 expr = expr_elem['expr'] 974 info = expr_elem['info'] 975 doc = expr_elem.get('doc') 976 977 if 'include' in expr: 978 continue 979 if 'enum' in expr: 980 check_enum(expr, info) 981 elif 'union' in expr: 982 check_union(expr, info) 983 elif 'alternate' in expr: 984 check_alternate(expr, info) 985 elif 'struct' in expr: 986 check_struct(expr, info) 987 elif 'command' in expr: 988 check_command(expr, info) 989 elif 'event' in expr: 990 check_event(expr, info) 991 else: 992 assert False, 'unexpected meta type' 993 994 if doc: 995 doc.check_expr(expr) 996 997 return exprs 998 999 1000# 1001# Schema compiler frontend 1002# 1003 1004def listify_cond(ifcond): 1005 if not ifcond: 1006 return [] 1007 if not isinstance(ifcond, list): 1008 return [ifcond] 1009 return ifcond 1010 1011 1012class QAPISchemaEntity(object): 1013 def __init__(self, name, info, doc, ifcond=None): 1014 assert name is None or isinstance(name, str) 1015 self.name = name 1016 self.module = None 1017 # For explicitly defined entities, info points to the (explicit) 1018 # definition. For builtins (and their arrays), info is None. 1019 # For implicitly defined entities, info points to a place that 1020 # triggered the implicit definition (there may be more than one 1021 # such place). 1022 self.info = info 1023 self.doc = doc 1024 self._ifcond = ifcond # self.ifcond is set only after .check() 1025 1026 def c_name(self): 1027 return c_name(self.name) 1028 1029 def check(self, schema): 1030 if isinstance(self._ifcond, QAPISchemaType): 1031 # inherit the condition from a type 1032 typ = self._ifcond 1033 typ.check(schema) 1034 self.ifcond = typ.ifcond 1035 else: 1036 self.ifcond = listify_cond(self._ifcond) 1037 1038 def is_implicit(self): 1039 return not self.info 1040 1041 def visit(self, visitor): 1042 pass 1043 1044 1045class QAPISchemaVisitor(object): 1046 def visit_begin(self, schema): 1047 pass 1048 1049 def visit_end(self): 1050 pass 1051 1052 def visit_module(self, fname): 1053 pass 1054 1055 def visit_needed(self, entity): 1056 # Default to visiting everything 1057 return True 1058 1059 def visit_include(self, fname, info): 1060 pass 1061 1062 def visit_builtin_type(self, name, info, json_type): 1063 pass 1064 1065 def visit_enum_type(self, name, info, values, prefix): 1066 pass 1067 1068 def visit_array_type(self, name, info, element_type): 1069 pass 1070 1071 def visit_object_type(self, name, info, base, members, variants): 1072 pass 1073 1074 def visit_object_type_flat(self, name, info, members, variants): 1075 pass 1076 1077 def visit_alternate_type(self, name, info, variants): 1078 pass 1079 1080 def visit_command(self, name, info, arg_type, ret_type, gen, 1081 success_response, boxed, allow_oob, allow_preconfig): 1082 pass 1083 1084 def visit_event(self, name, info, arg_type, boxed): 1085 pass 1086 1087 1088class QAPISchemaInclude(QAPISchemaEntity): 1089 1090 def __init__(self, fname, info): 1091 QAPISchemaEntity.__init__(self, None, info, None) 1092 self.fname = fname 1093 1094 def visit(self, visitor): 1095 visitor.visit_include(self.fname, self.info) 1096 1097 1098class QAPISchemaType(QAPISchemaEntity): 1099 # Return the C type for common use. 1100 # For the types we commonly box, this is a pointer type. 1101 def c_type(self): 1102 pass 1103 1104 # Return the C type to be used in a parameter list. 1105 def c_param_type(self): 1106 return self.c_type() 1107 1108 # Return the C type to be used where we suppress boxing. 1109 def c_unboxed_type(self): 1110 return self.c_type() 1111 1112 def json_type(self): 1113 pass 1114 1115 def alternate_qtype(self): 1116 json2qtype = { 1117 'null': 'QTYPE_QNULL', 1118 'string': 'QTYPE_QSTRING', 1119 'number': 'QTYPE_QNUM', 1120 'int': 'QTYPE_QNUM', 1121 'boolean': 'QTYPE_QBOOL', 1122 'object': 'QTYPE_QDICT' 1123 } 1124 return json2qtype.get(self.json_type()) 1125 1126 def doc_type(self): 1127 if self.is_implicit(): 1128 return None 1129 return self.name 1130 1131 1132class QAPISchemaBuiltinType(QAPISchemaType): 1133 def __init__(self, name, json_type, c_type): 1134 QAPISchemaType.__init__(self, name, None, None) 1135 assert not c_type or isinstance(c_type, str) 1136 assert json_type in ('string', 'number', 'int', 'boolean', 'null', 1137 'value') 1138 self._json_type_name = json_type 1139 self._c_type_name = c_type 1140 1141 def c_name(self): 1142 return self.name 1143 1144 def c_type(self): 1145 return self._c_type_name 1146 1147 def c_param_type(self): 1148 if self.name == 'str': 1149 return 'const ' + self._c_type_name 1150 return self._c_type_name 1151 1152 def json_type(self): 1153 return self._json_type_name 1154 1155 def doc_type(self): 1156 return self.json_type() 1157 1158 def visit(self, visitor): 1159 visitor.visit_builtin_type(self.name, self.info, self.json_type()) 1160 1161 1162class QAPISchemaEnumType(QAPISchemaType): 1163 def __init__(self, name, info, doc, ifcond, values, prefix): 1164 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1165 for v in values: 1166 assert isinstance(v, QAPISchemaMember) 1167 v.set_owner(name) 1168 assert prefix is None or isinstance(prefix, str) 1169 self.values = values 1170 self.prefix = prefix 1171 1172 def check(self, schema): 1173 QAPISchemaType.check(self, schema) 1174 seen = {} 1175 for v in self.values: 1176 v.check_clash(self.info, seen) 1177 if self.doc: 1178 self.doc.connect_member(v) 1179 1180 def is_implicit(self): 1181 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() 1182 return self.name.endswith('Kind') or self.name == 'QType' 1183 1184 def c_type(self): 1185 return c_name(self.name) 1186 1187 def member_names(self): 1188 return [v.name for v in self.values] 1189 1190 def json_type(self): 1191 return 'string' 1192 1193 def visit(self, visitor): 1194 visitor.visit_enum_type(self.name, self.info, 1195 self.member_names(), self.prefix) 1196 1197 1198class QAPISchemaArrayType(QAPISchemaType): 1199 def __init__(self, name, info, element_type): 1200 QAPISchemaType.__init__(self, name, info, None, None) 1201 assert isinstance(element_type, str) 1202 self._element_type_name = element_type 1203 self.element_type = None 1204 1205 def check(self, schema): 1206 QAPISchemaType.check(self, schema) 1207 self.element_type = schema.lookup_type(self._element_type_name) 1208 assert self.element_type 1209 self.element_type.check(schema) 1210 self.ifcond = self.element_type.ifcond 1211 1212 def is_implicit(self): 1213 return True 1214 1215 def c_type(self): 1216 return c_name(self.name) + pointer_suffix 1217 1218 def json_type(self): 1219 return 'array' 1220 1221 def doc_type(self): 1222 elt_doc_type = self.element_type.doc_type() 1223 if not elt_doc_type: 1224 return None 1225 return 'array of ' + elt_doc_type 1226 1227 def visit(self, visitor): 1228 visitor.visit_array_type(self.name, self.info, self.element_type) 1229 1230 1231class QAPISchemaObjectType(QAPISchemaType): 1232 def __init__(self, name, info, doc, ifcond, 1233 base, local_members, variants): 1234 # struct has local_members, optional base, and no variants 1235 # flat union has base, variants, and no local_members 1236 # simple union has local_members, variants, and no base 1237 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1238 assert base is None or isinstance(base, str) 1239 for m in local_members: 1240 assert isinstance(m, QAPISchemaObjectTypeMember) 1241 m.set_owner(name) 1242 if variants is not None: 1243 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1244 variants.set_owner(name) 1245 self._base_name = base 1246 self.base = None 1247 self.local_members = local_members 1248 self.variants = variants 1249 self.members = None 1250 1251 def check(self, schema): 1252 QAPISchemaType.check(self, schema) 1253 if self.members is False: # check for cycles 1254 raise QAPISemError(self.info, 1255 "Object %s contains itself" % self.name) 1256 if self.members: 1257 return 1258 self.members = False # mark as being checked 1259 seen = OrderedDict() 1260 if self._base_name: 1261 self.base = schema.lookup_type(self._base_name) 1262 assert isinstance(self.base, QAPISchemaObjectType) 1263 self.base.check(schema) 1264 self.base.check_clash(self.info, seen) 1265 for m in self.local_members: 1266 m.check(schema) 1267 m.check_clash(self.info, seen) 1268 if self.doc: 1269 self.doc.connect_member(m) 1270 self.members = seen.values() 1271 if self.variants: 1272 self.variants.check(schema, seen) 1273 assert self.variants.tag_member in self.members 1274 self.variants.check_clash(self.info, seen) 1275 if self.doc: 1276 self.doc.check() 1277 1278 # Check that the members of this type do not cause duplicate JSON members, 1279 # and update seen to track the members seen so far. Report any errors 1280 # on behalf of info, which is not necessarily self.info 1281 def check_clash(self, info, seen): 1282 assert not self.variants # not implemented 1283 for m in self.members: 1284 m.check_clash(info, seen) 1285 1286 def is_implicit(self): 1287 # See QAPISchema._make_implicit_object_type(), as well as 1288 # _def_predefineds() 1289 return self.name.startswith('q_') 1290 1291 def is_empty(self): 1292 assert self.members is not None 1293 return not self.members and not self.variants 1294 1295 def c_name(self): 1296 assert self.name != 'q_empty' 1297 return QAPISchemaType.c_name(self) 1298 1299 def c_type(self): 1300 assert not self.is_implicit() 1301 return c_name(self.name) + pointer_suffix 1302 1303 def c_unboxed_type(self): 1304 return c_name(self.name) 1305 1306 def json_type(self): 1307 return 'object' 1308 1309 def visit(self, visitor): 1310 visitor.visit_object_type(self.name, self.info, 1311 self.base, self.local_members, self.variants) 1312 visitor.visit_object_type_flat(self.name, self.info, 1313 self.members, self.variants) 1314 1315 1316class QAPISchemaMember(object): 1317 role = 'member' 1318 1319 def __init__(self, name): 1320 assert isinstance(name, str) 1321 self.name = name 1322 self.owner = None 1323 1324 def set_owner(self, name): 1325 assert not self.owner 1326 self.owner = name 1327 1328 def check_clash(self, info, seen): 1329 cname = c_name(self.name) 1330 if cname.lower() != cname and self.owner not in name_case_whitelist: 1331 raise QAPISemError(info, 1332 "%s should not use uppercase" % self.describe()) 1333 if cname in seen: 1334 raise QAPISemError(info, "%s collides with %s" % 1335 (self.describe(), seen[cname].describe())) 1336 seen[cname] = self 1337 1338 def _pretty_owner(self): 1339 owner = self.owner 1340 if owner.startswith('q_obj_'): 1341 # See QAPISchema._make_implicit_object_type() - reverse the 1342 # mapping there to create a nice human-readable description 1343 owner = owner[6:] 1344 if owner.endswith('-arg'): 1345 return '(parameter of %s)' % owner[:-4] 1346 elif owner.endswith('-base'): 1347 return '(base of %s)' % owner[:-5] 1348 else: 1349 assert owner.endswith('-wrapper') 1350 # Unreachable and not implemented 1351 assert False 1352 if owner.endswith('Kind'): 1353 # See QAPISchema._make_implicit_enum_type() 1354 return '(branch of %s)' % owner[:-4] 1355 return '(%s of %s)' % (self.role, owner) 1356 1357 def describe(self): 1358 return "'%s' %s" % (self.name, self._pretty_owner()) 1359 1360 1361class QAPISchemaObjectTypeMember(QAPISchemaMember): 1362 def __init__(self, name, typ, optional): 1363 QAPISchemaMember.__init__(self, name) 1364 assert isinstance(typ, str) 1365 assert isinstance(optional, bool) 1366 self._type_name = typ 1367 self.type = None 1368 self.optional = optional 1369 1370 def check(self, schema): 1371 assert self.owner 1372 self.type = schema.lookup_type(self._type_name) 1373 assert self.type 1374 1375 1376class QAPISchemaObjectTypeVariants(object): 1377 def __init__(self, tag_name, tag_member, variants): 1378 # Flat unions pass tag_name but not tag_member. 1379 # Simple unions and alternates pass tag_member but not tag_name. 1380 # After check(), tag_member is always set, and tag_name remains 1381 # a reliable witness of being used by a flat union. 1382 assert bool(tag_member) != bool(tag_name) 1383 assert (isinstance(tag_name, str) or 1384 isinstance(tag_member, QAPISchemaObjectTypeMember)) 1385 assert len(variants) > 0 1386 for v in variants: 1387 assert isinstance(v, QAPISchemaObjectTypeVariant) 1388 self._tag_name = tag_name 1389 self.tag_member = tag_member 1390 self.variants = variants 1391 1392 def set_owner(self, name): 1393 for v in self.variants: 1394 v.set_owner(name) 1395 1396 def check(self, schema, seen): 1397 if not self.tag_member: # flat union 1398 self.tag_member = seen[c_name(self._tag_name)] 1399 assert self._tag_name == self.tag_member.name 1400 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 1401 if self._tag_name: # flat union 1402 # branches that are not explicitly covered get an empty type 1403 cases = set([v.name for v in self.variants]) 1404 for val in self.tag_member.type.values: 1405 if val.name not in cases: 1406 v = QAPISchemaObjectTypeVariant(val.name, 'q_empty') 1407 v.set_owner(self.tag_member.owner) 1408 self.variants.append(v) 1409 for v in self.variants: 1410 v.check(schema) 1411 # Union names must match enum values; alternate names are 1412 # checked separately. Use 'seen' to tell the two apart. 1413 if seen: 1414 assert v.name in self.tag_member.type.member_names() 1415 assert isinstance(v.type, QAPISchemaObjectType) 1416 v.type.check(schema) 1417 1418 def check_clash(self, info, seen): 1419 for v in self.variants: 1420 # Reset seen map for each variant, since qapi names from one 1421 # branch do not affect another branch 1422 assert isinstance(v.type, QAPISchemaObjectType) 1423 v.type.check_clash(info, dict(seen)) 1424 1425 1426class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): 1427 role = 'branch' 1428 1429 def __init__(self, name, typ): 1430 QAPISchemaObjectTypeMember.__init__(self, name, typ, False) 1431 1432 1433class QAPISchemaAlternateType(QAPISchemaType): 1434 def __init__(self, name, info, doc, ifcond, variants): 1435 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1436 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1437 assert variants.tag_member 1438 variants.set_owner(name) 1439 variants.tag_member.set_owner(self.name) 1440 self.variants = variants 1441 1442 def check(self, schema): 1443 QAPISchemaType.check(self, schema) 1444 self.variants.tag_member.check(schema) 1445 # Not calling self.variants.check_clash(), because there's nothing 1446 # to clash with 1447 self.variants.check(schema, {}) 1448 # Alternate branch names have no relation to the tag enum values; 1449 # so we have to check for potential name collisions ourselves. 1450 seen = {} 1451 for v in self.variants.variants: 1452 v.check_clash(self.info, seen) 1453 if self.doc: 1454 self.doc.connect_member(v) 1455 if self.doc: 1456 self.doc.check() 1457 1458 def c_type(self): 1459 return c_name(self.name) + pointer_suffix 1460 1461 def json_type(self): 1462 return 'value' 1463 1464 def visit(self, visitor): 1465 visitor.visit_alternate_type(self.name, self.info, self.variants) 1466 1467 def is_empty(self): 1468 return False 1469 1470 1471class QAPISchemaCommand(QAPISchemaEntity): 1472 def __init__(self, name, info, doc, ifcond, arg_type, ret_type, 1473 gen, success_response, boxed, allow_oob, allow_preconfig): 1474 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1475 assert not arg_type or isinstance(arg_type, str) 1476 assert not ret_type or isinstance(ret_type, str) 1477 self._arg_type_name = arg_type 1478 self.arg_type = None 1479 self._ret_type_name = ret_type 1480 self.ret_type = None 1481 self.gen = gen 1482 self.success_response = success_response 1483 self.boxed = boxed 1484 self.allow_oob = allow_oob 1485 self.allow_preconfig = allow_preconfig 1486 1487 def check(self, schema): 1488 QAPISchemaEntity.check(self, schema) 1489 if self._arg_type_name: 1490 self.arg_type = schema.lookup_type(self._arg_type_name) 1491 assert (isinstance(self.arg_type, QAPISchemaObjectType) or 1492 isinstance(self.arg_type, QAPISchemaAlternateType)) 1493 self.arg_type.check(schema) 1494 if self.boxed: 1495 if self.arg_type.is_empty(): 1496 raise QAPISemError(self.info, 1497 "Cannot use 'boxed' with empty type") 1498 else: 1499 assert not isinstance(self.arg_type, QAPISchemaAlternateType) 1500 assert not self.arg_type.variants 1501 elif self.boxed: 1502 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1503 if self._ret_type_name: 1504 self.ret_type = schema.lookup_type(self._ret_type_name) 1505 assert isinstance(self.ret_type, QAPISchemaType) 1506 1507 def visit(self, visitor): 1508 visitor.visit_command(self.name, self.info, 1509 self.arg_type, self.ret_type, 1510 self.gen, self.success_response, 1511 self.boxed, self.allow_oob, 1512 self.allow_preconfig) 1513 1514 1515class QAPISchemaEvent(QAPISchemaEntity): 1516 def __init__(self, name, info, doc, ifcond, arg_type, boxed): 1517 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1518 assert not arg_type or isinstance(arg_type, str) 1519 self._arg_type_name = arg_type 1520 self.arg_type = None 1521 self.boxed = boxed 1522 1523 def check(self, schema): 1524 QAPISchemaEntity.check(self, schema) 1525 if self._arg_type_name: 1526 self.arg_type = schema.lookup_type(self._arg_type_name) 1527 assert (isinstance(self.arg_type, QAPISchemaObjectType) or 1528 isinstance(self.arg_type, QAPISchemaAlternateType)) 1529 self.arg_type.check(schema) 1530 if self.boxed: 1531 if self.arg_type.is_empty(): 1532 raise QAPISemError(self.info, 1533 "Cannot use 'boxed' with empty type") 1534 else: 1535 assert not isinstance(self.arg_type, QAPISchemaAlternateType) 1536 assert not self.arg_type.variants 1537 elif self.boxed: 1538 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1539 1540 def visit(self, visitor): 1541 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed) 1542 1543 1544class QAPISchema(object): 1545 def __init__(self, fname): 1546 self._fname = fname 1547 if sys.version_info[0] >= 3: 1548 f = open(fname, 'r', encoding='utf-8') 1549 else: 1550 f = open(fname, 'r') 1551 parser = QAPISchemaParser(f) 1552 exprs = check_exprs(parser.exprs) 1553 self.docs = parser.docs 1554 self._entity_list = [] 1555 self._entity_dict = {} 1556 self._predefining = True 1557 self._def_predefineds() 1558 self._predefining = False 1559 self._def_exprs(exprs) 1560 self.check() 1561 1562 def _def_entity(self, ent): 1563 # Only the predefined types are allowed to not have info 1564 assert ent.info or self._predefining 1565 assert ent.name is None or ent.name not in self._entity_dict 1566 self._entity_list.append(ent) 1567 if ent.name is not None: 1568 self._entity_dict[ent.name] = ent 1569 if ent.info: 1570 ent.module = os.path.relpath(ent.info['file'], 1571 os.path.dirname(self._fname)) 1572 1573 def lookup_entity(self, name, typ=None): 1574 ent = self._entity_dict.get(name) 1575 if typ and not isinstance(ent, typ): 1576 return None 1577 return ent 1578 1579 def lookup_type(self, name): 1580 return self.lookup_entity(name, QAPISchemaType) 1581 1582 def _def_include(self, expr, info, doc): 1583 include = expr['include'] 1584 assert doc is None 1585 main_info = info 1586 while main_info['parent']: 1587 main_info = main_info['parent'] 1588 fname = os.path.relpath(include, os.path.dirname(main_info['file'])) 1589 self._def_entity(QAPISchemaInclude(fname, info)) 1590 1591 def _def_builtin_type(self, name, json_type, c_type): 1592 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) 1593 # Instantiating only the arrays that are actually used would 1594 # be nice, but we can't as long as their generated code 1595 # (qapi-builtin-types.[ch]) may be shared by some other 1596 # schema. 1597 self._make_array_type(name, None) 1598 1599 def _def_predefineds(self): 1600 for t in [('str', 'string', 'char' + pointer_suffix), 1601 ('number', 'number', 'double'), 1602 ('int', 'int', 'int64_t'), 1603 ('int8', 'int', 'int8_t'), 1604 ('int16', 'int', 'int16_t'), 1605 ('int32', 'int', 'int32_t'), 1606 ('int64', 'int', 'int64_t'), 1607 ('uint8', 'int', 'uint8_t'), 1608 ('uint16', 'int', 'uint16_t'), 1609 ('uint32', 'int', 'uint32_t'), 1610 ('uint64', 'int', 'uint64_t'), 1611 ('size', 'int', 'uint64_t'), 1612 ('bool', 'boolean', 'bool'), 1613 ('any', 'value', 'QObject' + pointer_suffix), 1614 ('null', 'null', 'QNull' + pointer_suffix)]: 1615 self._def_builtin_type(*t) 1616 self.the_empty_object_type = QAPISchemaObjectType( 1617 'q_empty', None, None, None, None, [], None) 1618 self._def_entity(self.the_empty_object_type) 1619 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', 1620 'qstring', 'qdict', 'qlist', 1621 'qbool']) 1622 self._def_entity(QAPISchemaEnumType('QType', None, None, None, 1623 qtype_values, 'QTYPE')) 1624 1625 def _make_enum_members(self, values): 1626 return [QAPISchemaMember(v) for v in values] 1627 1628 def _make_implicit_enum_type(self, name, info, ifcond, values): 1629 # See also QAPISchemaObjectTypeMember._pretty_owner() 1630 name = name + 'Kind' # Use namespace reserved by add_name() 1631 self._def_entity(QAPISchemaEnumType( 1632 name, info, None, ifcond, self._make_enum_members(values), None)) 1633 return name 1634 1635 def _make_array_type(self, element_type, info): 1636 name = element_type + 'List' # Use namespace reserved by add_name() 1637 if not self.lookup_type(name): 1638 self._def_entity(QAPISchemaArrayType(name, info, element_type)) 1639 return name 1640 1641 def _make_implicit_object_type(self, name, info, doc, ifcond, 1642 role, members): 1643 if not members: 1644 return None 1645 # See also QAPISchemaObjectTypeMember._pretty_owner() 1646 name = 'q_obj_%s-%s' % (name, role) 1647 typ = self.lookup_entity(name, QAPISchemaObjectType) 1648 if typ: 1649 # The implicit object type has multiple users. This can 1650 # happen only for simple unions' implicit wrapper types. 1651 # Its ifcond should be the disjunction of its user's 1652 # ifconds. Not implemented. Instead, we always pass the 1653 # wrapped type's ifcond, which is trivially the same for all 1654 # users. It's also necessary for the wrapper to compile. 1655 # But it's not tight: the disjunction need not imply it. We 1656 # may end up compiling useless wrapper types. 1657 # TODO kill simple unions or implement the disjunction 1658 assert ifcond == typ._ifcond # pylint: disable=protected-access 1659 else: 1660 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, 1661 None, members, None)) 1662 return name 1663 1664 def _def_enum_type(self, expr, info, doc): 1665 name = expr['enum'] 1666 data = expr['data'] 1667 prefix = expr.get('prefix') 1668 ifcond = expr.get('if') 1669 self._def_entity(QAPISchemaEnumType( 1670 name, info, doc, ifcond, 1671 self._make_enum_members(data), prefix)) 1672 1673 def _make_member(self, name, typ, info): 1674 optional = False 1675 if name.startswith('*'): 1676 name = name[1:] 1677 optional = True 1678 if isinstance(typ, list): 1679 assert len(typ) == 1 1680 typ = self._make_array_type(typ[0], info) 1681 return QAPISchemaObjectTypeMember(name, typ, optional) 1682 1683 def _make_members(self, data, info): 1684 return [self._make_member(key, value, info) 1685 for (key, value) in data.items()] 1686 1687 def _def_struct_type(self, expr, info, doc): 1688 name = expr['struct'] 1689 base = expr.get('base') 1690 data = expr['data'] 1691 ifcond = expr.get('if') 1692 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base, 1693 self._make_members(data, info), 1694 None)) 1695 1696 def _make_variant(self, case, typ): 1697 return QAPISchemaObjectTypeVariant(case, typ) 1698 1699 def _make_simple_variant(self, case, typ, info): 1700 if isinstance(typ, list): 1701 assert len(typ) == 1 1702 typ = self._make_array_type(typ[0], info) 1703 typ = self._make_implicit_object_type( 1704 typ, info, None, self.lookup_type(typ), 1705 'wrapper', [self._make_member('data', typ, info)]) 1706 return QAPISchemaObjectTypeVariant(case, typ) 1707 1708 def _def_union_type(self, expr, info, doc): 1709 name = expr['union'] 1710 data = expr['data'] 1711 base = expr.get('base') 1712 ifcond = expr.get('if') 1713 tag_name = expr.get('discriminator') 1714 tag_member = None 1715 if isinstance(base, dict): 1716 base = self._make_implicit_object_type( 1717 name, info, doc, ifcond, 1718 'base', self._make_members(base, info)) 1719 if tag_name: 1720 variants = [self._make_variant(key, value) 1721 for (key, value) in data.items()] 1722 members = [] 1723 else: 1724 variants = [self._make_simple_variant(key, value, info) 1725 for (key, value) in data.items()] 1726 typ = self._make_implicit_enum_type(name, info, ifcond, 1727 [v.name for v in variants]) 1728 tag_member = QAPISchemaObjectTypeMember('type', typ, False) 1729 members = [tag_member] 1730 self._def_entity( 1731 QAPISchemaObjectType(name, info, doc, ifcond, base, members, 1732 QAPISchemaObjectTypeVariants(tag_name, 1733 tag_member, 1734 variants))) 1735 1736 def _def_alternate_type(self, expr, info, doc): 1737 name = expr['alternate'] 1738 data = expr['data'] 1739 ifcond = expr.get('if') 1740 variants = [self._make_variant(key, value) 1741 for (key, value) in data.items()] 1742 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) 1743 self._def_entity( 1744 QAPISchemaAlternateType(name, info, doc, ifcond, 1745 QAPISchemaObjectTypeVariants(None, 1746 tag_member, 1747 variants))) 1748 1749 def _def_command(self, expr, info, doc): 1750 name = expr['command'] 1751 data = expr.get('data') 1752 rets = expr.get('returns') 1753 gen = expr.get('gen', True) 1754 success_response = expr.get('success-response', True) 1755 boxed = expr.get('boxed', False) 1756 allow_oob = expr.get('allow-oob', False) 1757 allow_preconfig = expr.get('allow-preconfig', False) 1758 ifcond = expr.get('if') 1759 if isinstance(data, OrderedDict): 1760 data = self._make_implicit_object_type( 1761 name, info, doc, ifcond, 'arg', self._make_members(data, info)) 1762 if isinstance(rets, list): 1763 assert len(rets) == 1 1764 rets = self._make_array_type(rets[0], info) 1765 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, 1766 gen, success_response, 1767 boxed, allow_oob, allow_preconfig)) 1768 1769 def _def_event(self, expr, info, doc): 1770 name = expr['event'] 1771 data = expr.get('data') 1772 boxed = expr.get('boxed', False) 1773 ifcond = expr.get('if') 1774 if isinstance(data, OrderedDict): 1775 data = self._make_implicit_object_type( 1776 name, info, doc, ifcond, 'arg', self._make_members(data, info)) 1777 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) 1778 1779 def _def_exprs(self, exprs): 1780 for expr_elem in exprs: 1781 expr = expr_elem['expr'] 1782 info = expr_elem['info'] 1783 doc = expr_elem.get('doc') 1784 if 'enum' in expr: 1785 self._def_enum_type(expr, info, doc) 1786 elif 'struct' in expr: 1787 self._def_struct_type(expr, info, doc) 1788 elif 'union' in expr: 1789 self._def_union_type(expr, info, doc) 1790 elif 'alternate' in expr: 1791 self._def_alternate_type(expr, info, doc) 1792 elif 'command' in expr: 1793 self._def_command(expr, info, doc) 1794 elif 'event' in expr: 1795 self._def_event(expr, info, doc) 1796 elif 'include' in expr: 1797 self._def_include(expr, info, doc) 1798 else: 1799 assert False 1800 1801 def check(self): 1802 for ent in self._entity_list: 1803 ent.check(self) 1804 1805 def visit(self, visitor): 1806 visitor.visit_begin(self) 1807 module = None 1808 for entity in self._entity_list: 1809 if visitor.visit_needed(entity): 1810 if entity.module != module: 1811 module = entity.module 1812 visitor.visit_module(module) 1813 entity.visit(visitor) 1814 visitor.visit_end() 1815 1816 1817# 1818# Code generation helpers 1819# 1820 1821def camel_case(name): 1822 new_name = '' 1823 first = True 1824 for ch in name: 1825 if ch in ['_', '-']: 1826 first = True 1827 elif first: 1828 new_name += ch.upper() 1829 first = False 1830 else: 1831 new_name += ch.lower() 1832 return new_name 1833 1834 1835# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 1836# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 1837# ENUM24_Name -> ENUM24_NAME 1838def camel_to_upper(value): 1839 c_fun_str = c_name(value, False) 1840 if value.isupper(): 1841 return c_fun_str 1842 1843 new_name = '' 1844 l = len(c_fun_str) 1845 for i in range(l): 1846 c = c_fun_str[i] 1847 # When c is upper and no '_' appears before, do more checks 1848 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_': 1849 if i < l - 1 and c_fun_str[i + 1].islower(): 1850 new_name += '_' 1851 elif c_fun_str[i - 1].isdigit(): 1852 new_name += '_' 1853 new_name += c 1854 return new_name.lstrip('_').upper() 1855 1856 1857def c_enum_const(type_name, const_name, prefix=None): 1858 if prefix is not None: 1859 type_name = prefix 1860 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() 1861 1862if hasattr(str, 'maketrans'): 1863 c_name_trans = str.maketrans('.-', '__') 1864else: 1865 c_name_trans = string.maketrans('.-', '__') 1866 1867 1868# Map @name to a valid C identifier. 1869# If @protect, avoid returning certain ticklish identifiers (like 1870# C keywords) by prepending 'q_'. 1871# 1872# Used for converting 'name' from a 'name':'type' qapi definition 1873# into a generated struct member, as well as converting type names 1874# into substrings of a generated C function name. 1875# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo' 1876# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int' 1877def c_name(name, protect=True): 1878 # ANSI X3J11/88-090, 3.1.1 1879 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', 1880 'default', 'do', 'double', 'else', 'enum', 'extern', 1881 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 1882 'return', 'short', 'signed', 'sizeof', 'static', 1883 'struct', 'switch', 'typedef', 'union', 'unsigned', 1884 'void', 'volatile', 'while']) 1885 # ISO/IEC 9899:1999, 6.4.1 1886 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) 1887 # ISO/IEC 9899:2011, 6.4.1 1888 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', 1889 '_Noreturn', '_Static_assert', '_Thread_local']) 1890 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html 1891 # excluding _.* 1892 gcc_words = set(['asm', 'typeof']) 1893 # C++ ISO/IEC 14882:2003 2.11 1894 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', 1895 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', 1896 'namespace', 'new', 'operator', 'private', 'protected', 1897 'public', 'reinterpret_cast', 'static_cast', 'template', 1898 'this', 'throw', 'true', 'try', 'typeid', 'typename', 1899 'using', 'virtual', 'wchar_t', 1900 # alternative representations 1901 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 1902 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) 1903 # namespace pollution: 1904 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) 1905 name = name.translate(c_name_trans) 1906 if protect and (name in c89_words | c99_words | c11_words | gcc_words 1907 | cpp_words | polluted_words): 1908 return 'q_' + name 1909 return name 1910 1911eatspace = '\033EATSPACE.' 1912pointer_suffix = ' *' + eatspace 1913 1914 1915def genindent(count): 1916 ret = '' 1917 for _ in range(count): 1918 ret += ' ' 1919 return ret 1920 1921indent_level = 0 1922 1923 1924def push_indent(indent_amount=4): 1925 global indent_level 1926 indent_level += indent_amount 1927 1928 1929def pop_indent(indent_amount=4): 1930 global indent_level 1931 indent_level -= indent_amount 1932 1933 1934# Generate @code with @kwds interpolated. 1935# Obey indent_level, and strip eatspace. 1936def cgen(code, **kwds): 1937 raw = code % kwds 1938 if indent_level: 1939 indent = genindent(indent_level) 1940 # re.subn() lacks flags support before Python 2.7, use re.compile() 1941 raw = re.subn(re.compile(r'^.', re.MULTILINE), 1942 indent + r'\g<0>', raw) 1943 raw = raw[0] 1944 return re.sub(re.escape(eatspace) + r' *', '', raw) 1945 1946 1947def mcgen(code, **kwds): 1948 if code[0] == '\n': 1949 code = code[1:] 1950 return cgen(code, **kwds) 1951 1952 1953def guardname(filename): 1954 return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper() 1955 1956 1957def guardstart(name): 1958 return mcgen(''' 1959#ifndef %(name)s 1960#define %(name)s 1961 1962''', 1963 name=guardname(name)) 1964 1965 1966def guardend(name): 1967 return mcgen(''' 1968 1969#endif /* %(name)s */ 1970''', 1971 name=guardname(name)) 1972 1973 1974def gen_enum_lookup(name, values, prefix=None): 1975 ret = mcgen(''' 1976 1977const QEnumLookup %(c_name)s_lookup = { 1978 .array = (const char *const[]) { 1979''', 1980 c_name=c_name(name)) 1981 for value in values: 1982 index = c_enum_const(name, value, prefix) 1983 ret += mcgen(''' 1984 [%(index)s] = "%(value)s", 1985''', 1986 index=index, value=value) 1987 1988 ret += mcgen(''' 1989 }, 1990 .size = %(max_index)s 1991}; 1992''', 1993 max_index=c_enum_const(name, '_MAX', prefix)) 1994 return ret 1995 1996 1997def gen_enum(name, values, prefix=None): 1998 # append automatically generated _MAX value 1999 enum_values = values + ['_MAX'] 2000 2001 ret = mcgen(''' 2002 2003typedef enum %(c_name)s { 2004''', 2005 c_name=c_name(name)) 2006 2007 i = 0 2008 for value in enum_values: 2009 ret += mcgen(''' 2010 %(c_enum)s = %(i)d, 2011''', 2012 c_enum=c_enum_const(name, value, prefix), 2013 i=i) 2014 i += 1 2015 2016 ret += mcgen(''' 2017} %(c_name)s; 2018''', 2019 c_name=c_name(name)) 2020 2021 ret += mcgen(''' 2022 2023#define %(c_name)s_str(val) \\ 2024 qapi_enum_lookup(&%(c_name)s_lookup, (val)) 2025 2026extern const QEnumLookup %(c_name)s_lookup; 2027''', 2028 c_name=c_name(name)) 2029 return ret 2030 2031 2032def build_params(arg_type, boxed, extra): 2033 if not arg_type: 2034 assert not boxed 2035 return extra 2036 ret = '' 2037 sep = '' 2038 if boxed: 2039 ret += '%s arg' % arg_type.c_param_type() 2040 sep = ', ' 2041 else: 2042 assert not arg_type.variants 2043 for memb in arg_type.members: 2044 ret += sep 2045 sep = ', ' 2046 if memb.optional: 2047 ret += 'bool has_%s, ' % c_name(memb.name) 2048 ret += '%s %s' % (memb.type.c_param_type(), 2049 c_name(memb.name)) 2050 if extra: 2051 ret += sep + extra 2052 return ret 2053 2054 2055# 2056# Accumulate and write output 2057# 2058 2059class QAPIGen(object): 2060 2061 def __init__(self): 2062 self._preamble = '' 2063 self._body = '' 2064 2065 def preamble_add(self, text): 2066 self._preamble += text 2067 2068 def add(self, text): 2069 self._body += text 2070 2071 def _top(self, fname): 2072 return '' 2073 2074 def _bottom(self, fname): 2075 return '' 2076 2077 def write(self, output_dir, fname): 2078 pathname = os.path.join(output_dir, fname) 2079 dir = os.path.dirname(pathname) 2080 if dir: 2081 try: 2082 os.makedirs(dir) 2083 except os.error as e: 2084 if e.errno != errno.EEXIST: 2085 raise 2086 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) 2087 if sys.version_info[0] >= 3: 2088 f = open(fd, 'r+', encoding='utf-8') 2089 else: 2090 f = os.fdopen(fd, 'r+') 2091 text = (self._top(fname) + self._preamble + self._body 2092 + self._bottom(fname)) 2093 oldtext = f.read(len(text) + 1) 2094 if text != oldtext: 2095 f.seek(0) 2096 f.truncate(0) 2097 f.write(text) 2098 f.close() 2099 2100 2101class QAPIGenC(QAPIGen): 2102 2103 def __init__(self, blurb, pydoc): 2104 QAPIGen.__init__(self) 2105 self._blurb = blurb 2106 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, 2107 re.MULTILINE)) 2108 2109 def _top(self, fname): 2110 return mcgen(''' 2111/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ 2112 2113/* 2114%(blurb)s 2115 * 2116 * %(copyright)s 2117 * 2118 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 2119 * See the COPYING.LIB file in the top-level directory. 2120 */ 2121 2122''', 2123 blurb=self._blurb, copyright=self._copyright) 2124 2125 def _bottom(self, fname): 2126 return mcgen(''' 2127/* Dummy declaration to prevent empty .o file */ 2128char dummy_%(name)s; 2129''', 2130 name=c_name(fname)) 2131 2132 2133class QAPIGenH(QAPIGenC): 2134 2135 def _top(self, fname): 2136 return QAPIGenC._top(self, fname) + guardstart(fname) 2137 2138 def _bottom(self, fname): 2139 return guardend(fname) 2140 2141 2142class QAPIGenDoc(QAPIGen): 2143 2144 def _top(self, fname): 2145 return (QAPIGen._top(self, fname) 2146 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') 2147 2148 2149class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): 2150 2151 def __init__(self, prefix, what, blurb, pydoc): 2152 self._prefix = prefix 2153 self._what = what 2154 self._genc = QAPIGenC(blurb, pydoc) 2155 self._genh = QAPIGenH(blurb, pydoc) 2156 2157 def write(self, output_dir): 2158 self._genc.write(output_dir, self._prefix + self._what + '.c') 2159 self._genh.write(output_dir, self._prefix + self._what + '.h') 2160 2161 2162class QAPISchemaModularCVisitor(QAPISchemaVisitor): 2163 2164 def __init__(self, prefix, what, blurb, pydoc): 2165 self._prefix = prefix 2166 self._what = what 2167 self._blurb = blurb 2168 self._pydoc = pydoc 2169 self._module = {} 2170 self._main_module = None 2171 2172 def _module_basename(self, what, name): 2173 if name is None: 2174 return re.sub(r'-', '-builtin-', what) 2175 basename = os.path.join(os.path.dirname(name), 2176 self._prefix + what) 2177 if name == self._main_module: 2178 return basename 2179 return basename + '-' + os.path.splitext(os.path.basename(name))[0] 2180 2181 def _add_module(self, name, blurb): 2182 if self._main_module is None and name is not None: 2183 self._main_module = name 2184 genc = QAPIGenC(blurb, self._pydoc) 2185 genh = QAPIGenH(blurb, self._pydoc) 2186 self._module[name] = (genc, genh) 2187 self._set_module(name) 2188 2189 def _set_module(self, name): 2190 self._genc, self._genh = self._module[name] 2191 2192 def write(self, output_dir, opt_builtins=False): 2193 for name in self._module: 2194 if name is None and not opt_builtins: 2195 continue 2196 basename = self._module_basename(self._what, name) 2197 (genc, genh) = self._module[name] 2198 genc.write(output_dir, basename + '.c') 2199 genh.write(output_dir, basename + '.h') 2200 2201 def _begin_module(self, name): 2202 pass 2203 2204 def visit_module(self, name): 2205 if name in self._module: 2206 self._set_module(name) 2207 return 2208 self._add_module(name, self._blurb) 2209 self._begin_module(name) 2210 2211 def visit_include(self, name, info): 2212 basename = self._module_basename(self._what, name) 2213 self._genh.preamble_add(mcgen(''' 2214#include "%(basename)s.h" 2215''', 2216 basename=basename)) 2217