qapi: Move gen_enum(), gen_enum_lookup() back to qapi/types.py
[qemu/kevin.git] / scripts / qapi / common.py
blob306857f0c061a738aa0959a4fcacc99b4f8bfd5a
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 copy
17 import errno
18 import os
19 import re
20 import string
21 import sys
22 from collections import OrderedDict
26 # Parsing the schema into expressions
30 class QAPISchemaPragma(object):
31 def __init__(self):
32 # Are documentation comments required?
33 self.doc_required = False
34 # Whitelist of commands allowed to return a non-dictionary
35 self.returns_whitelist = []
36 # Whitelist of entities allowed to violate case conventions
37 self.name_case_whitelist = []
40 class QAPISourceInfo(object):
41 def __init__(self, fname, line, parent):
42 self.fname = fname
43 self.line = line
44 self.parent = parent
45 self.pragma = parent.pragma if parent else QAPISchemaPragma()
46 self.defn_meta = None
47 self.defn_name = None
49 def set_defn(self, meta, name):
50 self.defn_meta = meta
51 self.defn_name = name
53 def next_line(self):
54 info = copy.copy(self)
55 info.line += 1
56 return info
58 def loc(self):
59 if self.fname is None:
60 return sys.argv[0]
61 ret = self.fname
62 if self.line is not None:
63 ret += ':%d' % self.line
64 return ret
66 def in_defn(self):
67 if self.defn_name:
68 return "%s: In %s '%s':\n" % (self.fname,
69 self.defn_meta, self.defn_name)
70 return ''
72 def include_path(self):
73 ret = ''
74 parent = self.parent
75 while parent:
76 ret = 'In file included from %s:\n' % parent.loc() + ret
77 parent = parent.parent
78 return ret
80 def __str__(self):
81 return self.include_path() + self.in_defn() + self.loc()
84 class QAPIError(Exception):
85 def __init__(self, info, col, msg):
86 Exception.__init__(self)
87 self.info = info
88 self.col = col
89 self.msg = msg
91 def __str__(self):
92 loc = str(self.info)
93 if self.col is not None:
94 assert self.info.line is not None
95 loc += ':%s' % self.col
96 return loc + ': ' + self.msg
99 class QAPIParseError(QAPIError):
100 def __init__(self, parser, msg):
101 col = 1
102 for ch in parser.src[parser.line_pos:parser.pos]:
103 if ch == '\t':
104 col = (col + 7) % 8 + 1
105 else:
106 col += 1
107 QAPIError.__init__(self, parser.info, col, msg)
110 class QAPISemError(QAPIError):
111 def __init__(self, info, msg):
112 QAPIError.__init__(self, info, None, msg)
115 class QAPIDoc(object):
117 A documentation comment block, either definition or free-form
119 Definition documentation blocks consist of
121 * a body section: one line naming the definition, followed by an
122 overview (any number of lines)
124 * argument sections: a description of each argument (for commands
125 and events) or member (for structs, unions and alternates)
127 * features sections: a description of each feature flag
129 * additional (non-argument) sections, possibly tagged
131 Free-form documentation blocks consist only of a body section.
134 class Section(object):
135 def __init__(self, name=None):
136 # optional section name (argument/member or section name)
137 self.name = name
138 # the list of lines for this section
139 self.text = ''
141 def append(self, line):
142 self.text += line.rstrip() + '\n'
144 class ArgSection(Section):
145 def __init__(self, name):
146 QAPIDoc.Section.__init__(self, name)
147 self.member = None
149 def connect(self, member):
150 self.member = member
152 def __init__(self, parser, info):
153 # self._parser is used to report errors with QAPIParseError. The
154 # resulting error position depends on the state of the parser.
155 # It happens to be the beginning of the comment. More or less
156 # servicable, but action at a distance.
157 self._parser = parser
158 self.info = info
159 self.symbol = None
160 self.body = QAPIDoc.Section()
161 # dict mapping parameter name to ArgSection
162 self.args = OrderedDict()
163 self.features = OrderedDict()
164 # a list of Section
165 self.sections = []
166 # the current section
167 self._section = self.body
168 self._append_line = self._append_body_line
170 def has_section(self, name):
171 """Return True if we have a section with this name."""
172 for i in self.sections:
173 if i.name == name:
174 return True
175 return False
177 def append(self, line):
179 Parse a comment line and add it to the documentation.
181 The way that the line is dealt with depends on which part of
182 the documentation we're parsing right now:
183 * The body section: ._append_line is ._append_body_line
184 * An argument section: ._append_line is ._append_args_line
185 * A features section: ._append_line is ._append_features_line
186 * An additional section: ._append_line is ._append_various_line
188 line = line[1:]
189 if not line:
190 self._append_freeform(line)
191 return
193 if line[0] != ' ':
194 raise QAPIParseError(self._parser, "missing space after #")
195 line = line[1:]
196 self._append_line(line)
198 def end_comment(self):
199 self._end_section()
201 @staticmethod
202 def _is_section_tag(name):
203 return name in ('Returns:', 'Since:',
204 # those are often singular or plural
205 'Note:', 'Notes:',
206 'Example:', 'Examples:',
207 'TODO:')
209 def _append_body_line(self, line):
211 Process a line of documentation text in the body section.
213 If this a symbol line and it is the section's first line, this
214 is a definition documentation block for that symbol.
216 If it's a definition documentation block, another symbol line
217 begins the argument section for the argument named by it, and
218 a section tag begins an additional section. Start that
219 section and append the line to it.
221 Else, append the line to the current section.
223 name = line.split(' ', 1)[0]
224 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
225 # recognized, and get silently treated as ordinary text
226 if not self.symbol and not self.body.text and line.startswith('@'):
227 if not line.endswith(':'):
228 raise QAPIParseError(self._parser, "line should end with ':'")
229 self.symbol = line[1:-1]
230 # FIXME invalid names other than the empty string aren't flagged
231 if not self.symbol:
232 raise QAPIParseError(self._parser, "invalid name")
233 elif self.symbol:
234 # This is a definition documentation block
235 if name.startswith('@') and name.endswith(':'):
236 self._append_line = self._append_args_line
237 self._append_args_line(line)
238 elif line == 'Features:':
239 self._append_line = self._append_features_line
240 elif self._is_section_tag(name):
241 self._append_line = self._append_various_line
242 self._append_various_line(line)
243 else:
244 self._append_freeform(line.strip())
245 else:
246 # This is a free-form documentation block
247 self._append_freeform(line.strip())
249 def _append_args_line(self, line):
251 Process a line of documentation text in an argument section.
253 A symbol line begins the next argument section, a section tag
254 section or a non-indented line after a blank line begins an
255 additional section. Start that section and append the line to
258 Else, append the line to the current section.
261 name = line.split(' ', 1)[0]
263 if name.startswith('@') and name.endswith(':'):
264 line = line[len(name)+1:]
265 self._start_args_section(name[1:-1])
266 elif self._is_section_tag(name):
267 self._append_line = self._append_various_line
268 self._append_various_line(line)
269 return
270 elif (self._section.text.endswith('\n\n')
271 and line and not line[0].isspace()):
272 if line == 'Features:':
273 self._append_line = self._append_features_line
274 else:
275 self._start_section()
276 self._append_line = self._append_various_line
277 self._append_various_line(line)
278 return
280 self._append_freeform(line.strip())
282 def _append_features_line(self, line):
283 name = line.split(' ', 1)[0]
285 if name.startswith('@') and name.endswith(':'):
286 line = line[len(name)+1:]
287 self._start_features_section(name[1:-1])
288 elif self._is_section_tag(name):
289 self._append_line = self._append_various_line
290 self._append_various_line(line)
291 return
292 elif (self._section.text.endswith('\n\n')
293 and line and not line[0].isspace()):
294 self._start_section()
295 self._append_line = self._append_various_line
296 self._append_various_line(line)
297 return
299 self._append_freeform(line.strip())
301 def _append_various_line(self, line):
303 Process a line of documentation text in an additional section.
305 A symbol line is an error.
307 A section tag begins an additional section. Start that
308 section and append the line to it.
310 Else, append the line to the current section.
312 name = line.split(' ', 1)[0]
314 if name.startswith('@') and name.endswith(':'):
315 raise QAPIParseError(self._parser,
316 "'%s' can't follow '%s' section"
317 % (name, self.sections[0].name))
318 elif self._is_section_tag(name):
319 line = line[len(name)+1:]
320 self._start_section(name[:-1])
322 if (not self._section.name or
323 not self._section.name.startswith('Example')):
324 line = line.strip()
326 self._append_freeform(line)
328 def _start_symbol_section(self, symbols_dict, name):
329 # FIXME invalid names other than the empty string aren't flagged
330 if not name:
331 raise QAPIParseError(self._parser, "invalid parameter name")
332 if name in symbols_dict:
333 raise QAPIParseError(self._parser,
334 "'%s' parameter name duplicated" % name)
335 assert not self.sections
336 self._end_section()
337 self._section = QAPIDoc.ArgSection(name)
338 symbols_dict[name] = self._section
340 def _start_args_section(self, name):
341 self._start_symbol_section(self.args, name)
343 def _start_features_section(self, name):
344 self._start_symbol_section(self.features, name)
346 def _start_section(self, name=None):
347 if name in ('Returns', 'Since') and self.has_section(name):
348 raise QAPIParseError(self._parser,
349 "duplicated '%s' section" % name)
350 self._end_section()
351 self._section = QAPIDoc.Section(name)
352 self.sections.append(self._section)
354 def _end_section(self):
355 if self._section:
356 text = self._section.text = self._section.text.strip()
357 if self._section.name and (not text or text.isspace()):
358 raise QAPIParseError(
359 self._parser,
360 "empty doc section '%s'" % self._section.name)
361 self._section = None
363 def _append_freeform(self, line):
364 match = re.match(r'(@\S+:)', line)
365 if match:
366 raise QAPIParseError(self._parser,
367 "'%s' not allowed in free-form documentation"
368 % match.group(1))
369 self._section.append(line)
371 def connect_member(self, member):
372 if member.name not in self.args:
373 # Undocumented TODO outlaw
374 self.args[member.name] = QAPIDoc.ArgSection(member.name)
375 self.args[member.name].connect(member)
377 def check_expr(self, expr):
378 if self.has_section('Returns') and 'command' not in expr:
379 raise QAPISemError(self.info,
380 "'Returns:' is only valid for commands")
382 def check(self):
383 bogus = [name for name, section in self.args.items()
384 if not section.member]
385 if bogus:
386 raise QAPISemError(
387 self.info,
388 "the following documented members are not in "
389 "the declaration: %s" % ", ".join(bogus))
392 class QAPISchemaParser(object):
394 def __init__(self, fname, previously_included=None, incl_info=None):
395 previously_included = previously_included or set()
396 previously_included.add(os.path.abspath(fname))
398 try:
399 if sys.version_info[0] >= 3:
400 fp = open(fname, 'r', encoding='utf-8')
401 else:
402 fp = open(fname, 'r')
403 self.src = fp.read()
404 except IOError as e:
405 raise QAPISemError(incl_info or QAPISourceInfo(None, None, None),
406 "can't read %s file '%s': %s"
407 % ("include" if incl_info else "schema",
408 fname,
409 e.strerror))
411 if self.src == '' or self.src[-1] != '\n':
412 self.src += '\n'
413 self.cursor = 0
414 self.info = QAPISourceInfo(fname, 1, incl_info)
415 self.line_pos = 0
416 self.exprs = []
417 self.docs = []
418 self.accept()
419 cur_doc = None
421 while self.tok is not None:
422 info = self.info
423 if self.tok == '#':
424 self.reject_expr_doc(cur_doc)
425 cur_doc = self.get_doc(info)
426 self.docs.append(cur_doc)
427 continue
429 expr = self.get_expr(False)
430 if 'include' in expr:
431 self.reject_expr_doc(cur_doc)
432 if len(expr) != 1:
433 raise QAPISemError(info, "invalid 'include' directive")
434 include = expr['include']
435 if not isinstance(include, str):
436 raise QAPISemError(info,
437 "value of 'include' must be a string")
438 incl_fname = os.path.join(os.path.dirname(fname),
439 include)
440 self.exprs.append({'expr': {'include': incl_fname},
441 'info': info})
442 exprs_include = self._include(include, info, incl_fname,
443 previously_included)
444 if exprs_include:
445 self.exprs.extend(exprs_include.exprs)
446 self.docs.extend(exprs_include.docs)
447 elif "pragma" in expr:
448 self.reject_expr_doc(cur_doc)
449 if len(expr) != 1:
450 raise QAPISemError(info, "invalid 'pragma' directive")
451 pragma = expr['pragma']
452 if not isinstance(pragma, dict):
453 raise QAPISemError(
454 info, "value of 'pragma' must be an object")
455 for name, value in pragma.items():
456 self._pragma(name, value, info)
457 else:
458 expr_elem = {'expr': expr,
459 'info': info}
460 if cur_doc:
461 if not cur_doc.symbol:
462 raise QAPISemError(
463 cur_doc.info, "definition documentation required")
464 expr_elem['doc'] = cur_doc
465 self.exprs.append(expr_elem)
466 cur_doc = None
467 self.reject_expr_doc(cur_doc)
469 @staticmethod
470 def reject_expr_doc(doc):
471 if doc and doc.symbol:
472 raise QAPISemError(
473 doc.info,
474 "documentation for '%s' is not followed by the definition"
475 % doc.symbol)
477 def _include(self, include, info, incl_fname, previously_included):
478 incl_abs_fname = os.path.abspath(incl_fname)
479 # catch inclusion cycle
480 inf = info
481 while inf:
482 if incl_abs_fname == os.path.abspath(inf.fname):
483 raise QAPISemError(info, "inclusion loop for %s" % include)
484 inf = inf.parent
486 # skip multiple include of the same file
487 if incl_abs_fname in previously_included:
488 return None
490 return QAPISchemaParser(incl_fname, previously_included, info)
492 def _pragma(self, name, value, info):
493 if name == 'doc-required':
494 if not isinstance(value, bool):
495 raise QAPISemError(info,
496 "pragma 'doc-required' must be boolean")
497 info.pragma.doc_required = value
498 elif name == 'returns-whitelist':
499 if (not isinstance(value, list)
500 or any([not isinstance(elt, str) for elt in value])):
501 raise QAPISemError(
502 info,
503 "pragma returns-whitelist must be a list of strings")
504 info.pragma.returns_whitelist = value
505 elif name == 'name-case-whitelist':
506 if (not isinstance(value, list)
507 or any([not isinstance(elt, str) for elt in value])):
508 raise QAPISemError(
509 info,
510 "pragma name-case-whitelist must be a list of strings")
511 info.pragma.name_case_whitelist = value
512 else:
513 raise QAPISemError(info, "unknown pragma '%s'" % name)
515 def accept(self, skip_comment=True):
516 while True:
517 self.tok = self.src[self.cursor]
518 self.pos = self.cursor
519 self.cursor += 1
520 self.val = None
522 if self.tok == '#':
523 if self.src[self.cursor] == '#':
524 # Start of doc comment
525 skip_comment = False
526 self.cursor = self.src.find('\n', self.cursor)
527 if not skip_comment:
528 self.val = self.src[self.pos:self.cursor]
529 return
530 elif self.tok in '{}:,[]':
531 return
532 elif self.tok == "'":
533 # Note: we accept only printable ASCII
534 string = ''
535 esc = False
536 while True:
537 ch = self.src[self.cursor]
538 self.cursor += 1
539 if ch == '\n':
540 raise QAPIParseError(self, "missing terminating \"'\"")
541 if esc:
542 # Note: we recognize only \\ because we have
543 # no use for funny characters in strings
544 if ch != '\\':
545 raise QAPIParseError(self,
546 "unknown escape \\%s" % ch)
547 esc = False
548 elif ch == '\\':
549 esc = True
550 continue
551 elif ch == "'":
552 self.val = string
553 return
554 if ord(ch) < 32 or ord(ch) >= 127:
555 raise QAPIParseError(
556 self, "funny character in string")
557 string += ch
558 elif self.src.startswith('true', self.pos):
559 self.val = True
560 self.cursor += 3
561 return
562 elif self.src.startswith('false', self.pos):
563 self.val = False
564 self.cursor += 4
565 return
566 elif self.tok == '\n':
567 if self.cursor == len(self.src):
568 self.tok = None
569 return
570 self.info = self.info.next_line()
571 self.line_pos = self.cursor
572 elif not self.tok.isspace():
573 # Show up to next structural, whitespace or quote
574 # character
575 match = re.match('[^[\\]{}:,\\s\'"]+',
576 self.src[self.cursor-1:])
577 raise QAPIParseError(self, "stray '%s'" % match.group(0))
579 def get_members(self):
580 expr = OrderedDict()
581 if self.tok == '}':
582 self.accept()
583 return expr
584 if self.tok != "'":
585 raise QAPIParseError(self, "expected string or '}'")
586 while True:
587 key = self.val
588 self.accept()
589 if self.tok != ':':
590 raise QAPIParseError(self, "expected ':'")
591 self.accept()
592 if key in expr:
593 raise QAPIParseError(self, "duplicate key '%s'" % key)
594 expr[key] = self.get_expr(True)
595 if self.tok == '}':
596 self.accept()
597 return expr
598 if self.tok != ',':
599 raise QAPIParseError(self, "expected ',' or '}'")
600 self.accept()
601 if self.tok != "'":
602 raise QAPIParseError(self, "expected string")
604 def get_values(self):
605 expr = []
606 if self.tok == ']':
607 self.accept()
608 return expr
609 if self.tok not in "{['tfn":
610 raise QAPIParseError(
611 self, "expected '{', '[', ']', string, boolean or 'null'")
612 while True:
613 expr.append(self.get_expr(True))
614 if self.tok == ']':
615 self.accept()
616 return expr
617 if self.tok != ',':
618 raise QAPIParseError(self, "expected ',' or ']'")
619 self.accept()
621 def get_expr(self, nested):
622 if self.tok != '{' and not nested:
623 raise QAPIParseError(self, "expected '{'")
624 if self.tok == '{':
625 self.accept()
626 expr = self.get_members()
627 elif self.tok == '[':
628 self.accept()
629 expr = self.get_values()
630 elif self.tok in "'tfn":
631 expr = self.val
632 self.accept()
633 else:
634 raise QAPIParseError(
635 self, "expected '{', '[', string, boolean or 'null'")
636 return expr
638 def get_doc(self, info):
639 if self.val != '##':
640 raise QAPIParseError(
641 self, "junk after '##' at start of documentation comment")
643 doc = QAPIDoc(self, info)
644 self.accept(False)
645 while self.tok == '#':
646 if self.val.startswith('##'):
647 # End of doc comment
648 if self.val != '##':
649 raise QAPIParseError(
650 self,
651 "junk after '##' at end of documentation comment")
652 doc.end_comment()
653 self.accept()
654 return doc
655 else:
656 doc.append(self.val)
657 self.accept(False)
659 raise QAPIParseError(self, "documentation comment must end with '##'")
663 # Check (context-free) schema expression structure
666 # Names must be letters, numbers, -, and _. They must start with letter,
667 # except for downstream extensions which must start with __RFQDN_.
668 # Dots are only valid in the downstream extension prefix.
669 valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
670 '[a-zA-Z][a-zA-Z0-9_-]*$')
673 def check_name_is_str(name, info, source):
674 if not isinstance(name, str):
675 raise QAPISemError(info, "%s requires a string name" % source)
678 def check_name_str(name, info, source,
679 allow_optional=False, enum_member=False,
680 permit_upper=False):
681 global valid_name
682 membername = name
684 if allow_optional and name.startswith('*'):
685 membername = name[1:]
686 # Enum members can start with a digit, because the generated C
687 # code always prefixes it with the enum name
688 if enum_member and membername[0].isdigit():
689 membername = 'D' + membername
690 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
691 # and 'q_obj_*' implicit type names.
692 if not valid_name.match(membername) or \
693 c_name(membername, False).startswith('q_'):
694 raise QAPISemError(info, "%s has an invalid name" % source)
695 if not permit_upper and name.lower() != name:
696 raise QAPISemError(
697 info, "%s uses uppercase in name" % source)
698 assert not membername.startswith('*')
701 def check_defn_name_str(name, info, meta):
702 check_name_str(name, info, meta, permit_upper=True)
703 if name.endswith('Kind') or name.endswith('List'):
704 raise QAPISemError(
705 info, "%s name should not end in '%s'" % (meta, name[-4:]))
708 def check_if(expr, info, source):
710 def check_if_str(ifcond, info):
711 if not isinstance(ifcond, str):
712 raise QAPISemError(
713 info,
714 "'if' condition of %s must be a string or a list of strings"
715 % source)
716 if ifcond.strip() == '':
717 raise QAPISemError(
718 info,
719 "'if' condition '%s' of %s makes no sense"
720 % (ifcond, source))
722 ifcond = expr.get('if')
723 if ifcond is None:
724 return
725 if isinstance(ifcond, list):
726 if ifcond == []:
727 raise QAPISemError(
728 info, "'if' condition [] of %s is useless" % source)
729 for elt in ifcond:
730 check_if_str(elt, info)
731 else:
732 check_if_str(ifcond, info)
735 def check_type(value, info, source,
736 allow_array=False, allow_dict=False):
737 if value is None:
738 return
740 # Array type
741 if isinstance(value, list):
742 if not allow_array:
743 raise QAPISemError(info, "%s cannot be an array" % source)
744 if len(value) != 1 or not isinstance(value[0], str):
745 raise QAPISemError(info,
746 "%s: array type must contain single type name" %
747 source)
748 return
750 # Type name
751 if isinstance(value, str):
752 return
754 # Anonymous type
756 if not allow_dict:
757 raise QAPISemError(info, "%s should be a type name" % source)
759 if not isinstance(value, OrderedDict):
760 raise QAPISemError(info,
761 "%s should be an object or type name" % source)
763 permit_upper = allow_dict in info.pragma.name_case_whitelist
765 # value is a dictionary, check that each member is okay
766 for (key, arg) in value.items():
767 key_source = "%s member '%s'" % (source, key)
768 check_name_str(key, info, key_source,
769 allow_optional=True, permit_upper=permit_upper)
770 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
771 raise QAPISemError(info, "%s uses reserved name" % key_source)
772 check_keys(arg, info, key_source, ['type'], ['if'])
773 check_if(arg, info, key_source)
774 normalize_if(arg)
775 check_type(arg['type'], info, key_source, allow_array=True)
778 def check_command(expr, info):
779 args = expr.get('data')
780 rets = expr.get('returns')
781 boxed = expr.get('boxed', False)
783 if boxed and args is None:
784 raise QAPISemError(info, "'boxed': true requires 'data'")
785 check_type(args, info, "'data'", allow_dict=not boxed)
786 check_type(rets, info, "'returns'", allow_array=True)
789 def check_event(expr, info):
790 args = expr.get('data')
791 boxed = expr.get('boxed', False)
793 if boxed and args is None:
794 raise QAPISemError(info, "'boxed': true requires 'data'")
795 check_type(args, info, "'data'", allow_dict=not boxed)
798 def check_union(expr, info):
799 name = expr['union']
800 base = expr.get('base')
801 discriminator = expr.get('discriminator')
802 members = expr['data']
804 if discriminator is None: # simple union
805 if base is not None:
806 raise QAPISemError(info, "'base' requires 'discriminator'")
807 else: # flat union
808 check_type(base, info, "'base'", allow_dict=name)
809 if not base:
810 raise QAPISemError(info, "'discriminator' requires 'base'")
811 check_name_is_str(discriminator, info, "'discriminator'")
813 for (key, value) in members.items():
814 source = "'data' member '%s'" % key
815 check_name_str(key, info, source)
816 check_keys(value, info, source, ['type'], ['if'])
817 check_if(value, info, source)
818 normalize_if(value)
819 check_type(value['type'], info, source, allow_array=not base)
822 def check_alternate(expr, info):
823 members = expr['data']
825 if len(members) == 0:
826 raise QAPISemError(info, "'data' must not be empty")
827 for (key, value) in members.items():
828 source = "'data' member '%s'" % key
829 check_name_str(key, info, source)
830 check_keys(value, info, source, ['type'], ['if'])
831 check_if(value, info, source)
832 normalize_if(value)
833 check_type(value['type'], info, source)
836 def check_enum(expr, info):
837 name = expr['enum']
838 members = expr['data']
839 prefix = expr.get('prefix')
841 if not isinstance(members, list):
842 raise QAPISemError(info, "'data' must be an array")
843 if prefix is not None and not isinstance(prefix, str):
844 raise QAPISemError(info, "'prefix' must be a string")
846 permit_upper = name in info.pragma.name_case_whitelist
848 for member in members:
849 source = "'data' member"
850 check_keys(member, info, source, ['name'], ['if'])
851 check_name_is_str(member['name'], info, source)
852 source = "%s '%s'" % (source, member['name'])
853 check_name_str(member['name'], info, source,
854 enum_member=True, permit_upper=permit_upper)
855 check_if(member, info, source)
856 normalize_if(member)
859 def check_struct(expr, info):
860 name = expr['struct']
861 members = expr['data']
862 features = expr.get('features')
864 check_type(members, info, "'data'", allow_dict=name)
865 check_type(expr.get('base'), info, "'base'")
867 if features:
868 if not isinstance(features, list):
869 raise QAPISemError(info, "'features' must be an array")
870 for f in features:
871 source = "'features' member"
872 assert isinstance(f, dict)
873 check_keys(f, info, source, ['name'], ['if'])
874 check_name_is_str(f['name'], info, source)
875 source = "%s '%s'" % (source, f['name'])
876 check_name_str(f['name'], info, source)
877 check_if(f, info, source)
878 normalize_if(f)
881 def check_keys(value, info, source, required, optional):
883 def pprint(elems):
884 return ', '.join("'" + e + "'" for e in sorted(elems))
886 missing = set(required) - set(value)
887 if missing:
888 raise QAPISemError(
889 info,
890 "%s misses key%s %s"
891 % (source, 's' if len(missing) > 1 else '',
892 pprint(missing)))
893 allowed = set(required + optional)
894 unknown = set(value) - allowed
895 if unknown:
896 raise QAPISemError(
897 info,
898 "%s has unknown key%s %s\nValid keys are %s."
899 % (source, 's' if len(unknown) > 1 else '',
900 pprint(unknown), pprint(allowed)))
903 def check_flags(expr, info):
904 for key in ['gen', 'success-response']:
905 if key in expr and expr[key] is not False:
906 raise QAPISemError(
907 info, "flag '%s' may only use false value" % key)
908 for key in ['boxed', 'allow-oob', 'allow-preconfig']:
909 if key in expr and expr[key] is not True:
910 raise QAPISemError(
911 info, "flag '%s' may only use true value" % key)
914 def normalize_enum(expr):
915 if isinstance(expr['data'], list):
916 expr['data'] = [m if isinstance(m, dict) else {'name': m}
917 for m in expr['data']]
920 def normalize_members(members):
921 if isinstance(members, OrderedDict):
922 for key, arg in members.items():
923 if isinstance(arg, dict):
924 continue
925 members[key] = {'type': arg}
928 def normalize_features(features):
929 if isinstance(features, list):
930 features[:] = [f if isinstance(f, dict) else {'name': f}
931 for f in features]
934 def normalize_if(expr):
935 ifcond = expr.get('if')
936 if isinstance(ifcond, str):
937 expr['if'] = [ifcond]
940 def check_exprs(exprs):
941 for expr_elem in exprs:
942 expr = expr_elem['expr']
943 info = expr_elem['info']
944 doc = expr_elem.get('doc')
946 if 'include' in expr:
947 continue
949 if 'enum' in expr:
950 meta = 'enum'
951 elif 'union' in expr:
952 meta = 'union'
953 elif 'alternate' in expr:
954 meta = 'alternate'
955 elif 'struct' in expr:
956 meta = 'struct'
957 elif 'command' in expr:
958 meta = 'command'
959 elif 'event' in expr:
960 meta = 'event'
961 else:
962 raise QAPISemError(info, "expression is missing metatype")
964 name = expr[meta]
965 check_name_is_str(name, info, "'%s'" % meta)
966 info.set_defn(meta, name)
967 check_defn_name_str(name, info, meta)
969 if doc:
970 if doc.symbol != name:
971 raise QAPISemError(
972 info, "documentation comment is for '%s'" % doc.symbol)
973 doc.check_expr(expr)
974 elif info.pragma.doc_required:
975 raise QAPISemError(info,
976 "documentation comment required")
978 if meta == 'enum':
979 check_keys(expr, info, meta,
980 ['enum', 'data'], ['if', 'prefix'])
981 normalize_enum(expr)
982 check_enum(expr, info)
983 elif meta == 'union':
984 check_keys(expr, info, meta,
985 ['union', 'data'],
986 ['base', 'discriminator', 'if'])
987 normalize_members(expr.get('base'))
988 normalize_members(expr['data'])
989 check_union(expr, info)
990 elif meta == 'alternate':
991 check_keys(expr, info, meta,
992 ['alternate', 'data'], ['if'])
993 normalize_members(expr['data'])
994 check_alternate(expr, info)
995 elif meta == 'struct':
996 check_keys(expr, info, meta,
997 ['struct', 'data'], ['base', 'if', 'features'])
998 normalize_members(expr['data'])
999 normalize_features(expr.get('features'))
1000 check_struct(expr, info)
1001 elif meta == 'command':
1002 check_keys(expr, info, meta,
1003 ['command'],
1004 ['data', 'returns', 'boxed', 'if',
1005 'gen', 'success-response', 'allow-oob',
1006 'allow-preconfig'])
1007 normalize_members(expr.get('data'))
1008 check_command(expr, info)
1009 elif meta == 'event':
1010 check_keys(expr, info, meta,
1011 ['event'], ['data', 'boxed', 'if'])
1012 normalize_members(expr.get('data'))
1013 check_event(expr, info)
1014 else:
1015 assert False, 'unexpected meta type'
1017 normalize_if(expr)
1018 check_if(expr, info, meta)
1019 check_flags(expr, info)
1021 return exprs
1025 # Schema compiler frontend
1026 # TODO catching name collisions in generated code would be nice
1029 class QAPISchemaEntity(object):
1030 meta = None
1032 def __init__(self, name, info, doc, ifcond=None):
1033 assert name is None or isinstance(name, str)
1034 self.name = name
1035 self._module = None
1036 # For explicitly defined entities, info points to the (explicit)
1037 # definition. For builtins (and their arrays), info is None.
1038 # For implicitly defined entities, info points to a place that
1039 # triggered the implicit definition (there may be more than one
1040 # such place).
1041 self.info = info
1042 self.doc = doc
1043 self._ifcond = ifcond or []
1044 self._checked = False
1046 def c_name(self):
1047 return c_name(self.name)
1049 def check(self, schema):
1050 assert not self._checked
1051 if self.info:
1052 self._module = os.path.relpath(self.info.fname,
1053 os.path.dirname(schema.fname))
1054 self._checked = True
1056 @property
1057 def ifcond(self):
1058 assert self._checked
1059 return self._ifcond
1061 @property
1062 def module(self):
1063 assert self._checked
1064 return self._module
1066 def is_implicit(self):
1067 return not self.info
1069 def visit(self, visitor):
1070 assert self._checked
1072 def describe(self):
1073 assert self.meta
1074 return "%s '%s'" % (self.meta, self.name)
1077 class QAPISchemaVisitor(object):
1078 def visit_begin(self, schema):
1079 pass
1081 def visit_end(self):
1082 pass
1084 def visit_module(self, fname):
1085 pass
1087 def visit_needed(self, entity):
1088 # Default to visiting everything
1089 return True
1091 def visit_include(self, fname, info):
1092 pass
1094 def visit_builtin_type(self, name, info, json_type):
1095 pass
1097 def visit_enum_type(self, name, info, ifcond, members, prefix):
1098 pass
1100 def visit_array_type(self, name, info, ifcond, element_type):
1101 pass
1103 def visit_object_type(self, name, info, ifcond, base, members, variants,
1104 features):
1105 pass
1107 def visit_object_type_flat(self, name, info, ifcond, members, variants,
1108 features):
1109 pass
1111 def visit_alternate_type(self, name, info, ifcond, variants):
1112 pass
1114 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1115 success_response, boxed, allow_oob, allow_preconfig):
1116 pass
1118 def visit_event(self, name, info, ifcond, arg_type, boxed):
1119 pass
1122 class QAPISchemaInclude(QAPISchemaEntity):
1124 def __init__(self, fname, info):
1125 QAPISchemaEntity.__init__(self, None, info, None)
1126 self.fname = fname
1128 def visit(self, visitor):
1129 QAPISchemaEntity.visit(self, visitor)
1130 visitor.visit_include(self.fname, self.info)
1133 class QAPISchemaType(QAPISchemaEntity):
1134 # Return the C type for common use.
1135 # For the types we commonly box, this is a pointer type.
1136 def c_type(self):
1137 pass
1139 # Return the C type to be used in a parameter list.
1140 def c_param_type(self):
1141 return self.c_type()
1143 # Return the C type to be used where we suppress boxing.
1144 def c_unboxed_type(self):
1145 return self.c_type()
1147 def json_type(self):
1148 pass
1150 def alternate_qtype(self):
1151 json2qtype = {
1152 'null': 'QTYPE_QNULL',
1153 'string': 'QTYPE_QSTRING',
1154 'number': 'QTYPE_QNUM',
1155 'int': 'QTYPE_QNUM',
1156 'boolean': 'QTYPE_QBOOL',
1157 'object': 'QTYPE_QDICT'
1159 return json2qtype.get(self.json_type())
1161 def doc_type(self):
1162 if self.is_implicit():
1163 return None
1164 return self.name
1166 def describe(self):
1167 assert self.meta
1168 return "%s type '%s'" % (self.meta, self.name)
1171 class QAPISchemaBuiltinType(QAPISchemaType):
1172 meta = 'built-in'
1174 def __init__(self, name, json_type, c_type):
1175 QAPISchemaType.__init__(self, name, None, None)
1176 assert not c_type or isinstance(c_type, str)
1177 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1178 'value')
1179 self._json_type_name = json_type
1180 self._c_type_name = c_type
1182 def c_name(self):
1183 return self.name
1185 def c_type(self):
1186 return self._c_type_name
1188 def c_param_type(self):
1189 if self.name == 'str':
1190 return 'const ' + self._c_type_name
1191 return self._c_type_name
1193 def json_type(self):
1194 return self._json_type_name
1196 def doc_type(self):
1197 return self.json_type()
1199 def visit(self, visitor):
1200 QAPISchemaType.visit(self, visitor)
1201 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1204 class QAPISchemaEnumType(QAPISchemaType):
1205 meta = 'enum'
1207 def __init__(self, name, info, doc, ifcond, members, prefix):
1208 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1209 for m in members:
1210 assert isinstance(m, QAPISchemaEnumMember)
1211 m.set_defined_in(name)
1212 assert prefix is None or isinstance(prefix, str)
1213 self.members = members
1214 self.prefix = prefix
1216 def check(self, schema):
1217 QAPISchemaType.check(self, schema)
1218 seen = {}
1219 for m in self.members:
1220 m.check_clash(self.info, seen)
1221 if self.doc:
1222 self.doc.connect_member(m)
1224 def is_implicit(self):
1225 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1226 return self.name.endswith('Kind') or self.name == 'QType'
1228 def c_type(self):
1229 return c_name(self.name)
1231 def member_names(self):
1232 return [m.name for m in self.members]
1234 def json_type(self):
1235 return 'string'
1237 def visit(self, visitor):
1238 QAPISchemaType.visit(self, visitor)
1239 visitor.visit_enum_type(self.name, self.info, self.ifcond,
1240 self.members, self.prefix)
1243 class QAPISchemaArrayType(QAPISchemaType):
1244 meta = 'array'
1246 def __init__(self, name, info, element_type):
1247 QAPISchemaType.__init__(self, name, info, None, None)
1248 assert isinstance(element_type, str)
1249 self._element_type_name = element_type
1250 self.element_type = None
1252 def check(self, schema):
1253 QAPISchemaType.check(self, schema)
1254 self.element_type = schema.resolve_type(
1255 self._element_type_name, self.info,
1256 self.info and self.info.defn_meta)
1257 assert not isinstance(self.element_type, QAPISchemaArrayType)
1259 @property
1260 def ifcond(self):
1261 assert self._checked
1262 return self.element_type.ifcond
1264 @property
1265 def module(self):
1266 assert self._checked
1267 return self.element_type.module
1269 def is_implicit(self):
1270 return True
1272 def c_type(self):
1273 return c_name(self.name) + pointer_suffix
1275 def json_type(self):
1276 return 'array'
1278 def doc_type(self):
1279 elt_doc_type = self.element_type.doc_type()
1280 if not elt_doc_type:
1281 return None
1282 return 'array of ' + elt_doc_type
1284 def visit(self, visitor):
1285 QAPISchemaType.visit(self, visitor)
1286 visitor.visit_array_type(self.name, self.info, self.ifcond,
1287 self.element_type)
1289 def describe(self):
1290 assert self.meta
1291 return "%s type ['%s']" % (self.meta, self._element_type_name)
1294 class QAPISchemaObjectType(QAPISchemaType):
1295 def __init__(self, name, info, doc, ifcond,
1296 base, local_members, variants, features):
1297 # struct has local_members, optional base, and no variants
1298 # flat union has base, variants, and no local_members
1299 # simple union has local_members, variants, and no base
1300 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1301 self.meta = 'union' if variants else 'struct'
1302 assert base is None or isinstance(base, str)
1303 for m in local_members:
1304 assert isinstance(m, QAPISchemaObjectTypeMember)
1305 m.set_defined_in(name)
1306 if variants is not None:
1307 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1308 variants.set_defined_in(name)
1309 for f in features:
1310 assert isinstance(f, QAPISchemaFeature)
1311 f.set_defined_in(name)
1312 self._base_name = base
1313 self.base = None
1314 self.local_members = local_members
1315 self.variants = variants
1316 self.members = None
1317 self.features = features
1319 def check(self, schema):
1320 # This calls another type T's .check() exactly when the C
1321 # struct emitted by gen_object() contains that T's C struct
1322 # (pointers don't count).
1323 if self.members is not None:
1324 # A previous .check() completed: nothing to do
1325 return
1326 if self._checked:
1327 # Recursed: C struct contains itself
1328 raise QAPISemError(self.info,
1329 "object %s contains itself" % self.name)
1331 QAPISchemaType.check(self, schema)
1332 assert self._checked and self.members is None
1334 seen = OrderedDict()
1335 if self._base_name:
1336 self.base = schema.resolve_type(self._base_name, self.info,
1337 "'base'")
1338 if (not isinstance(self.base, QAPISchemaObjectType)
1339 or self.base.variants):
1340 raise QAPISemError(
1341 self.info,
1342 "'base' requires a struct type, %s isn't"
1343 % self.base.describe())
1344 self.base.check(schema)
1345 self.base.check_clash(self.info, seen)
1346 for m in self.local_members:
1347 m.check(schema)
1348 m.check_clash(self.info, seen)
1349 if self.doc:
1350 self.doc.connect_member(m)
1351 members = seen.values()
1353 if self.variants:
1354 self.variants.check(schema, seen)
1355 self.variants.check_clash(self.info, seen)
1357 # Features are in a name space separate from members
1358 seen = {}
1359 for f in self.features:
1360 f.check_clash(self.info, seen)
1362 if self.doc:
1363 self.doc.check()
1365 self.members = members # mark completed
1367 # Check that the members of this type do not cause duplicate JSON members,
1368 # and update seen to track the members seen so far. Report any errors
1369 # on behalf of info, which is not necessarily self.info
1370 def check_clash(self, info, seen):
1371 assert self._checked
1372 assert not self.variants # not implemented
1373 for m in self.members:
1374 m.check_clash(info, seen)
1376 @property
1377 def ifcond(self):
1378 assert self._checked
1379 if isinstance(self._ifcond, QAPISchemaType):
1380 # Simple union wrapper type inherits from wrapped type;
1381 # see _make_implicit_object_type()
1382 return self._ifcond.ifcond
1383 return self._ifcond
1385 def is_implicit(self):
1386 # See QAPISchema._make_implicit_object_type(), as well as
1387 # _def_predefineds()
1388 return self.name.startswith('q_')
1390 def is_empty(self):
1391 assert self.members is not None
1392 return not self.members and not self.variants
1394 def c_name(self):
1395 assert self.name != 'q_empty'
1396 return QAPISchemaType.c_name(self)
1398 def c_type(self):
1399 assert not self.is_implicit()
1400 return c_name(self.name) + pointer_suffix
1402 def c_unboxed_type(self):
1403 return c_name(self.name)
1405 def json_type(self):
1406 return 'object'
1408 def visit(self, visitor):
1409 QAPISchemaType.visit(self, visitor)
1410 visitor.visit_object_type(self.name, self.info, self.ifcond,
1411 self.base, self.local_members, self.variants,
1412 self.features)
1413 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1414 self.members, self.variants,
1415 self.features)
1418 class QAPISchemaMember(object):
1419 """ Represents object members, enum members and features """
1420 role = 'member'
1422 def __init__(self, name, info, ifcond=None):
1423 assert isinstance(name, str)
1424 self.name = name
1425 self.info = info
1426 self.ifcond = ifcond or []
1427 self.defined_in = None
1429 def set_defined_in(self, name):
1430 assert not self.defined_in
1431 self.defined_in = name
1433 def check_clash(self, info, seen):
1434 cname = c_name(self.name)
1435 if cname in seen:
1436 raise QAPISemError(
1437 info,
1438 "%s collides with %s"
1439 % (self.describe(info), seen[cname].describe(info)))
1440 seen[cname] = self
1442 def describe(self, info):
1443 role = self.role
1444 defined_in = self.defined_in
1445 assert defined_in
1447 if defined_in.startswith('q_obj_'):
1448 # See QAPISchema._make_implicit_object_type() - reverse the
1449 # mapping there to create a nice human-readable description
1450 defined_in = defined_in[6:]
1451 if defined_in.endswith('-arg'):
1452 # Implicit type created for a command's dict 'data'
1453 assert role == 'member'
1454 role = 'parameter'
1455 elif defined_in.endswith('-base'):
1456 # Implicit type created for a flat union's dict 'base'
1457 role = 'base ' + role
1458 else:
1459 # Implicit type created for a simple union's branch
1460 assert defined_in.endswith('-wrapper')
1461 # Unreachable and not implemented
1462 assert False
1463 elif defined_in.endswith('Kind'):
1464 # See QAPISchema._make_implicit_enum_type()
1465 # Implicit enum created for simple union's branches
1466 assert role == 'value'
1467 role = 'branch'
1468 elif defined_in != info.defn_name:
1469 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
1470 return "%s '%s'" % (role, self.name)
1473 class QAPISchemaEnumMember(QAPISchemaMember):
1474 role = 'value'
1477 class QAPISchemaFeature(QAPISchemaMember):
1478 role = 'feature'
1481 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1482 def __init__(self, name, info, typ, optional, ifcond=None):
1483 QAPISchemaMember.__init__(self, name, info, ifcond)
1484 assert isinstance(typ, str)
1485 assert isinstance(optional, bool)
1486 self._type_name = typ
1487 self.type = None
1488 self.optional = optional
1490 def check(self, schema):
1491 assert self.defined_in
1492 self.type = schema.resolve_type(self._type_name, self.info,
1493 self.describe)
1496 class QAPISchemaObjectTypeVariants(object):
1497 def __init__(self, tag_name, info, tag_member, variants):
1498 # Flat unions pass tag_name but not tag_member.
1499 # Simple unions and alternates pass tag_member but not tag_name.
1500 # After check(), tag_member is always set, and tag_name remains
1501 # a reliable witness of being used by a flat union.
1502 assert bool(tag_member) != bool(tag_name)
1503 assert (isinstance(tag_name, str) or
1504 isinstance(tag_member, QAPISchemaObjectTypeMember))
1505 for v in variants:
1506 assert isinstance(v, QAPISchemaObjectTypeVariant)
1507 self._tag_name = tag_name
1508 self.info = info
1509 self.tag_member = tag_member
1510 self.variants = variants
1512 def set_defined_in(self, name):
1513 for v in self.variants:
1514 v.set_defined_in(name)
1516 def check(self, schema, seen):
1517 if not self.tag_member: # flat union
1518 self.tag_member = seen.get(c_name(self._tag_name))
1519 base = "'base'"
1520 # Pointing to the base type when not implicit would be
1521 # nice, but we don't know it here
1522 if not self.tag_member or self._tag_name != self.tag_member.name:
1523 raise QAPISemError(
1524 self.info,
1525 "discriminator '%s' is not a member of %s"
1526 % (self._tag_name, base))
1527 # Here we do:
1528 base_type = schema.lookup_type(self.tag_member.defined_in)
1529 assert base_type
1530 if not base_type.is_implicit():
1531 base = "base type '%s'" % self.tag_member.defined_in
1532 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
1533 raise QAPISemError(
1534 self.info,
1535 "discriminator member '%s' of %s must be of enum type"
1536 % (self._tag_name, base))
1537 if self.tag_member.optional:
1538 raise QAPISemError(
1539 self.info,
1540 "discriminator member '%s' of %s must not be optional"
1541 % (self._tag_name, base))
1542 if self.tag_member.ifcond:
1543 raise QAPISemError(
1544 self.info,
1545 "discriminator member '%s' of %s must not be conditional"
1546 % (self._tag_name, base))
1547 else: # simple union
1548 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1549 assert not self.tag_member.optional
1550 assert self.tag_member.ifcond == []
1551 if self._tag_name: # flat union
1552 # branches that are not explicitly covered get an empty type
1553 cases = set([v.name for v in self.variants])
1554 for m in self.tag_member.type.members:
1555 if m.name not in cases:
1556 v = QAPISchemaObjectTypeVariant(m.name, self.info,
1557 'q_empty', m.ifcond)
1558 v.set_defined_in(self.tag_member.defined_in)
1559 self.variants.append(v)
1560 if not self.variants:
1561 raise QAPISemError(self.info, "union has no branches")
1562 for v in self.variants:
1563 v.check(schema)
1564 # Union names must match enum values; alternate names are
1565 # checked separately. Use 'seen' to tell the two apart.
1566 if seen:
1567 if v.name not in self.tag_member.type.member_names():
1568 raise QAPISemError(
1569 self.info,
1570 "branch '%s' is not a value of %s"
1571 % (v.name, self.tag_member.type.describe()))
1572 if (not isinstance(v.type, QAPISchemaObjectType)
1573 or v.type.variants):
1574 raise QAPISemError(
1575 self.info,
1576 "%s cannot use %s"
1577 % (v.describe(self.info), v.type.describe()))
1578 v.type.check(schema)
1580 def check_clash(self, info, seen):
1581 for v in self.variants:
1582 # Reset seen map for each variant, since qapi names from one
1583 # branch do not affect another branch
1584 v.type.check_clash(info, dict(seen))
1587 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1588 role = 'branch'
1590 def __init__(self, name, info, typ, ifcond=None):
1591 QAPISchemaObjectTypeMember.__init__(self, name, info, typ,
1592 False, ifcond)
1595 class QAPISchemaAlternateType(QAPISchemaType):
1596 meta = 'alternate'
1598 def __init__(self, name, info, doc, ifcond, variants):
1599 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1600 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1601 assert variants.tag_member
1602 variants.set_defined_in(name)
1603 variants.tag_member.set_defined_in(self.name)
1604 self.variants = variants
1606 def check(self, schema):
1607 QAPISchemaType.check(self, schema)
1608 self.variants.tag_member.check(schema)
1609 # Not calling self.variants.check_clash(), because there's nothing
1610 # to clash with
1611 self.variants.check(schema, {})
1612 # Alternate branch names have no relation to the tag enum values;
1613 # so we have to check for potential name collisions ourselves.
1614 seen = {}
1615 types_seen = {}
1616 for v in self.variants.variants:
1617 v.check_clash(self.info, seen)
1618 qtype = v.type.alternate_qtype()
1619 if not qtype:
1620 raise QAPISemError(
1621 self.info,
1622 "%s cannot use %s"
1623 % (v.describe(self.info), v.type.describe()))
1624 conflicting = set([qtype])
1625 if qtype == 'QTYPE_QSTRING':
1626 if isinstance(v.type, QAPISchemaEnumType):
1627 for m in v.type.members:
1628 if m.name in ['on', 'off']:
1629 conflicting.add('QTYPE_QBOOL')
1630 if re.match(r'[-+0-9.]', m.name):
1631 # lazy, could be tightened
1632 conflicting.add('QTYPE_QNUM')
1633 else:
1634 conflicting.add('QTYPE_QNUM')
1635 conflicting.add('QTYPE_QBOOL')
1636 for qt in conflicting:
1637 if qt in types_seen:
1638 raise QAPISemError(
1639 self.info,
1640 "%s can't be distinguished from '%s'"
1641 % (v.describe(self.info), types_seen[qt]))
1642 types_seen[qt] = v.name
1643 if self.doc:
1644 self.doc.connect_member(v)
1645 if self.doc:
1646 self.doc.check()
1648 def c_type(self):
1649 return c_name(self.name) + pointer_suffix
1651 def json_type(self):
1652 return 'value'
1654 def visit(self, visitor):
1655 QAPISchemaType.visit(self, visitor)
1656 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1657 self.variants)
1660 class QAPISchemaCommand(QAPISchemaEntity):
1661 meta = 'command'
1663 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1664 gen, success_response, boxed, allow_oob, allow_preconfig):
1665 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1666 assert not arg_type or isinstance(arg_type, str)
1667 assert not ret_type or isinstance(ret_type, str)
1668 self._arg_type_name = arg_type
1669 self.arg_type = None
1670 self._ret_type_name = ret_type
1671 self.ret_type = None
1672 self.gen = gen
1673 self.success_response = success_response
1674 self.boxed = boxed
1675 self.allow_oob = allow_oob
1676 self.allow_preconfig = allow_preconfig
1678 def check(self, schema):
1679 QAPISchemaEntity.check(self, schema)
1680 if self._arg_type_name:
1681 self.arg_type = schema.resolve_type(
1682 self._arg_type_name, self.info, "command's 'data'")
1683 if not isinstance(self.arg_type, QAPISchemaObjectType):
1684 raise QAPISemError(
1685 self.info,
1686 "command's 'data' cannot take %s"
1687 % self.arg_type.describe())
1688 if self.arg_type.variants and not self.boxed:
1689 raise QAPISemError(
1690 self.info,
1691 "command's 'data' can take %s only with 'boxed': true"
1692 % self.arg_type.describe())
1693 if self._ret_type_name:
1694 self.ret_type = schema.resolve_type(
1695 self._ret_type_name, self.info, "command's 'returns'")
1696 if self.name not in self.info.pragma.returns_whitelist:
1697 if not (isinstance(self.ret_type, QAPISchemaObjectType)
1698 or (isinstance(self.ret_type, QAPISchemaArrayType)
1699 and isinstance(self.ret_type.element_type,
1700 QAPISchemaObjectType))):
1701 raise QAPISemError(
1702 self.info,
1703 "command's 'returns' cannot take %s"
1704 % self.ret_type.describe())
1706 def visit(self, visitor):
1707 QAPISchemaEntity.visit(self, visitor)
1708 visitor.visit_command(self.name, self.info, self.ifcond,
1709 self.arg_type, self.ret_type,
1710 self.gen, self.success_response,
1711 self.boxed, self.allow_oob,
1712 self.allow_preconfig)
1715 class QAPISchemaEvent(QAPISchemaEntity):
1716 meta = 'event'
1718 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1719 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1720 assert not arg_type or isinstance(arg_type, str)
1721 self._arg_type_name = arg_type
1722 self.arg_type = None
1723 self.boxed = boxed
1725 def check(self, schema):
1726 QAPISchemaEntity.check(self, schema)
1727 if self._arg_type_name:
1728 self.arg_type = schema.resolve_type(
1729 self._arg_type_name, self.info, "event's 'data'")
1730 if not isinstance(self.arg_type, QAPISchemaObjectType):
1731 raise QAPISemError(
1732 self.info,
1733 "event's 'data' cannot take %s"
1734 % self.arg_type.describe())
1735 if self.arg_type.variants and not self.boxed:
1736 raise QAPISemError(
1737 self.info,
1738 "event's 'data' can take %s only with 'boxed': true"
1739 % self.arg_type.describe())
1741 def visit(self, visitor):
1742 QAPISchemaEntity.visit(self, visitor)
1743 visitor.visit_event(self.name, self.info, self.ifcond,
1744 self.arg_type, self.boxed)
1747 class QAPISchema(object):
1748 def __init__(self, fname):
1749 self.fname = fname
1750 parser = QAPISchemaParser(fname)
1751 exprs = check_exprs(parser.exprs)
1752 self.docs = parser.docs
1753 self._entity_list = []
1754 self._entity_dict = {}
1755 self._predefining = True
1756 self._def_predefineds()
1757 self._predefining = False
1758 self._def_exprs(exprs)
1759 self.check()
1761 def _def_entity(self, ent):
1762 # Only the predefined types are allowed to not have info
1763 assert ent.info or self._predefining
1764 self._entity_list.append(ent)
1765 if ent.name is None:
1766 return
1767 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1768 # because they're liable to clash in generated C.
1769 other_ent = self._entity_dict.get(ent.name)
1770 if other_ent:
1771 if other_ent.info:
1772 where = QAPIError(other_ent.info, None, "previous definition")
1773 raise QAPISemError(
1774 ent.info,
1775 "'%s' is already defined\n%s" % (ent.name, where))
1776 raise QAPISemError(
1777 ent.info, "%s is already defined" % other_ent.describe())
1778 self._entity_dict[ent.name] = ent
1780 def lookup_entity(self, name, typ=None):
1781 ent = self._entity_dict.get(name)
1782 if typ and not isinstance(ent, typ):
1783 return None
1784 return ent
1786 def lookup_type(self, name):
1787 return self.lookup_entity(name, QAPISchemaType)
1789 def resolve_type(self, name, info, what):
1790 typ = self.lookup_type(name)
1791 if not typ:
1792 if callable(what):
1793 what = what(info)
1794 raise QAPISemError(
1795 info, "%s uses unknown type '%s'" % (what, name))
1796 return typ
1798 def _def_include(self, expr, info, doc):
1799 include = expr['include']
1800 assert doc is None
1801 main_info = info
1802 while main_info.parent:
1803 main_info = main_info.parent
1804 fname = os.path.relpath(include, os.path.dirname(main_info.fname))
1805 self._def_entity(QAPISchemaInclude(fname, info))
1807 def _def_builtin_type(self, name, json_type, c_type):
1808 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1809 # Instantiating only the arrays that are actually used would
1810 # be nice, but we can't as long as their generated code
1811 # (qapi-builtin-types.[ch]) may be shared by some other
1812 # schema.
1813 self._make_array_type(name, None)
1815 def _def_predefineds(self):
1816 for t in [('str', 'string', 'char' + pointer_suffix),
1817 ('number', 'number', 'double'),
1818 ('int', 'int', 'int64_t'),
1819 ('int8', 'int', 'int8_t'),
1820 ('int16', 'int', 'int16_t'),
1821 ('int32', 'int', 'int32_t'),
1822 ('int64', 'int', 'int64_t'),
1823 ('uint8', 'int', 'uint8_t'),
1824 ('uint16', 'int', 'uint16_t'),
1825 ('uint32', 'int', 'uint32_t'),
1826 ('uint64', 'int', 'uint64_t'),
1827 ('size', 'int', 'uint64_t'),
1828 ('bool', 'boolean', 'bool'),
1829 ('any', 'value', 'QObject' + pointer_suffix),
1830 ('null', 'null', 'QNull' + pointer_suffix)]:
1831 self._def_builtin_type(*t)
1832 self.the_empty_object_type = QAPISchemaObjectType(
1833 'q_empty', None, None, None, None, [], None, [])
1834 self._def_entity(self.the_empty_object_type)
1836 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1837 'qbool']
1838 qtype_values = self._make_enum_members(
1839 [{'name': n} for n in qtypes], None)
1841 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1842 qtype_values, 'QTYPE'))
1844 def _make_features(self, features, info):
1845 return [QAPISchemaFeature(f['name'], info, f.get('if'))
1846 for f in features]
1848 def _make_enum_members(self, values, info):
1849 return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
1850 for v in values]
1852 def _make_implicit_enum_type(self, name, info, ifcond, values):
1853 # See also QAPISchemaObjectTypeMember.describe()
1854 name = name + 'Kind' # reserved by check_defn_name_str()
1855 self._def_entity(QAPISchemaEnumType(
1856 name, info, None, ifcond, self._make_enum_members(values, info),
1857 None))
1858 return name
1860 def _make_array_type(self, element_type, info):
1861 name = element_type + 'List' # reserved by check_defn_name_str()
1862 if not self.lookup_type(name):
1863 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1864 return name
1866 def _make_implicit_object_type(self, name, info, doc, ifcond,
1867 role, members):
1868 if not members:
1869 return None
1870 # See also QAPISchemaObjectTypeMember.describe()
1871 name = 'q_obj_%s-%s' % (name, role)
1872 typ = self.lookup_entity(name, QAPISchemaObjectType)
1873 if typ:
1874 # The implicit object type has multiple users. This can
1875 # happen only for simple unions' implicit wrapper types.
1876 # Its ifcond should be the disjunction of its user's
1877 # ifconds. Not implemented. Instead, we always pass the
1878 # wrapped type's ifcond, which is trivially the same for all
1879 # users. It's also necessary for the wrapper to compile.
1880 # But it's not tight: the disjunction need not imply it. We
1881 # may end up compiling useless wrapper types.
1882 # TODO kill simple unions or implement the disjunction
1883 assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access
1884 else:
1885 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1886 None, members, None, []))
1887 return name
1889 def _def_enum_type(self, expr, info, doc):
1890 name = expr['enum']
1891 data = expr['data']
1892 prefix = expr.get('prefix')
1893 ifcond = expr.get('if')
1894 self._def_entity(QAPISchemaEnumType(
1895 name, info, doc, ifcond,
1896 self._make_enum_members(data, info), prefix))
1898 def _make_member(self, name, typ, ifcond, info):
1899 optional = False
1900 if name.startswith('*'):
1901 name = name[1:]
1902 optional = True
1903 if isinstance(typ, list):
1904 assert len(typ) == 1
1905 typ = self._make_array_type(typ[0], info)
1906 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond)
1908 def _make_members(self, data, info):
1909 return [self._make_member(key, value['type'], value.get('if'), info)
1910 for (key, value) in data.items()]
1912 def _def_struct_type(self, expr, info, doc):
1913 name = expr['struct']
1914 base = expr.get('base')
1915 data = expr['data']
1916 ifcond = expr.get('if')
1917 features = expr.get('features', [])
1918 self._def_entity(QAPISchemaObjectType(
1919 name, info, doc, ifcond, base,
1920 self._make_members(data, info),
1921 None,
1922 self._make_features(features, info)))
1924 def _make_variant(self, case, typ, ifcond, info):
1925 return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
1927 def _make_simple_variant(self, case, typ, ifcond, info):
1928 if isinstance(typ, list):
1929 assert len(typ) == 1
1930 typ = self._make_array_type(typ[0], info)
1931 typ = self._make_implicit_object_type(
1932 typ, info, None, self.lookup_type(typ),
1933 'wrapper', [self._make_member('data', typ, None, info)])
1934 return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
1936 def _def_union_type(self, expr, info, doc):
1937 name = expr['union']
1938 data = expr['data']
1939 base = expr.get('base')
1940 ifcond = expr.get('if')
1941 tag_name = expr.get('discriminator')
1942 tag_member = None
1943 if isinstance(base, dict):
1944 base = self._make_implicit_object_type(
1945 name, info, doc, ifcond,
1946 'base', self._make_members(base, info))
1947 if tag_name:
1948 variants = [self._make_variant(key, value['type'],
1949 value.get('if'), info)
1950 for (key, value) in data.items()]
1951 members = []
1952 else:
1953 variants = [self._make_simple_variant(key, value['type'],
1954 value.get('if'), info)
1955 for (key, value) in data.items()]
1956 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1957 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1958 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
1959 members = [tag_member]
1960 self._def_entity(
1961 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1962 QAPISchemaObjectTypeVariants(
1963 tag_name, info, tag_member, variants),
1964 []))
1966 def _def_alternate_type(self, expr, info, doc):
1967 name = expr['alternate']
1968 data = expr['data']
1969 ifcond = expr.get('if')
1970 variants = [self._make_variant(key, value['type'], value.get('if'),
1971 info)
1972 for (key, value) in data.items()]
1973 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1974 self._def_entity(
1975 QAPISchemaAlternateType(name, info, doc, ifcond,
1976 QAPISchemaObjectTypeVariants(
1977 None, info, tag_member, variants)))
1979 def _def_command(self, expr, info, doc):
1980 name = expr['command']
1981 data = expr.get('data')
1982 rets = expr.get('returns')
1983 gen = expr.get('gen', True)
1984 success_response = expr.get('success-response', True)
1985 boxed = expr.get('boxed', False)
1986 allow_oob = expr.get('allow-oob', False)
1987 allow_preconfig = expr.get('allow-preconfig', False)
1988 ifcond = expr.get('if')
1989 if isinstance(data, OrderedDict):
1990 data = self._make_implicit_object_type(
1991 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1992 if isinstance(rets, list):
1993 assert len(rets) == 1
1994 rets = self._make_array_type(rets[0], info)
1995 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1996 gen, success_response,
1997 boxed, allow_oob, allow_preconfig))
1999 def _def_event(self, expr, info, doc):
2000 name = expr['event']
2001 data = expr.get('data')
2002 boxed = expr.get('boxed', False)
2003 ifcond = expr.get('if')
2004 if isinstance(data, OrderedDict):
2005 data = self._make_implicit_object_type(
2006 name, info, doc, ifcond, 'arg', self._make_members(data, info))
2007 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
2009 def _def_exprs(self, exprs):
2010 for expr_elem in exprs:
2011 expr = expr_elem['expr']
2012 info = expr_elem['info']
2013 doc = expr_elem.get('doc')
2014 if 'enum' in expr:
2015 self._def_enum_type(expr, info, doc)
2016 elif 'struct' in expr:
2017 self._def_struct_type(expr, info, doc)
2018 elif 'union' in expr:
2019 self._def_union_type(expr, info, doc)
2020 elif 'alternate' in expr:
2021 self._def_alternate_type(expr, info, doc)
2022 elif 'command' in expr:
2023 self._def_command(expr, info, doc)
2024 elif 'event' in expr:
2025 self._def_event(expr, info, doc)
2026 elif 'include' in expr:
2027 self._def_include(expr, info, doc)
2028 else:
2029 assert False
2031 def check(self):
2032 for ent in self._entity_list:
2033 ent.check(self)
2035 def visit(self, visitor):
2036 visitor.visit_begin(self)
2037 module = None
2038 visitor.visit_module(module)
2039 for entity in self._entity_list:
2040 if visitor.visit_needed(entity):
2041 if entity.module != module:
2042 module = entity.module
2043 visitor.visit_module(module)
2044 entity.visit(visitor)
2045 visitor.visit_end()
2049 # Code generation helpers
2052 def camel_case(name):
2053 new_name = ''
2054 first = True
2055 for ch in name:
2056 if ch in ['_', '-']:
2057 first = True
2058 elif first:
2059 new_name += ch.upper()
2060 first = False
2061 else:
2062 new_name += ch.lower()
2063 return new_name
2066 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2067 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2068 # ENUM24_Name -> ENUM24_NAME
2069 def camel_to_upper(value):
2070 c_fun_str = c_name(value, False)
2071 if value.isupper():
2072 return c_fun_str
2074 new_name = ''
2075 length = len(c_fun_str)
2076 for i in range(length):
2077 c = c_fun_str[i]
2078 # When c is upper and no '_' appears before, do more checks
2079 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
2080 if i < length - 1 and c_fun_str[i + 1].islower():
2081 new_name += '_'
2082 elif c_fun_str[i - 1].isdigit():
2083 new_name += '_'
2084 new_name += c
2085 return new_name.lstrip('_').upper()
2088 def c_enum_const(type_name, const_name, prefix=None):
2089 if prefix is not None:
2090 type_name = prefix
2091 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
2094 if hasattr(str, 'maketrans'):
2095 c_name_trans = str.maketrans('.-', '__')
2096 else:
2097 c_name_trans = string.maketrans('.-', '__')
2100 # Map @name to a valid C identifier.
2101 # If @protect, avoid returning certain ticklish identifiers (like
2102 # C keywords) by prepending 'q_'.
2104 # Used for converting 'name' from a 'name':'type' qapi definition
2105 # into a generated struct member, as well as converting type names
2106 # into substrings of a generated C function name.
2107 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2108 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2109 def c_name(name, protect=True):
2110 # ANSI X3J11/88-090, 3.1.1
2111 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
2112 'default', 'do', 'double', 'else', 'enum', 'extern',
2113 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2114 'return', 'short', 'signed', 'sizeof', 'static',
2115 'struct', 'switch', 'typedef', 'union', 'unsigned',
2116 'void', 'volatile', 'while'])
2117 # ISO/IEC 9899:1999, 6.4.1
2118 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2119 # ISO/IEC 9899:2011, 6.4.1
2120 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2121 '_Noreturn', '_Static_assert', '_Thread_local'])
2122 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2123 # excluding _.*
2124 gcc_words = set(['asm', 'typeof'])
2125 # C++ ISO/IEC 14882:2003 2.11
2126 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
2127 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2128 'namespace', 'new', 'operator', 'private', 'protected',
2129 'public', 'reinterpret_cast', 'static_cast', 'template',
2130 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2131 'using', 'virtual', 'wchar_t',
2132 # alternative representations
2133 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2134 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2135 # namespace pollution:
2136 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2137 name = name.translate(c_name_trans)
2138 if protect and (name in c89_words | c99_words | c11_words | gcc_words
2139 | cpp_words | polluted_words):
2140 return 'q_' + name
2141 return name
2144 eatspace = '\033EATSPACE.'
2145 pointer_suffix = ' *' + eatspace
2148 def genindent(count):
2149 ret = ''
2150 for _ in range(count):
2151 ret += ' '
2152 return ret
2155 indent_level = 0
2158 def push_indent(indent_amount=4):
2159 global indent_level
2160 indent_level += indent_amount
2163 def pop_indent(indent_amount=4):
2164 global indent_level
2165 indent_level -= indent_amount
2168 # Generate @code with @kwds interpolated.
2169 # Obey indent_level, and strip eatspace.
2170 def cgen(code, **kwds):
2171 raw = code % kwds
2172 if indent_level:
2173 indent = genindent(indent_level)
2174 # re.subn() lacks flags support before Python 2.7, use re.compile()
2175 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2176 indent, raw)
2177 raw = raw[0]
2178 return re.sub(re.escape(eatspace) + r' *', '', raw)
2181 def mcgen(code, **kwds):
2182 if code[0] == '\n':
2183 code = code[1:]
2184 return cgen(code, **kwds)
2187 def c_fname(filename):
2188 return re.sub(r'[^A-Za-z0-9_]', '_', filename)
2191 def guardstart(name):
2192 return mcgen('''
2193 #ifndef %(name)s
2194 #define %(name)s
2196 ''',
2197 name=c_fname(name).upper())
2200 def guardend(name):
2201 return mcgen('''
2203 #endif /* %(name)s */
2204 ''',
2205 name=c_fname(name).upper())
2208 def gen_if(ifcond):
2209 ret = ''
2210 for ifc in ifcond:
2211 ret += mcgen('''
2212 #if %(cond)s
2213 ''', cond=ifc)
2214 return ret
2217 def gen_endif(ifcond):
2218 ret = ''
2219 for ifc in reversed(ifcond):
2220 ret += mcgen('''
2221 #endif /* %(cond)s */
2222 ''', cond=ifc)
2223 return ret
2226 def _wrap_ifcond(ifcond, before, after):
2227 if before == after:
2228 return after # suppress empty #if ... #endif
2230 assert after.startswith(before)
2231 out = before
2232 added = after[len(before):]
2233 if added[0] == '\n':
2234 out += '\n'
2235 added = added[1:]
2236 out += gen_if(ifcond)
2237 out += added
2238 out += gen_endif(ifcond)
2239 return out
2242 def build_params(arg_type, boxed, extra=None):
2243 ret = ''
2244 sep = ''
2245 if boxed:
2246 assert arg_type
2247 ret += '%s arg' % arg_type.c_param_type()
2248 sep = ', '
2249 elif arg_type:
2250 assert not arg_type.variants
2251 for memb in arg_type.members:
2252 ret += sep
2253 sep = ', '
2254 if memb.optional:
2255 ret += 'bool has_%s, ' % c_name(memb.name)
2256 ret += '%s %s' % (memb.type.c_param_type(),
2257 c_name(memb.name))
2258 if extra:
2259 ret += sep + extra
2260 return ret if ret else 'void'
2264 # Accumulate and write output
2267 class QAPIGen(object):
2269 def __init__(self, fname):
2270 self.fname = fname
2271 self._preamble = ''
2272 self._body = ''
2274 def preamble_add(self, text):
2275 self._preamble += text
2277 def add(self, text):
2278 self._body += text
2280 def get_content(self):
2281 return self._top() + self._preamble + self._body + self._bottom()
2283 def _top(self):
2284 return ''
2286 def _bottom(self):
2287 return ''
2289 def write(self, output_dir):
2290 pathname = os.path.join(output_dir, self.fname)
2291 dir = os.path.dirname(pathname)
2292 if dir:
2293 try:
2294 os.makedirs(dir)
2295 except os.error as e:
2296 if e.errno != errno.EEXIST:
2297 raise
2298 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2299 if sys.version_info[0] >= 3:
2300 f = open(fd, 'r+', encoding='utf-8')
2301 else:
2302 f = os.fdopen(fd, 'r+')
2303 text = self.get_content()
2304 oldtext = f.read(len(text) + 1)
2305 if text != oldtext:
2306 f.seek(0)
2307 f.truncate(0)
2308 f.write(text)
2309 f.close()
2312 @contextmanager
2313 def ifcontext(ifcond, *args):
2314 """A 'with' statement context manager to wrap with start_if()/end_if()
2316 *args: any number of QAPIGenCCode
2318 Example::
2320 with ifcontext(ifcond, self._genh, self._genc):
2321 modify self._genh and self._genc ...
2323 Is equivalent to calling::
2325 self._genh.start_if(ifcond)
2326 self._genc.start_if(ifcond)
2327 modify self._genh and self._genc ...
2328 self._genh.end_if()
2329 self._genc.end_if()
2331 for arg in args:
2332 arg.start_if(ifcond)
2333 yield
2334 for arg in args:
2335 arg.end_if()
2338 class QAPIGenCCode(QAPIGen):
2340 def __init__(self, fname):
2341 QAPIGen.__init__(self, fname)
2342 self._start_if = None
2344 def start_if(self, ifcond):
2345 assert self._start_if is None
2346 self._start_if = (ifcond, self._body, self._preamble)
2348 def end_if(self):
2349 assert self._start_if
2350 self._wrap_ifcond()
2351 self._start_if = None
2353 def _wrap_ifcond(self):
2354 self._body = _wrap_ifcond(self._start_if[0],
2355 self._start_if[1], self._body)
2356 self._preamble = _wrap_ifcond(self._start_if[0],
2357 self._start_if[2], self._preamble)
2359 def get_content(self):
2360 assert self._start_if is None
2361 return QAPIGen.get_content(self)
2364 class QAPIGenC(QAPIGenCCode):
2366 def __init__(self, fname, blurb, pydoc):
2367 QAPIGenCCode.__init__(self, fname)
2368 self._blurb = blurb
2369 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2370 re.MULTILINE))
2372 def _top(self):
2373 return mcgen('''
2374 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2377 %(blurb)s
2379 * %(copyright)s
2381 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2382 * See the COPYING.LIB file in the top-level directory.
2385 ''',
2386 blurb=self._blurb, copyright=self._copyright)
2388 def _bottom(self):
2389 return mcgen('''
2391 /* Dummy declaration to prevent empty .o file */
2392 char qapi_dummy_%(name)s;
2393 ''',
2394 name=c_fname(self.fname))
2397 class QAPIGenH(QAPIGenC):
2399 def _top(self):
2400 return QAPIGenC._top(self) + guardstart(self.fname)
2402 def _bottom(self):
2403 return guardend(self.fname)
2406 class QAPIGenDoc(QAPIGen):
2408 def _top(self):
2409 return (QAPIGen._top(self)
2410 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2413 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2415 def __init__(self, prefix, what, blurb, pydoc):
2416 self._prefix = prefix
2417 self._what = what
2418 self._genc = QAPIGenC(self._prefix + self._what + '.c',
2419 blurb, pydoc)
2420 self._genh = QAPIGenH(self._prefix + self._what + '.h',
2421 blurb, pydoc)
2423 def write(self, output_dir):
2424 self._genc.write(output_dir)
2425 self._genh.write(output_dir)
2428 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2430 def __init__(self, prefix, what, blurb, pydoc):
2431 self._prefix = prefix
2432 self._what = what
2433 self._blurb = blurb
2434 self._pydoc = pydoc
2435 self._genc = None
2436 self._genh = None
2437 self._module = {}
2438 self._main_module = None
2440 @staticmethod
2441 def _is_user_module(name):
2442 return name and not name.startswith('./')
2444 @staticmethod
2445 def _is_builtin_module(name):
2446 return not name
2448 def _module_dirname(self, what, name):
2449 if self._is_user_module(name):
2450 return os.path.dirname(name)
2451 return ''
2453 def _module_basename(self, what, name):
2454 ret = '' if self._is_builtin_module(name) else self._prefix
2455 if self._is_user_module(name):
2456 basename = os.path.basename(name)
2457 ret += what
2458 if name != self._main_module:
2459 ret += '-' + os.path.splitext(basename)[0]
2460 else:
2461 name = name[2:] if name else 'builtin'
2462 ret += re.sub(r'-', '-' + name + '-', what)
2463 return ret
2465 def _module_filename(self, what, name):
2466 return os.path.join(self._module_dirname(what, name),
2467 self._module_basename(what, name))
2469 def _add_module(self, name, blurb):
2470 basename = self._module_filename(self._what, name)
2471 genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
2472 genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
2473 self._module[name] = (genc, genh)
2474 self._set_module(name)
2476 def _add_user_module(self, name, blurb):
2477 assert self._is_user_module(name)
2478 if self._main_module is None:
2479 self._main_module = name
2480 self._add_module(name, blurb)
2482 def _add_system_module(self, name, blurb):
2483 self._add_module(name and './' + name, blurb)
2485 def _set_module(self, name):
2486 self._genc, self._genh = self._module[name]
2488 def write(self, output_dir, opt_builtins=False):
2489 for name in self._module:
2490 if self._is_builtin_module(name) and not opt_builtins:
2491 continue
2492 (genc, genh) = self._module[name]
2493 genc.write(output_dir)
2494 genh.write(output_dir)
2496 def _begin_user_module(self, name):
2497 pass
2499 def visit_module(self, name):
2500 if name in self._module:
2501 self._set_module(name)
2502 elif self._is_builtin_module(name):
2503 # The built-in module has not been created. No code may
2504 # be generated.
2505 self._genc = None
2506 self._genh = None
2507 else:
2508 self._add_user_module(name, self._blurb)
2509 self._begin_user_module(name)
2511 def visit_include(self, name, info):
2512 relname = os.path.relpath(self._module_filename(self._what, name),
2513 os.path.dirname(self._genh.fname))
2514 self._genh.preamble_add(mcgen('''
2515 #include "%(relname)s.h"
2516 ''',
2517 relname=relname))