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