qapi: add 'if' to enum members
[qemu/ar7.git] / scripts / qapi / common.py
bloba609ffcfee6123b40dc70209c11c848317476a76
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 enum_get_names(expr):
744 return [e['name'] for e in expr['data']]
747 def check_union(expr, info):
748 name = expr['union']
749 base = expr.get('base')
750 discriminator = expr.get('discriminator')
751 members = expr['data']
753 # Two types of unions, determined by discriminator.
755 # With no discriminator it is a simple union.
756 if discriminator is None:
757 enum_define = None
758 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
759 if base is not None:
760 raise QAPISemError(info, "Simple union '%s' must not have a base" %
761 name)
763 # Else, it's a flat union.
764 else:
765 # The object must have a string or dictionary 'base'.
766 check_type(info, "'base' for union '%s'" % name,
767 base, allow_dict=True, allow_optional=True,
768 allow_metas=['struct'])
769 if not base:
770 raise QAPISemError(info, "Flat union '%s' must have a base"
771 % name)
772 base_members = find_base_members(base)
773 assert base_members is not None
775 # The value of member 'discriminator' must name a non-optional
776 # member of the base struct.
777 check_name(info, "Discriminator of flat union '%s'" % name,
778 discriminator)
779 discriminator_type = base_members.get(discriminator)
780 if not discriminator_type:
781 raise QAPISemError(info,
782 "Discriminator '%s' is not a member of base "
783 "struct '%s'"
784 % (discriminator, base))
785 enum_define = enum_types.get(discriminator_type)
786 allow_metas = ['struct']
787 # Do not allow string discriminator
788 if not enum_define:
789 raise QAPISemError(info,
790 "Discriminator '%s' must be of enumeration "
791 "type" % discriminator)
793 # Check every branch; don't allow an empty union
794 if len(members) == 0:
795 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
796 for (key, value) in members.items():
797 check_name(info, "Member of union '%s'" % name, key)
799 # Each value must name a known type
800 check_type(info, "Member '%s' of union '%s'" % (key, name),
801 value, allow_array=not base, allow_metas=allow_metas)
803 # If the discriminator names an enum type, then all members
804 # of 'data' must also be members of the enum type.
805 if enum_define:
806 if key not in enum_get_names(enum_define):
807 raise QAPISemError(info,
808 "Discriminator value '%s' is not found in "
809 "enum '%s'"
810 % (key, enum_define['enum']))
813 def check_alternate(expr, info):
814 name = expr['alternate']
815 members = expr['data']
816 types_seen = {}
818 # Check every branch; require at least two branches
819 if len(members) < 2:
820 raise QAPISemError(info,
821 "Alternate '%s' should have at least two branches "
822 "in 'data'" % name)
823 for (key, value) in members.items():
824 check_name(info, "Member of alternate '%s'" % name, key)
826 # Ensure alternates have no type conflicts.
827 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
828 value,
829 allow_metas=['built-in', 'union', 'struct', 'enum'])
830 qtype = find_alternate_member_qtype(value)
831 if not qtype:
832 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
833 "type '%s'" % (name, key, value))
834 conflicting = set([qtype])
835 if qtype == 'QTYPE_QSTRING':
836 enum_expr = enum_types.get(value)
837 if enum_expr:
838 for v in enum_get_names(enum_expr):
839 if v in ['on', 'off']:
840 conflicting.add('QTYPE_QBOOL')
841 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
842 conflicting.add('QTYPE_QNUM')
843 else:
844 conflicting.add('QTYPE_QNUM')
845 conflicting.add('QTYPE_QBOOL')
846 for qt in conflicting:
847 if qt in types_seen:
848 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
849 "be distinguished from member '%s'"
850 % (name, key, types_seen[qt]))
851 types_seen[qt] = key
854 def normalize_enum(expr):
855 if isinstance(expr['data'], list):
856 expr['data'] = [m if isinstance(m, dict) else {'name': m}
857 for m in expr['data']]
860 def check_enum(expr, info):
861 name = expr['enum']
862 members = expr['data']
863 prefix = expr.get('prefix')
865 if not isinstance(members, list):
866 raise QAPISemError(info,
867 "Enum '%s' requires an array for 'data'" % name)
868 if prefix is not None and not isinstance(prefix, str):
869 raise QAPISemError(info,
870 "Enum '%s' requires a string for 'prefix'" % name)
872 for member in members:
873 source = "dictionary member of enum '%s'" % name
874 check_known_keys(info, source, member, ['name'], ['if'])
875 check_if(member, info)
876 check_name(info, "Member of enum '%s'" % name, member['name'],
877 enum_member=True)
880 def check_struct(expr, info):
881 name = expr['struct']
882 members = expr['data']
884 check_type(info, "'data' for struct '%s'" % name, members,
885 allow_dict=True, allow_optional=True)
886 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
887 allow_metas=['struct'])
890 def check_known_keys(info, source, keys, required, optional):
892 def pprint(elems):
893 return ', '.join("'" + e + "'" for e in sorted(elems))
895 missing = set(required) - set(keys)
896 if missing:
897 raise QAPISemError(info, "Key%s %s %s missing from %s"
898 % ('s' if len(missing) > 1 else '', pprint(missing),
899 'are' if len(missing) > 1 else 'is', source))
900 allowed = set(required + optional)
901 unknown = set(keys) - allowed
902 if unknown:
903 raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s."
904 % ('s' if len(unknown) > 1 else '', pprint(unknown),
905 source, pprint(allowed)))
908 def check_keys(expr_elem, meta, required, optional=[]):
909 expr = expr_elem['expr']
910 info = expr_elem['info']
911 name = expr[meta]
912 if not isinstance(name, str):
913 raise QAPISemError(info, "'%s' key must have a string value" % meta)
914 required = required + [meta]
915 source = "%s '%s'" % (meta, name)
916 check_known_keys(info, source, expr.keys(), required, optional)
917 for (key, value) in expr.items():
918 if key in ['gen', 'success-response'] and value is not False:
919 raise QAPISemError(info,
920 "'%s' of %s '%s' should only use false value"
921 % (key, meta, name))
922 if (key in ['boxed', 'allow-oob', 'allow-preconfig']
923 and value is not True):
924 raise QAPISemError(info,
925 "'%s' of %s '%s' should only use true value"
926 % (key, meta, name))
927 if key == 'if':
928 check_if(expr, info)
931 def check_exprs(exprs):
932 global all_names
934 # Populate name table with names of built-in types
935 for builtin in builtin_types.keys():
936 all_names[builtin] = 'built-in'
938 # Learn the types and check for valid expression keys
939 for expr_elem in exprs:
940 expr = expr_elem['expr']
941 info = expr_elem['info']
942 doc = expr_elem.get('doc')
944 if 'include' in expr:
945 continue
947 if not doc and doc_required:
948 raise QAPISemError(info,
949 "Expression missing documentation comment")
951 if 'enum' in expr:
952 meta = 'enum'
953 check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
954 normalize_enum(expr)
955 enum_types[expr[meta]] = expr
956 elif 'union' in expr:
957 meta = 'union'
958 check_keys(expr_elem, 'union', ['data'],
959 ['base', 'discriminator', 'if'])
960 union_types[expr[meta]] = expr
961 elif 'alternate' in expr:
962 meta = 'alternate'
963 check_keys(expr_elem, 'alternate', ['data'], ['if'])
964 elif 'struct' in expr:
965 meta = 'struct'
966 check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
967 struct_types[expr[meta]] = expr
968 elif 'command' in expr:
969 meta = 'command'
970 check_keys(expr_elem, 'command', [],
971 ['data', 'returns', 'gen', 'success-response',
972 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
973 elif 'event' in expr:
974 meta = 'event'
975 check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
976 else:
977 raise QAPISemError(expr_elem['info'],
978 "Expression is missing metatype")
979 name = expr[meta]
980 add_name(name, info, meta)
981 if doc and doc.symbol != name:
982 raise QAPISemError(info, "Definition of '%s' follows documentation"
983 " for '%s'" % (name, doc.symbol))
985 # Try again for hidden UnionKind enum
986 for expr_elem in exprs:
987 expr = expr_elem['expr']
989 if 'include' in expr:
990 continue
991 if 'union' in expr and not discriminator_find_enum_define(expr):
992 name = '%sKind' % expr['union']
993 elif 'alternate' in expr:
994 name = '%sKind' % expr['alternate']
995 else:
996 continue
997 enum_types[name] = {'enum': name}
998 add_name(name, info, 'enum', implicit=True)
1000 # Validate that exprs make sense
1001 for expr_elem in exprs:
1002 expr = expr_elem['expr']
1003 info = expr_elem['info']
1004 doc = expr_elem.get('doc')
1006 if 'include' in expr:
1007 continue
1008 if 'enum' in expr:
1009 check_enum(expr, info)
1010 elif 'union' in expr:
1011 check_union(expr, info)
1012 elif 'alternate' in expr:
1013 check_alternate(expr, info)
1014 elif 'struct' in expr:
1015 check_struct(expr, info)
1016 elif 'command' in expr:
1017 check_command(expr, info)
1018 elif 'event' in expr:
1019 check_event(expr, info)
1020 else:
1021 assert False, 'unexpected meta type'
1023 if doc:
1024 doc.check_expr(expr)
1026 return exprs
1030 # Schema compiler frontend
1033 def listify_cond(ifcond):
1034 if not ifcond:
1035 return []
1036 if not isinstance(ifcond, list):
1037 return [ifcond]
1038 return ifcond
1041 class QAPISchemaEntity(object):
1042 def __init__(self, name, info, doc, ifcond=None):
1043 assert name is None or isinstance(name, str)
1044 self.name = name
1045 self.module = None
1046 # For explicitly defined entities, info points to the (explicit)
1047 # definition. For builtins (and their arrays), info is None.
1048 # For implicitly defined entities, info points to a place that
1049 # triggered the implicit definition (there may be more than one
1050 # such place).
1051 self.info = info
1052 self.doc = doc
1053 self._ifcond = ifcond # self.ifcond is set only after .check()
1055 def c_name(self):
1056 return c_name(self.name)
1058 def check(self, schema):
1059 if isinstance(self._ifcond, QAPISchemaType):
1060 # inherit the condition from a type
1061 typ = self._ifcond
1062 typ.check(schema)
1063 self.ifcond = typ.ifcond
1064 else:
1065 self.ifcond = listify_cond(self._ifcond)
1067 def is_implicit(self):
1068 return not self.info
1070 def visit(self, visitor):
1071 pass
1074 class QAPISchemaVisitor(object):
1075 def visit_begin(self, schema):
1076 pass
1078 def visit_end(self):
1079 pass
1081 def visit_module(self, fname):
1082 pass
1084 def visit_needed(self, entity):
1085 # Default to visiting everything
1086 return True
1088 def visit_include(self, fname, info):
1089 pass
1091 def visit_builtin_type(self, name, info, json_type):
1092 pass
1094 def visit_enum_type(self, name, info, ifcond, members, prefix):
1095 pass
1097 def visit_array_type(self, name, info, ifcond, element_type):
1098 pass
1100 def visit_object_type(self, name, info, ifcond, base, members, variants):
1101 pass
1103 def visit_object_type_flat(self, name, info, ifcond, members, variants):
1104 pass
1106 def visit_alternate_type(self, name, info, ifcond, variants):
1107 pass
1109 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1110 success_response, boxed, allow_oob, allow_preconfig):
1111 pass
1113 def visit_event(self, name, info, ifcond, arg_type, boxed):
1114 pass
1117 class QAPISchemaInclude(QAPISchemaEntity):
1119 def __init__(self, fname, info):
1120 QAPISchemaEntity.__init__(self, None, info, None)
1121 self.fname = fname
1123 def visit(self, visitor):
1124 visitor.visit_include(self.fname, self.info)
1127 class QAPISchemaType(QAPISchemaEntity):
1128 # Return the C type for common use.
1129 # For the types we commonly box, this is a pointer type.
1130 def c_type(self):
1131 pass
1133 # Return the C type to be used in a parameter list.
1134 def c_param_type(self):
1135 return self.c_type()
1137 # Return the C type to be used where we suppress boxing.
1138 def c_unboxed_type(self):
1139 return self.c_type()
1141 def json_type(self):
1142 pass
1144 def alternate_qtype(self):
1145 json2qtype = {
1146 'null': 'QTYPE_QNULL',
1147 'string': 'QTYPE_QSTRING',
1148 'number': 'QTYPE_QNUM',
1149 'int': 'QTYPE_QNUM',
1150 'boolean': 'QTYPE_QBOOL',
1151 'object': 'QTYPE_QDICT'
1153 return json2qtype.get(self.json_type())
1155 def doc_type(self):
1156 if self.is_implicit():
1157 return None
1158 return self.name
1161 class QAPISchemaBuiltinType(QAPISchemaType):
1162 def __init__(self, name, json_type, c_type):
1163 QAPISchemaType.__init__(self, name, None, None)
1164 assert not c_type or isinstance(c_type, str)
1165 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1166 'value')
1167 self._json_type_name = json_type
1168 self._c_type_name = c_type
1170 def c_name(self):
1171 return self.name
1173 def c_type(self):
1174 return self._c_type_name
1176 def c_param_type(self):
1177 if self.name == 'str':
1178 return 'const ' + self._c_type_name
1179 return self._c_type_name
1181 def json_type(self):
1182 return self._json_type_name
1184 def doc_type(self):
1185 return self.json_type()
1187 def visit(self, visitor):
1188 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1191 class QAPISchemaEnumType(QAPISchemaType):
1192 def __init__(self, name, info, doc, ifcond, members, prefix):
1193 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1194 for m in members:
1195 assert isinstance(m, QAPISchemaMember)
1196 m.set_owner(name)
1197 assert prefix is None or isinstance(prefix, str)
1198 self.members = members
1199 self.prefix = prefix
1201 def check(self, schema):
1202 QAPISchemaType.check(self, schema)
1203 seen = {}
1204 for m in self.members:
1205 m.check_clash(self.info, seen)
1206 if self.doc:
1207 self.doc.connect_member(m)
1209 def is_implicit(self):
1210 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1211 return self.name.endswith('Kind') or self.name == 'QType'
1213 def c_type(self):
1214 return c_name(self.name)
1216 def member_names(self):
1217 return [m.name for m in self.members]
1219 def json_type(self):
1220 return 'string'
1222 def visit(self, visitor):
1223 visitor.visit_enum_type(self.name, self.info, self.ifcond,
1224 self.members, self.prefix)
1227 class QAPISchemaArrayType(QAPISchemaType):
1228 def __init__(self, name, info, element_type):
1229 QAPISchemaType.__init__(self, name, info, None, None)
1230 assert isinstance(element_type, str)
1231 self._element_type_name = element_type
1232 self.element_type = None
1234 def check(self, schema):
1235 QAPISchemaType.check(self, schema)
1236 self.element_type = schema.lookup_type(self._element_type_name)
1237 assert self.element_type
1238 self.element_type.check(schema)
1239 self.ifcond = self.element_type.ifcond
1241 def is_implicit(self):
1242 return True
1244 def c_type(self):
1245 return c_name(self.name) + pointer_suffix
1247 def json_type(self):
1248 return 'array'
1250 def doc_type(self):
1251 elt_doc_type = self.element_type.doc_type()
1252 if not elt_doc_type:
1253 return None
1254 return 'array of ' + elt_doc_type
1256 def visit(self, visitor):
1257 visitor.visit_array_type(self.name, self.info, self.ifcond,
1258 self.element_type)
1261 class QAPISchemaObjectType(QAPISchemaType):
1262 def __init__(self, name, info, doc, ifcond,
1263 base, local_members, variants):
1264 # struct has local_members, optional base, and no variants
1265 # flat union has base, variants, and no local_members
1266 # simple union has local_members, variants, and no base
1267 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1268 assert base is None or isinstance(base, str)
1269 for m in local_members:
1270 assert isinstance(m, QAPISchemaObjectTypeMember)
1271 m.set_owner(name)
1272 if variants is not None:
1273 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1274 variants.set_owner(name)
1275 self._base_name = base
1276 self.base = None
1277 self.local_members = local_members
1278 self.variants = variants
1279 self.members = None
1281 def check(self, schema):
1282 QAPISchemaType.check(self, schema)
1283 if self.members is False: # check for cycles
1284 raise QAPISemError(self.info,
1285 "Object %s contains itself" % self.name)
1286 if self.members:
1287 return
1288 self.members = False # mark as being checked
1289 seen = OrderedDict()
1290 if self._base_name:
1291 self.base = schema.lookup_type(self._base_name)
1292 assert isinstance(self.base, QAPISchemaObjectType)
1293 self.base.check(schema)
1294 self.base.check_clash(self.info, seen)
1295 for m in self.local_members:
1296 m.check(schema)
1297 m.check_clash(self.info, seen)
1298 if self.doc:
1299 self.doc.connect_member(m)
1300 self.members = seen.values()
1301 if self.variants:
1302 self.variants.check(schema, seen)
1303 assert self.variants.tag_member in self.members
1304 self.variants.check_clash(self.info, seen)
1305 if self.doc:
1306 self.doc.check()
1308 # Check that the members of this type do not cause duplicate JSON members,
1309 # and update seen to track the members seen so far. Report any errors
1310 # on behalf of info, which is not necessarily self.info
1311 def check_clash(self, info, seen):
1312 assert not self.variants # not implemented
1313 for m in self.members:
1314 m.check_clash(info, seen)
1316 def is_implicit(self):
1317 # See QAPISchema._make_implicit_object_type(), as well as
1318 # _def_predefineds()
1319 return self.name.startswith('q_')
1321 def is_empty(self):
1322 assert self.members is not None
1323 return not self.members and not self.variants
1325 def c_name(self):
1326 assert self.name != 'q_empty'
1327 return QAPISchemaType.c_name(self)
1329 def c_type(self):
1330 assert not self.is_implicit()
1331 return c_name(self.name) + pointer_suffix
1333 def c_unboxed_type(self):
1334 return c_name(self.name)
1336 def json_type(self):
1337 return 'object'
1339 def visit(self, visitor):
1340 visitor.visit_object_type(self.name, self.info, self.ifcond,
1341 self.base, self.local_members, self.variants)
1342 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1343 self.members, self.variants)
1346 class QAPISchemaMember(object):
1347 role = 'member'
1349 def __init__(self, name, ifcond=None):
1350 assert isinstance(name, str)
1351 self.name = name
1352 self.ifcond = listify_cond(ifcond)
1353 self.owner = None
1355 def set_owner(self, name):
1356 assert not self.owner
1357 self.owner = name
1359 def check_clash(self, info, seen):
1360 cname = c_name(self.name)
1361 if cname.lower() != cname and self.owner not in name_case_whitelist:
1362 raise QAPISemError(info,
1363 "%s should not use uppercase" % self.describe())
1364 if cname in seen:
1365 raise QAPISemError(info, "%s collides with %s" %
1366 (self.describe(), seen[cname].describe()))
1367 seen[cname] = self
1369 def _pretty_owner(self):
1370 owner = self.owner
1371 if owner.startswith('q_obj_'):
1372 # See QAPISchema._make_implicit_object_type() - reverse the
1373 # mapping there to create a nice human-readable description
1374 owner = owner[6:]
1375 if owner.endswith('-arg'):
1376 return '(parameter of %s)' % owner[:-4]
1377 elif owner.endswith('-base'):
1378 return '(base of %s)' % owner[:-5]
1379 else:
1380 assert owner.endswith('-wrapper')
1381 # Unreachable and not implemented
1382 assert False
1383 if owner.endswith('Kind'):
1384 # See QAPISchema._make_implicit_enum_type()
1385 return '(branch of %s)' % owner[:-4]
1386 return '(%s of %s)' % (self.role, owner)
1388 def describe(self):
1389 return "'%s' %s" % (self.name, self._pretty_owner())
1392 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1393 def __init__(self, name, typ, optional):
1394 QAPISchemaMember.__init__(self, name)
1395 assert isinstance(typ, str)
1396 assert isinstance(optional, bool)
1397 self._type_name = typ
1398 self.type = None
1399 self.optional = optional
1401 def check(self, schema):
1402 assert self.owner
1403 self.type = schema.lookup_type(self._type_name)
1404 assert self.type
1407 class QAPISchemaObjectTypeVariants(object):
1408 def __init__(self, tag_name, tag_member, variants):
1409 # Flat unions pass tag_name but not tag_member.
1410 # Simple unions and alternates pass tag_member but not tag_name.
1411 # After check(), tag_member is always set, and tag_name remains
1412 # a reliable witness of being used by a flat union.
1413 assert bool(tag_member) != bool(tag_name)
1414 assert (isinstance(tag_name, str) or
1415 isinstance(tag_member, QAPISchemaObjectTypeMember))
1416 assert len(variants) > 0
1417 for v in variants:
1418 assert isinstance(v, QAPISchemaObjectTypeVariant)
1419 self._tag_name = tag_name
1420 self.tag_member = tag_member
1421 self.variants = variants
1423 def set_owner(self, name):
1424 for v in self.variants:
1425 v.set_owner(name)
1427 def check(self, schema, seen):
1428 if not self.tag_member: # flat union
1429 self.tag_member = seen[c_name(self._tag_name)]
1430 assert self._tag_name == self.tag_member.name
1431 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1432 if self._tag_name: # flat union
1433 # branches that are not explicitly covered get an empty type
1434 cases = set([v.name for v in self.variants])
1435 for m in self.tag_member.type.members:
1436 if m.name not in cases:
1437 v = QAPISchemaObjectTypeVariant(m.name, 'q_empty')
1438 v.set_owner(self.tag_member.owner)
1439 self.variants.append(v)
1440 for v in self.variants:
1441 v.check(schema)
1442 # Union names must match enum values; alternate names are
1443 # checked separately. Use 'seen' to tell the two apart.
1444 if seen:
1445 assert v.name in self.tag_member.type.member_names()
1446 assert isinstance(v.type, QAPISchemaObjectType)
1447 v.type.check(schema)
1449 def check_clash(self, info, seen):
1450 for v in self.variants:
1451 # Reset seen map for each variant, since qapi names from one
1452 # branch do not affect another branch
1453 assert isinstance(v.type, QAPISchemaObjectType)
1454 v.type.check_clash(info, dict(seen))
1457 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1458 role = 'branch'
1460 def __init__(self, name, typ):
1461 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1464 class QAPISchemaAlternateType(QAPISchemaType):
1465 def __init__(self, name, info, doc, ifcond, variants):
1466 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1467 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1468 assert variants.tag_member
1469 variants.set_owner(name)
1470 variants.tag_member.set_owner(self.name)
1471 self.variants = variants
1473 def check(self, schema):
1474 QAPISchemaType.check(self, schema)
1475 self.variants.tag_member.check(schema)
1476 # Not calling self.variants.check_clash(), because there's nothing
1477 # to clash with
1478 self.variants.check(schema, {})
1479 # Alternate branch names have no relation to the tag enum values;
1480 # so we have to check for potential name collisions ourselves.
1481 seen = {}
1482 for v in self.variants.variants:
1483 v.check_clash(self.info, seen)
1484 if self.doc:
1485 self.doc.connect_member(v)
1486 if self.doc:
1487 self.doc.check()
1489 def c_type(self):
1490 return c_name(self.name) + pointer_suffix
1492 def json_type(self):
1493 return 'value'
1495 def visit(self, visitor):
1496 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1497 self.variants)
1499 def is_empty(self):
1500 return False
1503 class QAPISchemaCommand(QAPISchemaEntity):
1504 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1505 gen, success_response, boxed, allow_oob, allow_preconfig):
1506 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1507 assert not arg_type or isinstance(arg_type, str)
1508 assert not ret_type or isinstance(ret_type, str)
1509 self._arg_type_name = arg_type
1510 self.arg_type = None
1511 self._ret_type_name = ret_type
1512 self.ret_type = None
1513 self.gen = gen
1514 self.success_response = success_response
1515 self.boxed = boxed
1516 self.allow_oob = allow_oob
1517 self.allow_preconfig = allow_preconfig
1519 def check(self, schema):
1520 QAPISchemaEntity.check(self, schema)
1521 if self._arg_type_name:
1522 self.arg_type = schema.lookup_type(self._arg_type_name)
1523 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1524 isinstance(self.arg_type, QAPISchemaAlternateType))
1525 self.arg_type.check(schema)
1526 if self.boxed:
1527 if self.arg_type.is_empty():
1528 raise QAPISemError(self.info,
1529 "Cannot use 'boxed' with empty type")
1530 else:
1531 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1532 assert not self.arg_type.variants
1533 elif self.boxed:
1534 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1535 if self._ret_type_name:
1536 self.ret_type = schema.lookup_type(self._ret_type_name)
1537 assert isinstance(self.ret_type, QAPISchemaType)
1539 def visit(self, visitor):
1540 visitor.visit_command(self.name, self.info, self.ifcond,
1541 self.arg_type, self.ret_type,
1542 self.gen, self.success_response,
1543 self.boxed, self.allow_oob,
1544 self.allow_preconfig)
1547 class QAPISchemaEvent(QAPISchemaEntity):
1548 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1549 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1550 assert not arg_type or isinstance(arg_type, str)
1551 self._arg_type_name = arg_type
1552 self.arg_type = None
1553 self.boxed = boxed
1555 def check(self, schema):
1556 QAPISchemaEntity.check(self, schema)
1557 if self._arg_type_name:
1558 self.arg_type = schema.lookup_type(self._arg_type_name)
1559 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1560 isinstance(self.arg_type, QAPISchemaAlternateType))
1561 self.arg_type.check(schema)
1562 if self.boxed:
1563 if self.arg_type.is_empty():
1564 raise QAPISemError(self.info,
1565 "Cannot use 'boxed' with empty type")
1566 else:
1567 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1568 assert not self.arg_type.variants
1569 elif self.boxed:
1570 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1572 def visit(self, visitor):
1573 visitor.visit_event(self.name, self.info, self.ifcond,
1574 self.arg_type, self.boxed)
1577 class QAPISchema(object):
1578 def __init__(self, fname):
1579 self._fname = fname
1580 if sys.version_info[0] >= 3:
1581 f = open(fname, 'r', encoding='utf-8')
1582 else:
1583 f = open(fname, 'r')
1584 parser = QAPISchemaParser(f)
1585 exprs = check_exprs(parser.exprs)
1586 self.docs = parser.docs
1587 self._entity_list = []
1588 self._entity_dict = {}
1589 self._predefining = True
1590 self._def_predefineds()
1591 self._predefining = False
1592 self._def_exprs(exprs)
1593 self.check()
1595 def _def_entity(self, ent):
1596 # Only the predefined types are allowed to not have info
1597 assert ent.info or self._predefining
1598 assert ent.name is None or ent.name not in self._entity_dict
1599 self._entity_list.append(ent)
1600 if ent.name is not None:
1601 self._entity_dict[ent.name] = ent
1602 if ent.info:
1603 ent.module = os.path.relpath(ent.info['file'],
1604 os.path.dirname(self._fname))
1606 def lookup_entity(self, name, typ=None):
1607 ent = self._entity_dict.get(name)
1608 if typ and not isinstance(ent, typ):
1609 return None
1610 return ent
1612 def lookup_type(self, name):
1613 return self.lookup_entity(name, QAPISchemaType)
1615 def _def_include(self, expr, info, doc):
1616 include = expr['include']
1617 assert doc is None
1618 main_info = info
1619 while main_info['parent']:
1620 main_info = main_info['parent']
1621 fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1622 self._def_entity(QAPISchemaInclude(fname, info))
1624 def _def_builtin_type(self, name, json_type, c_type):
1625 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1626 # Instantiating only the arrays that are actually used would
1627 # be nice, but we can't as long as their generated code
1628 # (qapi-builtin-types.[ch]) may be shared by some other
1629 # schema.
1630 self._make_array_type(name, None)
1632 def _def_predefineds(self):
1633 for t in [('str', 'string', 'char' + pointer_suffix),
1634 ('number', 'number', 'double'),
1635 ('int', 'int', 'int64_t'),
1636 ('int8', 'int', 'int8_t'),
1637 ('int16', 'int', 'int16_t'),
1638 ('int32', 'int', 'int32_t'),
1639 ('int64', 'int', 'int64_t'),
1640 ('uint8', 'int', 'uint8_t'),
1641 ('uint16', 'int', 'uint16_t'),
1642 ('uint32', 'int', 'uint32_t'),
1643 ('uint64', 'int', 'uint64_t'),
1644 ('size', 'int', 'uint64_t'),
1645 ('bool', 'boolean', 'bool'),
1646 ('any', 'value', 'QObject' + pointer_suffix),
1647 ('null', 'null', 'QNull' + pointer_suffix)]:
1648 self._def_builtin_type(*t)
1649 self.the_empty_object_type = QAPISchemaObjectType(
1650 'q_empty', None, None, None, None, [], None)
1651 self._def_entity(self.the_empty_object_type)
1653 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1654 'qbool']
1655 qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
1657 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1658 qtype_values, 'QTYPE'))
1660 def _make_enum_members(self, values):
1661 return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
1663 def _make_implicit_enum_type(self, name, info, ifcond, values):
1664 # See also QAPISchemaObjectTypeMember._pretty_owner()
1665 name = name + 'Kind' # Use namespace reserved by add_name()
1666 self._def_entity(QAPISchemaEnumType(
1667 name, info, None, ifcond, self._make_enum_members(values), None))
1668 return name
1670 def _make_array_type(self, element_type, info):
1671 name = element_type + 'List' # Use namespace reserved by add_name()
1672 if not self.lookup_type(name):
1673 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1674 return name
1676 def _make_implicit_object_type(self, name, info, doc, ifcond,
1677 role, members):
1678 if not members:
1679 return None
1680 # See also QAPISchemaObjectTypeMember._pretty_owner()
1681 name = 'q_obj_%s-%s' % (name, role)
1682 typ = self.lookup_entity(name, QAPISchemaObjectType)
1683 if typ:
1684 # The implicit object type has multiple users. This can
1685 # happen only for simple unions' implicit wrapper types.
1686 # Its ifcond should be the disjunction of its user's
1687 # ifconds. Not implemented. Instead, we always pass the
1688 # wrapped type's ifcond, which is trivially the same for all
1689 # users. It's also necessary for the wrapper to compile.
1690 # But it's not tight: the disjunction need not imply it. We
1691 # may end up compiling useless wrapper types.
1692 # TODO kill simple unions or implement the disjunction
1693 assert ifcond == typ._ifcond # pylint: disable=protected-access
1694 else:
1695 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1696 None, members, None))
1697 return name
1699 def _def_enum_type(self, expr, info, doc):
1700 name = expr['enum']
1701 data = expr['data']
1702 prefix = expr.get('prefix')
1703 ifcond = expr.get('if')
1704 self._def_entity(QAPISchemaEnumType(
1705 name, info, doc, ifcond,
1706 self._make_enum_members(data), prefix))
1708 def _make_member(self, name, typ, info):
1709 optional = False
1710 if name.startswith('*'):
1711 name = name[1:]
1712 optional = True
1713 if isinstance(typ, list):
1714 assert len(typ) == 1
1715 typ = self._make_array_type(typ[0], info)
1716 return QAPISchemaObjectTypeMember(name, typ, optional)
1718 def _make_members(self, data, info):
1719 return [self._make_member(key, value, info)
1720 for (key, value) in data.items()]
1722 def _def_struct_type(self, expr, info, doc):
1723 name = expr['struct']
1724 base = expr.get('base')
1725 data = expr['data']
1726 ifcond = expr.get('if')
1727 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1728 self._make_members(data, info),
1729 None))
1731 def _make_variant(self, case, typ):
1732 return QAPISchemaObjectTypeVariant(case, typ)
1734 def _make_simple_variant(self, case, typ, info):
1735 if isinstance(typ, list):
1736 assert len(typ) == 1
1737 typ = self._make_array_type(typ[0], info)
1738 typ = self._make_implicit_object_type(
1739 typ, info, None, self.lookup_type(typ),
1740 'wrapper', [self._make_member('data', typ, info)])
1741 return QAPISchemaObjectTypeVariant(case, typ)
1743 def _def_union_type(self, expr, info, doc):
1744 name = expr['union']
1745 data = expr['data']
1746 base = expr.get('base')
1747 ifcond = expr.get('if')
1748 tag_name = expr.get('discriminator')
1749 tag_member = None
1750 if isinstance(base, dict):
1751 base = self._make_implicit_object_type(
1752 name, info, doc, ifcond,
1753 'base', self._make_members(base, info))
1754 if tag_name:
1755 variants = [self._make_variant(key, value)
1756 for (key, value) in data.items()]
1757 members = []
1758 else:
1759 variants = [self._make_simple_variant(key, value, info)
1760 for (key, value) in data.items()]
1761 enum = [{'name': v.name} for v in variants]
1762 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1763 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1764 members = [tag_member]
1765 self._def_entity(
1766 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1767 QAPISchemaObjectTypeVariants(tag_name,
1768 tag_member,
1769 variants)))
1771 def _def_alternate_type(self, expr, info, doc):
1772 name = expr['alternate']
1773 data = expr['data']
1774 ifcond = expr.get('if')
1775 variants = [self._make_variant(key, value)
1776 for (key, value) in data.items()]
1777 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1778 self._def_entity(
1779 QAPISchemaAlternateType(name, info, doc, ifcond,
1780 QAPISchemaObjectTypeVariants(None,
1781 tag_member,
1782 variants)))
1784 def _def_command(self, expr, info, doc):
1785 name = expr['command']
1786 data = expr.get('data')
1787 rets = expr.get('returns')
1788 gen = expr.get('gen', True)
1789 success_response = expr.get('success-response', True)
1790 boxed = expr.get('boxed', False)
1791 allow_oob = expr.get('allow-oob', False)
1792 allow_preconfig = expr.get('allow-preconfig', False)
1793 ifcond = expr.get('if')
1794 if isinstance(data, OrderedDict):
1795 data = self._make_implicit_object_type(
1796 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1797 if isinstance(rets, list):
1798 assert len(rets) == 1
1799 rets = self._make_array_type(rets[0], info)
1800 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1801 gen, success_response,
1802 boxed, allow_oob, allow_preconfig))
1804 def _def_event(self, expr, info, doc):
1805 name = expr['event']
1806 data = expr.get('data')
1807 boxed = expr.get('boxed', False)
1808 ifcond = expr.get('if')
1809 if isinstance(data, OrderedDict):
1810 data = self._make_implicit_object_type(
1811 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1812 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1814 def _def_exprs(self, exprs):
1815 for expr_elem in exprs:
1816 expr = expr_elem['expr']
1817 info = expr_elem['info']
1818 doc = expr_elem.get('doc')
1819 if 'enum' in expr:
1820 self._def_enum_type(expr, info, doc)
1821 elif 'struct' in expr:
1822 self._def_struct_type(expr, info, doc)
1823 elif 'union' in expr:
1824 self._def_union_type(expr, info, doc)
1825 elif 'alternate' in expr:
1826 self._def_alternate_type(expr, info, doc)
1827 elif 'command' in expr:
1828 self._def_command(expr, info, doc)
1829 elif 'event' in expr:
1830 self._def_event(expr, info, doc)
1831 elif 'include' in expr:
1832 self._def_include(expr, info, doc)
1833 else:
1834 assert False
1836 def check(self):
1837 for ent in self._entity_list:
1838 ent.check(self)
1840 def visit(self, visitor):
1841 visitor.visit_begin(self)
1842 module = None
1843 for entity in self._entity_list:
1844 if visitor.visit_needed(entity):
1845 if entity.module != module:
1846 module = entity.module
1847 visitor.visit_module(module)
1848 entity.visit(visitor)
1849 visitor.visit_end()
1853 # Code generation helpers
1856 def camel_case(name):
1857 new_name = ''
1858 first = True
1859 for ch in name:
1860 if ch in ['_', '-']:
1861 first = True
1862 elif first:
1863 new_name += ch.upper()
1864 first = False
1865 else:
1866 new_name += ch.lower()
1867 return new_name
1870 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1871 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1872 # ENUM24_Name -> ENUM24_NAME
1873 def camel_to_upper(value):
1874 c_fun_str = c_name(value, False)
1875 if value.isupper():
1876 return c_fun_str
1878 new_name = ''
1879 length = len(c_fun_str)
1880 for i in range(length):
1881 c = c_fun_str[i]
1882 # When c is upper and no '_' appears before, do more checks
1883 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1884 if i < length - 1 and c_fun_str[i + 1].islower():
1885 new_name += '_'
1886 elif c_fun_str[i - 1].isdigit():
1887 new_name += '_'
1888 new_name += c
1889 return new_name.lstrip('_').upper()
1892 def c_enum_const(type_name, const_name, prefix=None):
1893 if prefix is not None:
1894 type_name = prefix
1895 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1898 if hasattr(str, 'maketrans'):
1899 c_name_trans = str.maketrans('.-', '__')
1900 else:
1901 c_name_trans = string.maketrans('.-', '__')
1904 # Map @name to a valid C identifier.
1905 # If @protect, avoid returning certain ticklish identifiers (like
1906 # C keywords) by prepending 'q_'.
1908 # Used for converting 'name' from a 'name':'type' qapi definition
1909 # into a generated struct member, as well as converting type names
1910 # into substrings of a generated C function name.
1911 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1912 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1913 def c_name(name, protect=True):
1914 # ANSI X3J11/88-090, 3.1.1
1915 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1916 'default', 'do', 'double', 'else', 'enum', 'extern',
1917 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1918 'return', 'short', 'signed', 'sizeof', 'static',
1919 'struct', 'switch', 'typedef', 'union', 'unsigned',
1920 'void', 'volatile', 'while'])
1921 # ISO/IEC 9899:1999, 6.4.1
1922 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1923 # ISO/IEC 9899:2011, 6.4.1
1924 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1925 '_Noreturn', '_Static_assert', '_Thread_local'])
1926 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1927 # excluding _.*
1928 gcc_words = set(['asm', 'typeof'])
1929 # C++ ISO/IEC 14882:2003 2.11
1930 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1931 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1932 'namespace', 'new', 'operator', 'private', 'protected',
1933 'public', 'reinterpret_cast', 'static_cast', 'template',
1934 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1935 'using', 'virtual', 'wchar_t',
1936 # alternative representations
1937 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1938 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1939 # namespace pollution:
1940 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1941 name = name.translate(c_name_trans)
1942 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1943 | cpp_words | polluted_words):
1944 return 'q_' + name
1945 return name
1948 eatspace = '\033EATSPACE.'
1949 pointer_suffix = ' *' + eatspace
1952 def genindent(count):
1953 ret = ''
1954 for _ in range(count):
1955 ret += ' '
1956 return ret
1959 indent_level = 0
1962 def push_indent(indent_amount=4):
1963 global indent_level
1964 indent_level += indent_amount
1967 def pop_indent(indent_amount=4):
1968 global indent_level
1969 indent_level -= indent_amount
1972 # Generate @code with @kwds interpolated.
1973 # Obey indent_level, and strip eatspace.
1974 def cgen(code, **kwds):
1975 raw = code % kwds
1976 if indent_level:
1977 indent = genindent(indent_level)
1978 # re.subn() lacks flags support before Python 2.7, use re.compile()
1979 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
1980 indent, raw)
1981 raw = raw[0]
1982 return re.sub(re.escape(eatspace) + r' *', '', raw)
1985 def mcgen(code, **kwds):
1986 if code[0] == '\n':
1987 code = code[1:]
1988 return cgen(code, **kwds)
1991 def guardname(filename):
1992 return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
1995 def guardstart(name):
1996 return mcgen('''
1997 #ifndef %(name)s
1998 #define %(name)s
2000 ''',
2001 name=guardname(name))
2004 def guardend(name):
2005 return mcgen('''
2007 #endif /* %(name)s */
2008 ''',
2009 name=guardname(name))
2012 def gen_if(ifcond):
2013 ret = ''
2014 for ifc in ifcond:
2015 ret += mcgen('''
2016 #if %(cond)s
2017 ''', cond=ifc)
2018 return ret
2021 def gen_endif(ifcond):
2022 ret = ''
2023 for ifc in reversed(ifcond):
2024 ret += mcgen('''
2025 #endif /* %(cond)s */
2026 ''', cond=ifc)
2027 return ret
2030 def _wrap_ifcond(ifcond, before, after):
2031 if before == after:
2032 return after # suppress empty #if ... #endif
2034 assert after.startswith(before)
2035 out = before
2036 added = after[len(before):]
2037 if added[0] == '\n':
2038 out += '\n'
2039 added = added[1:]
2040 out += gen_if(ifcond)
2041 out += added
2042 out += gen_endif(ifcond)
2043 return out
2046 def gen_enum_lookup(name, members, prefix=None):
2047 ret = mcgen('''
2049 const QEnumLookup %(c_name)s_lookup = {
2050 .array = (const char *const[]) {
2051 ''',
2052 c_name=c_name(name))
2053 for m in members:
2054 index = c_enum_const(name, m.name, prefix)
2055 ret += mcgen('''
2056 [%(index)s] = "%(name)s",
2057 ''',
2058 index=index, name=m.name)
2060 ret += mcgen('''
2062 .size = %(max_index)s
2064 ''',
2065 max_index=c_enum_const(name, '_MAX', prefix))
2066 return ret
2069 def gen_enum(name, members, prefix=None):
2070 # append automatically generated _MAX value
2071 enum_members = members + [QAPISchemaMember('_MAX')]
2073 ret = mcgen('''
2075 typedef enum %(c_name)s {
2076 ''',
2077 c_name=c_name(name))
2079 for m in enum_members:
2080 ret += mcgen('''
2081 %(c_enum)s,
2082 ''',
2083 c_enum=c_enum_const(name, m.name, prefix))
2085 ret += mcgen('''
2086 } %(c_name)s;
2087 ''',
2088 c_name=c_name(name))
2090 ret += mcgen('''
2092 #define %(c_name)s_str(val) \\
2093 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2095 extern const QEnumLookup %(c_name)s_lookup;
2096 ''',
2097 c_name=c_name(name))
2098 return ret
2101 def build_params(arg_type, boxed, extra=None):
2102 ret = ''
2103 sep = ''
2104 if boxed:
2105 assert arg_type
2106 ret += '%s arg' % arg_type.c_param_type()
2107 sep = ', '
2108 elif arg_type:
2109 assert not arg_type.variants
2110 for memb in arg_type.members:
2111 ret += sep
2112 sep = ', '
2113 if memb.optional:
2114 ret += 'bool has_%s, ' % c_name(memb.name)
2115 ret += '%s %s' % (memb.type.c_param_type(),
2116 c_name(memb.name))
2117 if extra:
2118 ret += sep + extra
2119 return ret if ret else 'void'
2123 # Accumulate and write output
2126 class QAPIGen(object):
2128 def __init__(self):
2129 self._preamble = ''
2130 self._body = ''
2132 def preamble_add(self, text):
2133 self._preamble += text
2135 def add(self, text):
2136 self._body += text
2138 def get_content(self, fname=None):
2139 return (self._top(fname) + self._preamble + self._body
2140 + self._bottom(fname))
2142 def _top(self, fname):
2143 return ''
2145 def _bottom(self, fname):
2146 return ''
2148 def write(self, output_dir, fname):
2149 pathname = os.path.join(output_dir, fname)
2150 dir = os.path.dirname(pathname)
2151 if dir:
2152 try:
2153 os.makedirs(dir)
2154 except os.error as e:
2155 if e.errno != errno.EEXIST:
2156 raise
2157 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2158 if sys.version_info[0] >= 3:
2159 f = open(fd, 'r+', encoding='utf-8')
2160 else:
2161 f = os.fdopen(fd, 'r+')
2162 text = self.get_content(fname)
2163 oldtext = f.read(len(text) + 1)
2164 if text != oldtext:
2165 f.seek(0)
2166 f.truncate(0)
2167 f.write(text)
2168 f.close()
2171 @contextmanager
2172 def ifcontext(ifcond, *args):
2173 """A 'with' statement context manager to wrap with start_if()/end_if()
2175 *args: any number of QAPIGenCCode
2177 Example::
2179 with ifcontext(ifcond, self._genh, self._genc):
2180 modify self._genh and self._genc ...
2182 Is equivalent to calling::
2184 self._genh.start_if(ifcond)
2185 self._genc.start_if(ifcond)
2186 modify self._genh and self._genc ...
2187 self._genh.end_if()
2188 self._genc.end_if()
2190 for arg in args:
2191 arg.start_if(ifcond)
2192 yield
2193 for arg in args:
2194 arg.end_if()
2197 class QAPIGenCCode(QAPIGen):
2199 def __init__(self):
2200 QAPIGen.__init__(self)
2201 self._start_if = None
2203 def start_if(self, ifcond):
2204 assert self._start_if is None
2205 self._start_if = (ifcond, self._body, self._preamble)
2207 def end_if(self):
2208 assert self._start_if
2209 self._wrap_ifcond()
2210 self._start_if = None
2212 def _wrap_ifcond(self):
2213 self._body = _wrap_ifcond(self._start_if[0],
2214 self._start_if[1], self._body)
2215 self._preamble = _wrap_ifcond(self._start_if[0],
2216 self._start_if[2], self._preamble)
2218 def get_content(self, fname=None):
2219 assert self._start_if is None
2220 return QAPIGen.get_content(self, fname)
2223 class QAPIGenC(QAPIGenCCode):
2225 def __init__(self, blurb, pydoc):
2226 QAPIGenCCode.__init__(self)
2227 self._blurb = blurb
2228 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2229 re.MULTILINE))
2231 def _top(self, fname):
2232 return mcgen('''
2233 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2236 %(blurb)s
2238 * %(copyright)s
2240 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2241 * See the COPYING.LIB file in the top-level directory.
2244 ''',
2245 blurb=self._blurb, copyright=self._copyright)
2247 def _bottom(self, fname):
2248 return mcgen('''
2250 /* Dummy declaration to prevent empty .o file */
2251 char dummy_%(name)s;
2252 ''',
2253 name=c_name(fname))
2256 class QAPIGenH(QAPIGenC):
2258 def _top(self, fname):
2259 return QAPIGenC._top(self, fname) + guardstart(fname)
2261 def _bottom(self, fname):
2262 return guardend(fname)
2265 class QAPIGenDoc(QAPIGen):
2267 def _top(self, fname):
2268 return (QAPIGen._top(self, fname)
2269 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2272 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2274 def __init__(self, prefix, what, blurb, pydoc):
2275 self._prefix = prefix
2276 self._what = what
2277 self._genc = QAPIGenC(blurb, pydoc)
2278 self._genh = QAPIGenH(blurb, pydoc)
2280 def write(self, output_dir):
2281 self._genc.write(output_dir, self._prefix + self._what + '.c')
2282 self._genh.write(output_dir, self._prefix + self._what + '.h')
2285 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2287 def __init__(self, prefix, what, blurb, pydoc):
2288 self._prefix = prefix
2289 self._what = what
2290 self._blurb = blurb
2291 self._pydoc = pydoc
2292 self._module = {}
2293 self._main_module = None
2295 def _module_basename(self, what, name):
2296 if name is None:
2297 return re.sub(r'-', '-builtin-', what)
2298 basename = os.path.join(os.path.dirname(name),
2299 self._prefix + what)
2300 if name == self._main_module:
2301 return basename
2302 return basename + '-' + os.path.splitext(os.path.basename(name))[0]
2304 def _add_module(self, name, blurb):
2305 if self._main_module is None and name is not None:
2306 self._main_module = name
2307 genc = QAPIGenC(blurb, self._pydoc)
2308 genh = QAPIGenH(blurb, self._pydoc)
2309 self._module[name] = (genc, genh)
2310 self._set_module(name)
2312 def _set_module(self, name):
2313 self._genc, self._genh = self._module[name]
2315 def write(self, output_dir, opt_builtins=False):
2316 for name in self._module:
2317 if name is None and not opt_builtins:
2318 continue
2319 basename = self._module_basename(self._what, name)
2320 (genc, genh) = self._module[name]
2321 genc.write(output_dir, basename + '.c')
2322 genh.write(output_dir, basename + '.h')
2324 def _begin_module(self, name):
2325 pass
2327 def visit_module(self, name):
2328 if name in self._module:
2329 self._set_module(name)
2330 return
2331 self._add_module(name, self._blurb)
2332 self._begin_module(name)
2334 def visit_include(self, name, info):
2335 basename = self._module_basename(self._what, name)
2336 self._genh.preamble_add(mcgen('''
2337 #include "%(basename)s.h"
2338 ''',
2339 basename=basename))