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 = listify_cond(ifcond) 1025 1026 def c_name(self): 1027 return c_name(self.name) 1028 1029 def check(self, schema): 1030 pass 1031 1032 def is_implicit(self): 1033 return not self.info 1034 1035 def visit(self, visitor): 1036 pass 1037 1038 1039class QAPISchemaVisitor(object): 1040 def visit_begin(self, schema): 1041 pass 1042 1043 def visit_end(self): 1044 pass 1045 1046 def visit_module(self, fname): 1047 pass 1048 1049 def visit_needed(self, entity): 1050 # Default to visiting everything 1051 return True 1052 1053 def visit_include(self, fname, info): 1054 pass 1055 1056 def visit_builtin_type(self, name, info, json_type): 1057 pass 1058 1059 def visit_enum_type(self, name, info, values, prefix): 1060 pass 1061 1062 def visit_array_type(self, name, info, element_type): 1063 pass 1064 1065 def visit_object_type(self, name, info, base, members, variants): 1066 pass 1067 1068 def visit_object_type_flat(self, name, info, members, variants): 1069 pass 1070 1071 def visit_alternate_type(self, name, info, variants): 1072 pass 1073 1074 def visit_command(self, name, info, arg_type, ret_type, gen, 1075 success_response, boxed, allow_oob, allow_preconfig): 1076 pass 1077 1078 def visit_event(self, name, info, arg_type, boxed): 1079 pass 1080 1081 1082class QAPISchemaInclude(QAPISchemaEntity): 1083 1084 def __init__(self, fname, info): 1085 QAPISchemaEntity.__init__(self, None, info, None) 1086 self.fname = fname 1087 1088 def visit(self, visitor): 1089 visitor.visit_include(self.fname, self.info) 1090 1091 1092class QAPISchemaType(QAPISchemaEntity): 1093 # Return the C type for common use. 1094 # For the types we commonly box, this is a pointer type. 1095 def c_type(self): 1096 pass 1097 1098 # Return the C type to be used in a parameter list. 1099 def c_param_type(self): 1100 return self.c_type() 1101 1102 # Return the C type to be used where we suppress boxing. 1103 def c_unboxed_type(self): 1104 return self.c_type() 1105 1106 def json_type(self): 1107 pass 1108 1109 def alternate_qtype(self): 1110 json2qtype = { 1111 'null': 'QTYPE_QNULL', 1112 'string': 'QTYPE_QSTRING', 1113 'number': 'QTYPE_QNUM', 1114 'int': 'QTYPE_QNUM', 1115 'boolean': 'QTYPE_QBOOL', 1116 'object': 'QTYPE_QDICT' 1117 } 1118 return json2qtype.get(self.json_type()) 1119 1120 def doc_type(self): 1121 if self.is_implicit(): 1122 return None 1123 return self.name 1124 1125 1126class QAPISchemaBuiltinType(QAPISchemaType): 1127 def __init__(self, name, json_type, c_type): 1128 QAPISchemaType.__init__(self, name, None, None) 1129 assert not c_type or isinstance(c_type, str) 1130 assert json_type in ('string', 'number', 'int', 'boolean', 'null', 1131 'value') 1132 self._json_type_name = json_type 1133 self._c_type_name = c_type 1134 1135 def c_name(self): 1136 return self.name 1137 1138 def c_type(self): 1139 return self._c_type_name 1140 1141 def c_param_type(self): 1142 if self.name == 'str': 1143 return 'const ' + self._c_type_name 1144 return self._c_type_name 1145 1146 def json_type(self): 1147 return self._json_type_name 1148 1149 def doc_type(self): 1150 return self.json_type() 1151 1152 def visit(self, visitor): 1153 visitor.visit_builtin_type(self.name, self.info, self.json_type()) 1154 1155 1156class QAPISchemaEnumType(QAPISchemaType): 1157 def __init__(self, name, info, doc, ifcond, values, prefix): 1158 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1159 for v in values: 1160 assert isinstance(v, QAPISchemaMember) 1161 v.set_owner(name) 1162 assert prefix is None or isinstance(prefix, str) 1163 self.values = values 1164 self.prefix = prefix 1165 1166 def check(self, schema): 1167 seen = {} 1168 for v in self.values: 1169 v.check_clash(self.info, seen) 1170 if self.doc: 1171 self.doc.connect_member(v) 1172 1173 def is_implicit(self): 1174 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() 1175 return self.name.endswith('Kind') or self.name == 'QType' 1176 1177 def c_type(self): 1178 return c_name(self.name) 1179 1180 def member_names(self): 1181 return [v.name for v in self.values] 1182 1183 def json_type(self): 1184 return 'string' 1185 1186 def visit(self, visitor): 1187 visitor.visit_enum_type(self.name, self.info, 1188 self.member_names(), self.prefix) 1189 1190 1191class QAPISchemaArrayType(QAPISchemaType): 1192 def __init__(self, name, info, element_type): 1193 QAPISchemaType.__init__(self, name, info, None, None) 1194 assert isinstance(element_type, str) 1195 self._element_type_name = element_type 1196 self.element_type = None 1197 1198 def check(self, schema): 1199 self.element_type = schema.lookup_type(self._element_type_name) 1200 assert self.element_type 1201 self.ifcond = self.element_type.ifcond 1202 1203 def is_implicit(self): 1204 return True 1205 1206 def c_type(self): 1207 return c_name(self.name) + pointer_suffix 1208 1209 def json_type(self): 1210 return 'array' 1211 1212 def doc_type(self): 1213 elt_doc_type = self.element_type.doc_type() 1214 if not elt_doc_type: 1215 return None 1216 return 'array of ' + elt_doc_type 1217 1218 def visit(self, visitor): 1219 visitor.visit_array_type(self.name, self.info, self.element_type) 1220 1221 1222class QAPISchemaObjectType(QAPISchemaType): 1223 def __init__(self, name, info, doc, ifcond, 1224 base, local_members, variants): 1225 # struct has local_members, optional base, and no variants 1226 # flat union has base, variants, and no local_members 1227 # simple union has local_members, variants, and no base 1228 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1229 assert base is None or isinstance(base, str) 1230 for m in local_members: 1231 assert isinstance(m, QAPISchemaObjectTypeMember) 1232 m.set_owner(name) 1233 if variants is not None: 1234 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1235 variants.set_owner(name) 1236 self._base_name = base 1237 self.base = None 1238 self.local_members = local_members 1239 self.variants = variants 1240 self.members = None 1241 1242 def check(self, schema): 1243 if self.members is False: # check for cycles 1244 raise QAPISemError(self.info, 1245 "Object %s contains itself" % self.name) 1246 if self.members: 1247 return 1248 self.members = False # mark as being checked 1249 seen = OrderedDict() 1250 if self._base_name: 1251 self.base = schema.lookup_type(self._base_name) 1252 assert isinstance(self.base, QAPISchemaObjectType) 1253 self.base.check(schema) 1254 self.base.check_clash(self.info, seen) 1255 for m in self.local_members: 1256 m.check(schema) 1257 m.check_clash(self.info, seen) 1258 if self.doc: 1259 self.doc.connect_member(m) 1260 self.members = seen.values() 1261 if self.variants: 1262 self.variants.check(schema, seen) 1263 assert self.variants.tag_member in self.members 1264 self.variants.check_clash(self.info, seen) 1265 if self.doc: 1266 self.doc.check() 1267 1268 # Check that the members of this type do not cause duplicate JSON members, 1269 # and update seen to track the members seen so far. Report any errors 1270 # on behalf of info, which is not necessarily self.info 1271 def check_clash(self, info, seen): 1272 assert not self.variants # not implemented 1273 for m in self.members: 1274 m.check_clash(info, seen) 1275 1276 def is_implicit(self): 1277 # See QAPISchema._make_implicit_object_type(), as well as 1278 # _def_predefineds() 1279 return self.name.startswith('q_') 1280 1281 def is_empty(self): 1282 assert self.members is not None 1283 return not self.members and not self.variants 1284 1285 def c_name(self): 1286 assert self.name != 'q_empty' 1287 return QAPISchemaType.c_name(self) 1288 1289 def c_type(self): 1290 assert not self.is_implicit() 1291 return c_name(self.name) + pointer_suffix 1292 1293 def c_unboxed_type(self): 1294 return c_name(self.name) 1295 1296 def json_type(self): 1297 return 'object' 1298 1299 def visit(self, visitor): 1300 visitor.visit_object_type(self.name, self.info, 1301 self.base, self.local_members, self.variants) 1302 visitor.visit_object_type_flat(self.name, self.info, 1303 self.members, self.variants) 1304 1305 1306class QAPISchemaMember(object): 1307 role = 'member' 1308 1309 def __init__(self, name): 1310 assert isinstance(name, str) 1311 self.name = name 1312 self.owner = None 1313 1314 def set_owner(self, name): 1315 assert not self.owner 1316 self.owner = name 1317 1318 def check_clash(self, info, seen): 1319 cname = c_name(self.name) 1320 if cname.lower() != cname and self.owner not in name_case_whitelist: 1321 raise QAPISemError(info, 1322 "%s should not use uppercase" % self.describe()) 1323 if cname in seen: 1324 raise QAPISemError(info, "%s collides with %s" % 1325 (self.describe(), seen[cname].describe())) 1326 seen[cname] = self 1327 1328 def _pretty_owner(self): 1329 owner = self.owner 1330 if owner.startswith('q_obj_'): 1331 # See QAPISchema._make_implicit_object_type() - reverse the 1332 # mapping there to create a nice human-readable description 1333 owner = owner[6:] 1334 if owner.endswith('-arg'): 1335 return '(parameter of %s)' % owner[:-4] 1336 elif owner.endswith('-base'): 1337 return '(base of %s)' % owner[:-5] 1338 else: 1339 assert owner.endswith('-wrapper') 1340 # Unreachable and not implemented 1341 assert False 1342 if owner.endswith('Kind'): 1343 # See QAPISchema._make_implicit_enum_type() 1344 return '(branch of %s)' % owner[:-4] 1345 return '(%s of %s)' % (self.role, owner) 1346 1347 def describe(self): 1348 return "'%s' %s" % (self.name, self._pretty_owner()) 1349 1350 1351class QAPISchemaObjectTypeMember(QAPISchemaMember): 1352 def __init__(self, name, typ, optional): 1353 QAPISchemaMember.__init__(self, name) 1354 assert isinstance(typ, str) 1355 assert isinstance(optional, bool) 1356 self._type_name = typ 1357 self.type = None 1358 self.optional = optional 1359 1360 def check(self, schema): 1361 assert self.owner 1362 self.type = schema.lookup_type(self._type_name) 1363 assert self.type 1364 1365 1366class QAPISchemaObjectTypeVariants(object): 1367 def __init__(self, tag_name, tag_member, variants): 1368 # Flat unions pass tag_name but not tag_member. 1369 # Simple unions and alternates pass tag_member but not tag_name. 1370 # After check(), tag_member is always set, and tag_name remains 1371 # a reliable witness of being used by a flat union. 1372 assert bool(tag_member) != bool(tag_name) 1373 assert (isinstance(tag_name, str) or 1374 isinstance(tag_member, QAPISchemaObjectTypeMember)) 1375 assert len(variants) > 0 1376 for v in variants: 1377 assert isinstance(v, QAPISchemaObjectTypeVariant) 1378 self._tag_name = tag_name 1379 self.tag_member = tag_member 1380 self.variants = variants 1381 1382 def set_owner(self, name): 1383 for v in self.variants: 1384 v.set_owner(name) 1385 1386 def check(self, schema, seen): 1387 if not self.tag_member: # flat union 1388 self.tag_member = seen[c_name(self._tag_name)] 1389 assert self._tag_name == self.tag_member.name 1390 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 1391 if self._tag_name: # flat union 1392 # branches that are not explicitly covered get an empty type 1393 cases = set([v.name for v in self.variants]) 1394 for val in self.tag_member.type.values: 1395 if val.name not in cases: 1396 v = QAPISchemaObjectTypeVariant(val.name, 'q_empty') 1397 v.set_owner(self.tag_member.owner) 1398 self.variants.append(v) 1399 for v in self.variants: 1400 v.check(schema) 1401 # Union names must match enum values; alternate names are 1402 # checked separately. Use 'seen' to tell the two apart. 1403 if seen: 1404 assert v.name in self.tag_member.type.member_names() 1405 assert isinstance(v.type, QAPISchemaObjectType) 1406 v.type.check(schema) 1407 1408 def check_clash(self, info, seen): 1409 for v in self.variants: 1410 # Reset seen map for each variant, since qapi names from one 1411 # branch do not affect another branch 1412 assert isinstance(v.type, QAPISchemaObjectType) 1413 v.type.check_clash(info, dict(seen)) 1414 1415 1416class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): 1417 role = 'branch' 1418 1419 def __init__(self, name, typ): 1420 QAPISchemaObjectTypeMember.__init__(self, name, typ, False) 1421 1422 1423class QAPISchemaAlternateType(QAPISchemaType): 1424 def __init__(self, name, info, doc, ifcond, variants): 1425 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1426 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1427 assert variants.tag_member 1428 variants.set_owner(name) 1429 variants.tag_member.set_owner(self.name) 1430 self.variants = variants 1431 1432 def check(self, schema): 1433 self.variants.tag_member.check(schema) 1434 # Not calling self.variants.check_clash(), because there's nothing 1435 # to clash with 1436 self.variants.check(schema, {}) 1437 # Alternate branch names have no relation to the tag enum values; 1438 # so we have to check for potential name collisions ourselves. 1439 seen = {} 1440 for v in self.variants.variants: 1441 v.check_clash(self.info, seen) 1442 if self.doc: 1443 self.doc.connect_member(v) 1444 if self.doc: 1445 self.doc.check() 1446 1447 def c_type(self): 1448 return c_name(self.name) + pointer_suffix 1449 1450 def json_type(self): 1451 return 'value' 1452 1453 def visit(self, visitor): 1454 visitor.visit_alternate_type(self.name, self.info, self.variants) 1455 1456 def is_empty(self): 1457 return False 1458 1459 1460class QAPISchemaCommand(QAPISchemaEntity): 1461 def __init__(self, name, info, doc, ifcond, arg_type, ret_type, 1462 gen, success_response, boxed, allow_oob, allow_preconfig): 1463 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1464 assert not arg_type or isinstance(arg_type, str) 1465 assert not ret_type or isinstance(ret_type, str) 1466 self._arg_type_name = arg_type 1467 self.arg_type = None 1468 self._ret_type_name = ret_type 1469 self.ret_type = None 1470 self.gen = gen 1471 self.success_response = success_response 1472 self.boxed = boxed 1473 self.allow_oob = allow_oob 1474 self.allow_preconfig = allow_preconfig 1475 1476 def check(self, schema): 1477 if self._arg_type_name: 1478 self.arg_type = schema.lookup_type(self._arg_type_name) 1479 assert (isinstance(self.arg_type, QAPISchemaObjectType) or 1480 isinstance(self.arg_type, QAPISchemaAlternateType)) 1481 self.arg_type.check(schema) 1482 if self.boxed: 1483 if self.arg_type.is_empty(): 1484 raise QAPISemError(self.info, 1485 "Cannot use 'boxed' with empty type") 1486 else: 1487 assert not isinstance(self.arg_type, QAPISchemaAlternateType) 1488 assert not self.arg_type.variants 1489 elif self.boxed: 1490 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1491 if self._ret_type_name: 1492 self.ret_type = schema.lookup_type(self._ret_type_name) 1493 assert isinstance(self.ret_type, QAPISchemaType) 1494 1495 def visit(self, visitor): 1496 visitor.visit_command(self.name, self.info, 1497 self.arg_type, self.ret_type, 1498 self.gen, self.success_response, 1499 self.boxed, self.allow_oob, 1500 self.allow_preconfig) 1501 1502 1503class QAPISchemaEvent(QAPISchemaEntity): 1504 def __init__(self, name, info, doc, ifcond, arg_type, boxed): 1505 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1506 assert not arg_type or isinstance(arg_type, str) 1507 self._arg_type_name = arg_type 1508 self.arg_type = None 1509 self.boxed = boxed 1510 1511 def check(self, schema): 1512 if self._arg_type_name: 1513 self.arg_type = schema.lookup_type(self._arg_type_name) 1514 assert (isinstance(self.arg_type, QAPISchemaObjectType) or 1515 isinstance(self.arg_type, QAPISchemaAlternateType)) 1516 self.arg_type.check(schema) 1517 if self.boxed: 1518 if self.arg_type.is_empty(): 1519 raise QAPISemError(self.info, 1520 "Cannot use 'boxed' with empty type") 1521 else: 1522 assert not isinstance(self.arg_type, QAPISchemaAlternateType) 1523 assert not self.arg_type.variants 1524 elif self.boxed: 1525 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1526 1527 def visit(self, visitor): 1528 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed) 1529 1530 1531class QAPISchema(object): 1532 def __init__(self, fname): 1533 self._fname = fname 1534 if sys.version_info[0] >= 3: 1535 f = open(fname, 'r', encoding='utf-8') 1536 else: 1537 f = open(fname, 'r') 1538 parser = QAPISchemaParser(f) 1539 exprs = check_exprs(parser.exprs) 1540 self.docs = parser.docs 1541 self._entity_list = [] 1542 self._entity_dict = {} 1543 self._predefining = True 1544 self._def_predefineds() 1545 self._predefining = False 1546 self._def_exprs(exprs) 1547 self.check() 1548 1549 def _def_entity(self, ent): 1550 # Only the predefined types are allowed to not have info 1551 assert ent.info or self._predefining 1552 assert ent.name is None or ent.name not in self._entity_dict 1553 self._entity_list.append(ent) 1554 if ent.name is not None: 1555 self._entity_dict[ent.name] = ent 1556 if ent.info: 1557 ent.module = os.path.relpath(ent.info['file'], 1558 os.path.dirname(self._fname)) 1559 1560 def lookup_entity(self, name, typ=None): 1561 ent = self._entity_dict.get(name) 1562 if typ and not isinstance(ent, typ): 1563 return None 1564 return ent 1565 1566 def lookup_type(self, name): 1567 return self.lookup_entity(name, QAPISchemaType) 1568 1569 def _def_include(self, expr, info, doc): 1570 include = expr['include'] 1571 assert doc is None 1572 main_info = info 1573 while main_info['parent']: 1574 main_info = main_info['parent'] 1575 fname = os.path.relpath(include, os.path.dirname(main_info['file'])) 1576 self._def_entity(QAPISchemaInclude(fname, info)) 1577 1578 def _def_builtin_type(self, name, json_type, c_type): 1579 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) 1580 # Instantiating only the arrays that are actually used would 1581 # be nice, but we can't as long as their generated code 1582 # (qapi-builtin-types.[ch]) may be shared by some other 1583 # schema. 1584 self._make_array_type(name, None) 1585 1586 def _def_predefineds(self): 1587 for t in [('str', 'string', 'char' + pointer_suffix), 1588 ('number', 'number', 'double'), 1589 ('int', 'int', 'int64_t'), 1590 ('int8', 'int', 'int8_t'), 1591 ('int16', 'int', 'int16_t'), 1592 ('int32', 'int', 'int32_t'), 1593 ('int64', 'int', 'int64_t'), 1594 ('uint8', 'int', 'uint8_t'), 1595 ('uint16', 'int', 'uint16_t'), 1596 ('uint32', 'int', 'uint32_t'), 1597 ('uint64', 'int', 'uint64_t'), 1598 ('size', 'int', 'uint64_t'), 1599 ('bool', 'boolean', 'bool'), 1600 ('any', 'value', 'QObject' + pointer_suffix), 1601 ('null', 'null', 'QNull' + pointer_suffix)]: 1602 self._def_builtin_type(*t) 1603 self.the_empty_object_type = QAPISchemaObjectType( 1604 'q_empty', None, None, None, None, [], None) 1605 self._def_entity(self.the_empty_object_type) 1606 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', 1607 'qstring', 'qdict', 'qlist', 1608 'qbool']) 1609 self._def_entity(QAPISchemaEnumType('QType', None, None, None, 1610 qtype_values, 'QTYPE')) 1611 1612 def _make_enum_members(self, values): 1613 return [QAPISchemaMember(v) for v in values] 1614 1615 def _make_implicit_enum_type(self, name, info, ifcond, values): 1616 # See also QAPISchemaObjectTypeMember._pretty_owner() 1617 name = name + 'Kind' # Use namespace reserved by add_name() 1618 self._def_entity(QAPISchemaEnumType( 1619 name, info, None, ifcond, self._make_enum_members(values), None)) 1620 return name 1621 1622 def _make_array_type(self, element_type, info): 1623 name = element_type + 'List' # Use namespace reserved by add_name() 1624 if not self.lookup_type(name): 1625 self._def_entity(QAPISchemaArrayType(name, info, element_type)) 1626 return name 1627 1628 def _make_implicit_object_type(self, name, info, doc, ifcond, 1629 role, members): 1630 if not members: 1631 return None 1632 # See also QAPISchemaObjectTypeMember._pretty_owner() 1633 name = 'q_obj_%s-%s' % (name, role) 1634 typ = self.lookup_entity(name, QAPISchemaObjectType) 1635 if typ: 1636 # The implicit object type has multiple users. This can 1637 # happen only for simple unions' implicit wrapper types. 1638 # Its ifcond should be the disjunction of its user's 1639 # ifconds. Not implemented. Instead, we always pass the 1640 # wrapped type's ifcond, which is trivially the same for all 1641 # users. It's also necessary for the wrapper to compile. 1642 # But it's not tight: the disjunction need not imply it. We 1643 # may end up compiling useless wrapper types. 1644 # TODO kill simple unions or implement the disjunction 1645 assert ifcond == typ.ifcond 1646 else: 1647 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, 1648 None, members, None)) 1649 return name 1650 1651 def _def_enum_type(self, expr, info, doc): 1652 name = expr['enum'] 1653 data = expr['data'] 1654 prefix = expr.get('prefix') 1655 ifcond = expr.get('if') 1656 self._def_entity(QAPISchemaEnumType( 1657 name, info, doc, ifcond, 1658 self._make_enum_members(data), prefix)) 1659 1660 def _make_member(self, name, typ, info): 1661 optional = False 1662 if name.startswith('*'): 1663 name = name[1:] 1664 optional = True 1665 if isinstance(typ, list): 1666 assert len(typ) == 1 1667 typ = self._make_array_type(typ[0], info) 1668 return QAPISchemaObjectTypeMember(name, typ, optional) 1669 1670 def _make_members(self, data, info): 1671 return [self._make_member(key, value, info) 1672 for (key, value) in data.items()] 1673 1674 def _def_struct_type(self, expr, info, doc): 1675 name = expr['struct'] 1676 base = expr.get('base') 1677 data = expr['data'] 1678 ifcond = expr.get('if') 1679 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base, 1680 self._make_members(data, info), 1681 None)) 1682 1683 def _make_variant(self, case, typ): 1684 return QAPISchemaObjectTypeVariant(case, typ) 1685 1686 def _make_simple_variant(self, case, typ, info): 1687 if isinstance(typ, list): 1688 assert len(typ) == 1 1689 typ = self._make_array_type(typ[0], info) 1690 typ = self._make_implicit_object_type( 1691 typ, info, None, self.lookup_type(typ).ifcond, 1692 'wrapper', [self._make_member('data', typ, info)]) 1693 return QAPISchemaObjectTypeVariant(case, typ) 1694 1695 def _def_union_type(self, expr, info, doc): 1696 name = expr['union'] 1697 data = expr['data'] 1698 base = expr.get('base') 1699 ifcond = expr.get('if') 1700 tag_name = expr.get('discriminator') 1701 tag_member = None 1702 if isinstance(base, dict): 1703 base = self._make_implicit_object_type( 1704 name, info, doc, ifcond, 1705 'base', self._make_members(base, info)) 1706 if tag_name: 1707 variants = [self._make_variant(key, value) 1708 for (key, value) in data.items()] 1709 members = [] 1710 else: 1711 variants = [self._make_simple_variant(key, value, info) 1712 for (key, value) in data.items()] 1713 typ = self._make_implicit_enum_type(name, info, ifcond, 1714 [v.name for v in variants]) 1715 tag_member = QAPISchemaObjectTypeMember('type', typ, False) 1716 members = [tag_member] 1717 self._def_entity( 1718 QAPISchemaObjectType(name, info, doc, ifcond, base, members, 1719 QAPISchemaObjectTypeVariants(tag_name, 1720 tag_member, 1721 variants))) 1722 1723 def _def_alternate_type(self, expr, info, doc): 1724 name = expr['alternate'] 1725 data = expr['data'] 1726 ifcond = expr.get('if') 1727 variants = [self._make_variant(key, value) 1728 for (key, value) in data.items()] 1729 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) 1730 self._def_entity( 1731 QAPISchemaAlternateType(name, info, doc, ifcond, 1732 QAPISchemaObjectTypeVariants(None, 1733 tag_member, 1734 variants))) 1735 1736 def _def_command(self, expr, info, doc): 1737 name = expr['command'] 1738 data = expr.get('data') 1739 rets = expr.get('returns') 1740 gen = expr.get('gen', True) 1741 success_response = expr.get('success-response', True) 1742 boxed = expr.get('boxed', False) 1743 allow_oob = expr.get('allow-oob', False) 1744 allow_preconfig = expr.get('allow-preconfig', False) 1745 ifcond = expr.get('if') 1746 if isinstance(data, OrderedDict): 1747 data = self._make_implicit_object_type( 1748 name, info, doc, ifcond, 'arg', self._make_members(data, info)) 1749 if isinstance(rets, list): 1750 assert len(rets) == 1 1751 rets = self._make_array_type(rets[0], info) 1752 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, 1753 gen, success_response, 1754 boxed, allow_oob, allow_preconfig)) 1755 1756 def _def_event(self, expr, info, doc): 1757 name = expr['event'] 1758 data = expr.get('data') 1759 boxed = expr.get('boxed', False) 1760 ifcond = expr.get('if') 1761 if isinstance(data, OrderedDict): 1762 data = self._make_implicit_object_type( 1763 name, info, doc, ifcond, 'arg', self._make_members(data, info)) 1764 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) 1765 1766 def _def_exprs(self, exprs): 1767 for expr_elem in exprs: 1768 expr = expr_elem['expr'] 1769 info = expr_elem['info'] 1770 doc = expr_elem.get('doc') 1771 if 'enum' in expr: 1772 self._def_enum_type(expr, info, doc) 1773 elif 'struct' in expr: 1774 self._def_struct_type(expr, info, doc) 1775 elif 'union' in expr: 1776 self._def_union_type(expr, info, doc) 1777 elif 'alternate' in expr: 1778 self._def_alternate_type(expr, info, doc) 1779 elif 'command' in expr: 1780 self._def_command(expr, info, doc) 1781 elif 'event' in expr: 1782 self._def_event(expr, info, doc) 1783 elif 'include' in expr: 1784 self._def_include(expr, info, doc) 1785 else: 1786 assert False 1787 1788 def check(self): 1789 for ent in self._entity_list: 1790 ent.check(self) 1791 1792 def visit(self, visitor): 1793 visitor.visit_begin(self) 1794 module = None 1795 for entity in self._entity_list: 1796 if visitor.visit_needed(entity): 1797 if entity.module != module: 1798 module = entity.module 1799 visitor.visit_module(module) 1800 entity.visit(visitor) 1801 visitor.visit_end() 1802 1803 1804# 1805# Code generation helpers 1806# 1807 1808def camel_case(name): 1809 new_name = '' 1810 first = True 1811 for ch in name: 1812 if ch in ['_', '-']: 1813 first = True 1814 elif first: 1815 new_name += ch.upper() 1816 first = False 1817 else: 1818 new_name += ch.lower() 1819 return new_name 1820 1821 1822# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 1823# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 1824# ENUM24_Name -> ENUM24_NAME 1825def camel_to_upper(value): 1826 c_fun_str = c_name(value, False) 1827 if value.isupper(): 1828 return c_fun_str 1829 1830 new_name = '' 1831 l = len(c_fun_str) 1832 for i in range(l): 1833 c = c_fun_str[i] 1834 # When c is upper and no '_' appears before, do more checks 1835 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_': 1836 if i < l - 1 and c_fun_str[i + 1].islower(): 1837 new_name += '_' 1838 elif c_fun_str[i - 1].isdigit(): 1839 new_name += '_' 1840 new_name += c 1841 return new_name.lstrip('_').upper() 1842 1843 1844def c_enum_const(type_name, const_name, prefix=None): 1845 if prefix is not None: 1846 type_name = prefix 1847 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() 1848 1849if hasattr(str, 'maketrans'): 1850 c_name_trans = str.maketrans('.-', '__') 1851else: 1852 c_name_trans = string.maketrans('.-', '__') 1853 1854 1855# Map @name to a valid C identifier. 1856# If @protect, avoid returning certain ticklish identifiers (like 1857# C keywords) by prepending 'q_'. 1858# 1859# Used for converting 'name' from a 'name':'type' qapi definition 1860# into a generated struct member, as well as converting type names 1861# into substrings of a generated C function name. 1862# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo' 1863# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int' 1864def c_name(name, protect=True): 1865 # ANSI X3J11/88-090, 3.1.1 1866 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', 1867 'default', 'do', 'double', 'else', 'enum', 'extern', 1868 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 1869 'return', 'short', 'signed', 'sizeof', 'static', 1870 'struct', 'switch', 'typedef', 'union', 'unsigned', 1871 'void', 'volatile', 'while']) 1872 # ISO/IEC 9899:1999, 6.4.1 1873 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) 1874 # ISO/IEC 9899:2011, 6.4.1 1875 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', 1876 '_Noreturn', '_Static_assert', '_Thread_local']) 1877 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html 1878 # excluding _.* 1879 gcc_words = set(['asm', 'typeof']) 1880 # C++ ISO/IEC 14882:2003 2.11 1881 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', 1882 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', 1883 'namespace', 'new', 'operator', 'private', 'protected', 1884 'public', 'reinterpret_cast', 'static_cast', 'template', 1885 'this', 'throw', 'true', 'try', 'typeid', 'typename', 1886 'using', 'virtual', 'wchar_t', 1887 # alternative representations 1888 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 1889 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) 1890 # namespace pollution: 1891 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) 1892 name = name.translate(c_name_trans) 1893 if protect and (name in c89_words | c99_words | c11_words | gcc_words 1894 | cpp_words | polluted_words): 1895 return 'q_' + name 1896 return name 1897 1898eatspace = '\033EATSPACE.' 1899pointer_suffix = ' *' + eatspace 1900 1901 1902def genindent(count): 1903 ret = '' 1904 for _ in range(count): 1905 ret += ' ' 1906 return ret 1907 1908indent_level = 0 1909 1910 1911def push_indent(indent_amount=4): 1912 global indent_level 1913 indent_level += indent_amount 1914 1915 1916def pop_indent(indent_amount=4): 1917 global indent_level 1918 indent_level -= indent_amount 1919 1920 1921# Generate @code with @kwds interpolated. 1922# Obey indent_level, and strip eatspace. 1923def cgen(code, **kwds): 1924 raw = code % kwds 1925 if indent_level: 1926 indent = genindent(indent_level) 1927 # re.subn() lacks flags support before Python 2.7, use re.compile() 1928 raw = re.subn(re.compile(r'^.', re.MULTILINE), 1929 indent + r'\g<0>', raw) 1930 raw = raw[0] 1931 return re.sub(re.escape(eatspace) + r' *', '', raw) 1932 1933 1934def mcgen(code, **kwds): 1935 if code[0] == '\n': 1936 code = code[1:] 1937 return cgen(code, **kwds) 1938 1939 1940def guardname(filename): 1941 return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper() 1942 1943 1944def guardstart(name): 1945 return mcgen(''' 1946#ifndef %(name)s 1947#define %(name)s 1948 1949''', 1950 name=guardname(name)) 1951 1952 1953def guardend(name): 1954 return mcgen(''' 1955 1956#endif /* %(name)s */ 1957''', 1958 name=guardname(name)) 1959 1960 1961def gen_enum_lookup(name, values, prefix=None): 1962 ret = mcgen(''' 1963 1964const QEnumLookup %(c_name)s_lookup = { 1965 .array = (const char *const[]) { 1966''', 1967 c_name=c_name(name)) 1968 for value in values: 1969 index = c_enum_const(name, value, prefix) 1970 ret += mcgen(''' 1971 [%(index)s] = "%(value)s", 1972''', 1973 index=index, value=value) 1974 1975 ret += mcgen(''' 1976 }, 1977 .size = %(max_index)s 1978}; 1979''', 1980 max_index=c_enum_const(name, '_MAX', prefix)) 1981 return ret 1982 1983 1984def gen_enum(name, values, prefix=None): 1985 # append automatically generated _MAX value 1986 enum_values = values + ['_MAX'] 1987 1988 ret = mcgen(''' 1989 1990typedef enum %(c_name)s { 1991''', 1992 c_name=c_name(name)) 1993 1994 i = 0 1995 for value in enum_values: 1996 ret += mcgen(''' 1997 %(c_enum)s = %(i)d, 1998''', 1999 c_enum=c_enum_const(name, value, prefix), 2000 i=i) 2001 i += 1 2002 2003 ret += mcgen(''' 2004} %(c_name)s; 2005''', 2006 c_name=c_name(name)) 2007 2008 ret += mcgen(''' 2009 2010#define %(c_name)s_str(val) \\ 2011 qapi_enum_lookup(&%(c_name)s_lookup, (val)) 2012 2013extern const QEnumLookup %(c_name)s_lookup; 2014''', 2015 c_name=c_name(name)) 2016 return ret 2017 2018 2019def build_params(arg_type, boxed, extra): 2020 if not arg_type: 2021 assert not boxed 2022 return extra 2023 ret = '' 2024 sep = '' 2025 if boxed: 2026 ret += '%s arg' % arg_type.c_param_type() 2027 sep = ', ' 2028 else: 2029 assert not arg_type.variants 2030 for memb in arg_type.members: 2031 ret += sep 2032 sep = ', ' 2033 if memb.optional: 2034 ret += 'bool has_%s, ' % c_name(memb.name) 2035 ret += '%s %s' % (memb.type.c_param_type(), 2036 c_name(memb.name)) 2037 if extra: 2038 ret += sep + extra 2039 return ret 2040 2041 2042# 2043# Accumulate and write output 2044# 2045 2046class QAPIGen(object): 2047 2048 def __init__(self): 2049 self._preamble = '' 2050 self._body = '' 2051 2052 def preamble_add(self, text): 2053 self._preamble += text 2054 2055 def add(self, text): 2056 self._body += text 2057 2058 def _top(self, fname): 2059 return '' 2060 2061 def _bottom(self, fname): 2062 return '' 2063 2064 def write(self, output_dir, fname): 2065 pathname = os.path.join(output_dir, fname) 2066 dir = os.path.dirname(pathname) 2067 if dir: 2068 try: 2069 os.makedirs(dir) 2070 except os.error as e: 2071 if e.errno != errno.EEXIST: 2072 raise 2073 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) 2074 if sys.version_info[0] >= 3: 2075 f = open(fd, 'r+', encoding='utf-8') 2076 else: 2077 f = os.fdopen(fd, 'r+') 2078 text = (self._top(fname) + self._preamble + self._body 2079 + self._bottom(fname)) 2080 oldtext = f.read(len(text) + 1) 2081 if text != oldtext: 2082 f.seek(0) 2083 f.truncate(0) 2084 f.write(text) 2085 f.close() 2086 2087 2088class QAPIGenC(QAPIGen): 2089 2090 def __init__(self, blurb, pydoc): 2091 QAPIGen.__init__(self) 2092 self._blurb = blurb 2093 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, 2094 re.MULTILINE)) 2095 2096 def _top(self, fname): 2097 return mcgen(''' 2098/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ 2099 2100/* 2101%(blurb)s 2102 * 2103 * %(copyright)s 2104 * 2105 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 2106 * See the COPYING.LIB file in the top-level directory. 2107 */ 2108 2109''', 2110 blurb=self._blurb, copyright=self._copyright) 2111 2112 def _bottom(self, fname): 2113 return mcgen(''' 2114/* Dummy declaration to prevent empty .o file */ 2115char dummy_%(name)s; 2116''', 2117 name=c_name(fname)) 2118 2119 2120class QAPIGenH(QAPIGenC): 2121 2122 def _top(self, fname): 2123 return QAPIGenC._top(self, fname) + guardstart(fname) 2124 2125 def _bottom(self, fname): 2126 return guardend(fname) 2127 2128 2129class QAPIGenDoc(QAPIGen): 2130 2131 def _top(self, fname): 2132 return (QAPIGen._top(self, fname) 2133 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') 2134 2135 2136class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): 2137 2138 def __init__(self, prefix, what, blurb, pydoc): 2139 self._prefix = prefix 2140 self._what = what 2141 self._genc = QAPIGenC(blurb, pydoc) 2142 self._genh = QAPIGenH(blurb, pydoc) 2143 2144 def write(self, output_dir): 2145 self._genc.write(output_dir, self._prefix + self._what + '.c') 2146 self._genh.write(output_dir, self._prefix + self._what + '.h') 2147 2148 2149class QAPISchemaModularCVisitor(QAPISchemaVisitor): 2150 2151 def __init__(self, prefix, what, blurb, pydoc): 2152 self._prefix = prefix 2153 self._what = what 2154 self._blurb = blurb 2155 self._pydoc = pydoc 2156 self._module = {} 2157 self._main_module = None 2158 2159 def _module_basename(self, what, name): 2160 if name is None: 2161 return re.sub(r'-', '-builtin-', what) 2162 basename = os.path.join(os.path.dirname(name), 2163 self._prefix + what) 2164 if name == self._main_module: 2165 return basename 2166 return basename + '-' + os.path.splitext(os.path.basename(name))[0] 2167 2168 def _add_module(self, name, blurb): 2169 if self._main_module is None and name is not None: 2170 self._main_module = name 2171 genc = QAPIGenC(blurb, self._pydoc) 2172 genh = QAPIGenH(blurb, self._pydoc) 2173 self._module[name] = (genc, genh) 2174 self._set_module(name) 2175 2176 def _set_module(self, name): 2177 self._genc, self._genh = self._module[name] 2178 2179 def write(self, output_dir, opt_builtins=False): 2180 for name in self._module: 2181 if name is None and not opt_builtins: 2182 continue 2183 basename = self._module_basename(self._what, name) 2184 (genc, genh) = self._module[name] 2185 genc.write(output_dir, basename + '.c') 2186 genh.write(output_dir, basename + '.h') 2187 2188 def _begin_module(self, name): 2189 pass 2190 2191 def visit_module(self, name): 2192 if name in self._module: 2193 self._set_module(name) 2194 return 2195 self._add_module(name, self._blurb) 2196 self._begin_module(name) 2197 2198 def visit_include(self, name, info): 2199 basename = self._module_basename(self._what, name) 2200 self._genh.preamble_add(mcgen(''' 2201#include "%(basename)s.h" 2202''', 2203 basename=basename)) 2204