4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2018 Red Hat Inc.
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
21 from collections
import OrderedDict
24 'null': 'QTYPE_QNULL',
25 'str': 'QTYPE_QSTRING',
27 'number': 'QTYPE_QNUM',
28 'bool': 'QTYPE_QBOOL',
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',
38 'any': None, # any QType possible, actually
39 'QType': 'QTYPE_QSTRING',
42 # Are documentation comments required?
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
= []
57 # Parsing the schema into expressions
61 def error_path(parent
):
64 res
= ('In file included from %s:%d:\n' % (parent
['file'],
65 parent
['line'])) + res
66 parent
= parent
['parent']
70 class QAPIError(Exception):
71 def __init__(self
, fname
, line
, col
, incl_info
, msg
):
72 Exception.__init
__(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
):
89 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
91 col
= (col
+ 7) % 8 + 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,
104 class QAPIDoc(object):
106 A documentation comment block, either expression or free-form
108 Expression documentation blocks consist of
110 * a body section: one line naming the expression, followed by an
111 overview (any number of lines)
113 * argument sections: a description of each argument (for commands
114 and events) or member (for structs, unions and alternates)
116 * features sections: a description of each feature flag
118 * additional (non-argument) sections, possibly tagged
120 Free-form documentation blocks consist only of a body section.
123 class Section(object):
124 def __init__(self
, name
=None):
125 # optional section name (argument/member or section name)
127 # the list of lines for this section
130 def append(self
, line
):
131 self
.text
+= line
.rstrip() + '\n'
133 class ArgSection(Section
):
134 def __init__(self
, name
):
135 QAPIDoc
.Section
.__init
__(self
, name
)
138 def connect(self
, member
):
141 def __init__(self
, parser
, info
):
142 # self._parser is used to report errors with QAPIParseError. The
143 # resulting error position depends on the state of the parser.
144 # It happens to be the beginning of the comment. More or less
145 # servicable, but action at a distance.
146 self
._parser
= parser
149 self
.body
= QAPIDoc
.Section()
150 # dict mapping parameter name to ArgSection
151 self
.args
= OrderedDict()
152 self
.features
= OrderedDict()
155 # the current section
156 self
._section
= self
.body
157 self
._append
_line
= self
._append
_body
_line
159 def has_section(self
, name
):
160 """Return True if we have a section with this name."""
161 for i
in self
.sections
:
166 def append(self
, line
):
168 Parse a comment line and add it to the documentation.
170 The way that the line is dealt with depends on which part of
171 the documentation we're parsing right now:
172 * The body section: ._append_line is ._append_body_line
173 * An argument section: ._append_line is ._append_args_line
174 * A features section: ._append_line is ._append_features_line
175 * An additional section: ._append_line is ._append_various_line
179 self
._append
_freeform
(line
)
183 raise QAPIParseError(self
._parser
, "Missing space after #")
185 self
._append
_line
(line
)
187 def end_comment(self
):
191 def _is_section_tag(name
):
192 return name
in ('Returns:', 'Since:',
193 # those are often singular or plural
195 'Example:', 'Examples:',
198 def _append_body_line(self
, line
):
200 Process a line of documentation text in the body section.
202 If this a symbol line and it is the section's first line, this
203 is an expression documentation block for that symbol.
205 If it's an expression documentation block, another symbol line
206 begins the argument section for the argument named by it, and
207 a section tag begins an additional section. Start that
208 section and append the line to it.
210 Else, append the line to the current section.
212 name
= line
.split(' ', 1)[0]
213 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
214 # recognized, and get silently treated as ordinary text
215 if not self
.symbol
and not self
.body
.text
and line
.startswith('@'):
216 if not line
.endswith(':'):
217 raise QAPIParseError(self
._parser
, "Line should end with :")
218 self
.symbol
= line
[1:-1]
219 # FIXME invalid names other than the empty string aren't flagged
221 raise QAPIParseError(self
._parser
, "Invalid name")
223 # This is an expression documentation block
224 if name
.startswith('@') and name
.endswith(':'):
225 self
._append
_line
= self
._append
_args
_line
226 self
._append
_args
_line
(line
)
227 elif line
== 'Features:':
228 self
._append
_line
= self
._append
_features
_line
229 elif self
._is
_section
_tag
(name
):
230 self
._append
_line
= self
._append
_various
_line
231 self
._append
_various
_line
(line
)
233 self
._append
_freeform
(line
.strip())
235 # This is a free-form documentation block
236 self
._append
_freeform
(line
.strip())
238 def _append_args_line(self
, line
):
240 Process a line of documentation text in an argument section.
242 A symbol line begins the next argument section, a section tag
243 section or a non-indented line after a blank line begins an
244 additional section. Start that section and append the line to
247 Else, append the line to the current section.
250 name
= line
.split(' ', 1)[0]
252 if name
.startswith('@') and name
.endswith(':'):
253 line
= line
[len(name
)+1:]
254 self
._start
_args
_section
(name
[1:-1])
255 elif self
._is
_section
_tag
(name
):
256 self
._append
_line
= self
._append
_various
_line
257 self
._append
_various
_line
(line
)
259 elif (self
._section
.text
.endswith('\n\n')
260 and line
and not line
[0].isspace()):
261 if line
== 'Features:':
262 self
._append
_line
= self
._append
_features
_line
264 self
._start
_section
()
265 self
._append
_line
= self
._append
_various
_line
266 self
._append
_various
_line
(line
)
269 self
._append
_freeform
(line
.strip())
271 def _append_features_line(self
, line
):
272 name
= line
.split(' ', 1)[0]
274 if name
.startswith('@') and name
.endswith(':'):
275 line
= line
[len(name
)+1:]
276 self
._start
_features
_section
(name
[1:-1])
277 elif self
._is
_section
_tag
(name
):
278 self
._append
_line
= self
._append
_various
_line
279 self
._append
_various
_line
(line
)
281 elif (self
._section
.text
.endswith('\n\n')
282 and line
and not line
[0].isspace()):
283 self
._start
_section
()
284 self
._append
_line
= self
._append
_various
_line
285 self
._append
_various
_line
(line
)
288 self
._append
_freeform
(line
.strip())
290 def _append_various_line(self
, line
):
292 Process a line of documentation text in an additional section.
294 A symbol line is an error.
296 A section tag begins an additional section. Start that
297 section and append the line to it.
299 Else, append the line to the current section.
301 name
= line
.split(' ', 1)[0]
303 if name
.startswith('@') and name
.endswith(':'):
304 raise QAPIParseError(self
._parser
,
305 "'%s' can't follow '%s' section"
306 % (name
, self
.sections
[0].name
))
307 elif self
._is
_section
_tag
(name
):
308 line
= line
[len(name
)+1:]
309 self
._start
_section
(name
[:-1])
311 if (not self
._section
.name
or
312 not self
._section
.name
.startswith('Example')):
315 self
._append
_freeform
(line
)
317 def _start_symbol_section(self
, symbols_dict
, name
):
318 # FIXME invalid names other than the empty string aren't flagged
320 raise QAPIParseError(self
._parser
, "Invalid parameter name")
321 if name
in symbols_dict
:
322 raise QAPIParseError(self
._parser
,
323 "'%s' parameter name duplicated" % name
)
324 assert not self
.sections
326 self
._section
= QAPIDoc
.ArgSection(name
)
327 symbols_dict
[name
] = self
._section
329 def _start_args_section(self
, name
):
330 self
._start
_symbol
_section
(self
.args
, name
)
332 def _start_features_section(self
, name
):
333 self
._start
_symbol
_section
(self
.features
, name
)
335 def _start_section(self
, name
=None):
336 if name
in ('Returns', 'Since') and self
.has_section(name
):
337 raise QAPIParseError(self
._parser
,
338 "Duplicated '%s' section" % name
)
340 self
._section
= QAPIDoc
.Section(name
)
341 self
.sections
.append(self
._section
)
343 def _end_section(self
):
345 text
= self
._section
.text
= self
._section
.text
.strip()
346 if self
._section
.name
and (not text
or text
.isspace()):
347 raise QAPIParseError(self
._parser
, "Empty doc section '%s'"
348 % self
._section
.name
)
351 def _append_freeform(self
, line
):
352 match
= re
.match(r
'(@\S+:)', line
)
354 raise QAPIParseError(self
._parser
,
355 "'%s' not allowed in free-form documentation"
357 self
._section
.append(line
)
359 def connect_member(self
, member
):
360 if member
.name
not in self
.args
:
361 # Undocumented TODO outlaw
362 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
363 self
.args
[member
.name
].connect(member
)
365 def check_expr(self
, expr
):
366 if self
.has_section('Returns') and 'command' not in expr
:
367 raise QAPISemError(self
.info
,
368 "'Returns:' is only valid for commands")
371 bogus
= [name
for name
, section
in self
.args
.items()
372 if not section
.member
]
376 "The following documented members are not in "
377 "the declaration: %s" % ", ".join(bogus
))
380 class QAPISchemaParser(object):
382 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
384 previously_included
.append(os
.path
.abspath(fp
.name
))
385 self
.incl_info
= incl_info
387 if self
.src
== '' or self
.src
[-1] != '\n':
397 while self
.tok
is not None:
398 info
= {'file': self
.fname
, 'line': self
.line
,
399 'parent': self
.incl_info
}
401 self
.reject_expr_doc(cur_doc
)
402 cur_doc
= self
.get_doc(info
)
403 self
.docs
.append(cur_doc
)
406 expr
= self
.get_expr(False)
407 if 'include' in expr
:
408 self
.reject_expr_doc(cur_doc
)
410 raise QAPISemError(info
, "Invalid 'include' directive")
411 include
= expr
['include']
412 if not isinstance(include
, str):
413 raise QAPISemError(info
,
414 "Value of 'include' must be a string")
415 incl_fname
= os
.path
.join(os
.path
.dirname(self
.fname
),
417 self
.exprs
.append({'expr': {'include': incl_fname
},
419 exprs_include
= self
._include
(include
, info
, incl_fname
,
422 self
.exprs
.extend(exprs_include
.exprs
)
423 self
.docs
.extend(exprs_include
.docs
)
424 elif "pragma" in expr
:
425 self
.reject_expr_doc(cur_doc
)
427 raise QAPISemError(info
, "Invalid 'pragma' directive")
428 pragma
= expr
['pragma']
429 if not isinstance(pragma
, dict):
431 info
, "Value of 'pragma' must be a dictionary")
432 for name
, value
in pragma
.items():
433 self
._pragma
(name
, value
, info
)
435 expr_elem
= {'expr': expr
,
438 if not cur_doc
.symbol
:
440 cur_doc
.info
, "Expression documentation required")
441 expr_elem
['doc'] = cur_doc
442 self
.exprs
.append(expr_elem
)
444 self
.reject_expr_doc(cur_doc
)
447 def reject_expr_doc(doc
):
448 if doc
and doc
.symbol
:
451 "Documentation for '%s' is not followed by the definition"
454 def _include(self
, include
, info
, incl_fname
, previously_included
):
455 incl_abs_fname
= os
.path
.abspath(incl_fname
)
456 # catch inclusion cycle
459 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
460 raise QAPISemError(info
, "Inclusion loop for %s" % include
)
463 # skip multiple include of the same file
464 if incl_abs_fname
in previously_included
:
468 if sys
.version_info
[0] >= 3:
469 fobj
= open(incl_fname
, 'r', encoding
='utf-8')
471 fobj
= open(incl_fname
, 'r')
473 raise QAPISemError(info
, '%s: %s' % (e
.strerror
, incl_fname
))
474 return QAPISchemaParser(fobj
, previously_included
, info
)
476 def _pragma(self
, name
, value
, info
):
477 global doc_required
, returns_whitelist
, name_case_whitelist
478 if name
== 'doc-required':
479 if not isinstance(value
, bool):
480 raise QAPISemError(info
,
481 "Pragma 'doc-required' must be boolean")
483 elif name
== 'returns-whitelist':
484 if (not isinstance(value
, list)
485 or any([not isinstance(elt
, str) for elt
in value
])):
486 raise QAPISemError(info
,
487 "Pragma returns-whitelist must be"
488 " a list of strings")
489 returns_whitelist
= value
490 elif name
== 'name-case-whitelist':
491 if (not isinstance(value
, list)
492 or any([not isinstance(elt
, str) for elt
in value
])):
493 raise QAPISemError(info
,
494 "Pragma name-case-whitelist must be"
495 " a list of strings")
496 name_case_whitelist
= value
498 raise QAPISemError(info
, "Unknown pragma '%s'" % name
)
500 def accept(self
, skip_comment
=True):
502 self
.tok
= self
.src
[self
.cursor
]
503 self
.pos
= self
.cursor
508 if self
.src
[self
.cursor
] == '#':
509 # Start of doc comment
511 self
.cursor
= self
.src
.find('\n', self
.cursor
)
513 self
.val
= self
.src
[self
.pos
:self
.cursor
]
515 elif self
.tok
in '{}:,[]':
517 elif self
.tok
== "'":
521 ch
= self
.src
[self
.cursor
]
524 raise QAPIParseError(self
, 'Missing terminating "\'"')
538 for _
in range(0, 4):
539 ch
= self
.src
[self
.cursor
]
541 if ch
not in '0123456789abcdefABCDEF':
542 raise QAPIParseError(self
,
543 '\\u escape needs 4 '
545 value
= (value
<< 4) + int(ch
, 16)
546 # If Python 2 and 3 didn't disagree so much on
547 # how to handle Unicode, then we could allow
548 # Unicode string defaults. But most of QAPI is
549 # ASCII-only, so we aren't losing much for now.
550 if not value
or value
> 0x7f:
551 raise QAPIParseError(self
,
552 'For now, \\u escape '
553 'only supports non-zero '
554 'values up to \\u007f')
559 raise QAPIParseError(self
,
560 "Unknown escape \\%s" % ch
)
569 elif self
.src
.startswith('true', self
.pos
):
573 elif self
.src
.startswith('false', self
.pos
):
577 elif self
.src
.startswith('null', self
.pos
):
581 elif self
.tok
== '\n':
582 if self
.cursor
== len(self
.src
):
586 self
.line_pos
= self
.cursor
587 elif not self
.tok
.isspace():
588 raise QAPIParseError(self
, 'Stray "%s"' % self
.tok
)
590 def get_members(self
):
596 raise QAPIParseError(self
, 'Expected string or "}"')
601 raise QAPIParseError(self
, 'Expected ":"')
604 raise QAPIParseError(self
, 'Duplicate key "%s"' % key
)
605 expr
[key
] = self
.get_expr(True)
610 raise QAPIParseError(self
, 'Expected "," or "}"')
613 raise QAPIParseError(self
, 'Expected string')
615 def get_values(self
):
620 if self
.tok
not in "{['tfn":
621 raise QAPIParseError(self
, 'Expected "{", "[", "]", string, '
624 expr
.append(self
.get_expr(True))
629 raise QAPIParseError(self
, 'Expected "," or "]"')
632 def get_expr(self
, nested
):
633 if self
.tok
!= '{' and not nested
:
634 raise QAPIParseError(self
, 'Expected "{"')
637 expr
= self
.get_members()
638 elif self
.tok
== '[':
640 expr
= self
.get_values()
641 elif self
.tok
in "'tfn":
645 raise QAPIParseError(self
, 'Expected "{", "[", string, '
649 def get_doc(self
, info
):
651 raise QAPIParseError(self
, "Junk after '##' at start of "
652 "documentation comment")
654 doc
= QAPIDoc(self
, info
)
656 while self
.tok
== '#':
657 if self
.val
.startswith('##'):
660 raise QAPIParseError(self
, "Junk after '##' at end of "
661 "documentation comment")
669 raise QAPIParseError(self
, "Documentation comment must end with '##'")
673 # Semantic analysis of schema expressions
674 # TODO fold into QAPISchema
675 # TODO catching name collisions in generated code would be nice
679 def find_base_members(base
):
680 if isinstance(base
, dict):
682 base_struct_define
= struct_types
.get(base
)
683 if not base_struct_define
:
685 return base_struct_define
['data']
688 # Return the qtype of an alternate branch, or None on error.
689 def find_alternate_member_qtype(qapi_type
):
690 if qapi_type
in builtin_types
:
691 return builtin_types
[qapi_type
]
692 elif qapi_type
in struct_types
:
694 elif qapi_type
in enum_types
:
695 return 'QTYPE_QSTRING'
696 elif qapi_type
in union_types
:
701 # Return the discriminator enum define if discriminator is specified as an
702 # enum type, otherwise return None.
703 def discriminator_find_enum_define(expr
):
704 base
= expr
.get('base')
705 discriminator
= expr
.get('discriminator')
707 if not (discriminator
and base
):
710 base_members
= find_base_members(base
)
714 discriminator_value
= base_members
.get(discriminator
)
715 if not discriminator_value
:
718 return enum_types
.get(discriminator_value
['type'])
721 # Names must be letters, numbers, -, and _. They must start with letter,
722 # except for downstream extensions which must start with __RFQDN_.
723 # Dots are only valid in the downstream extension prefix.
724 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
725 '[a-zA-Z][a-zA-Z0-9_-]*$')
728 def check_name(info
, source
, name
, allow_optional
=False,
733 if not isinstance(name
, str):
734 raise QAPISemError(info
, "%s requires a string name" % source
)
735 if name
.startswith('*'):
736 membername
= name
[1:]
737 if not allow_optional
:
738 raise QAPISemError(info
, "%s does not allow optional name '%s'"
740 # Enum members can start with a digit, because the generated C
741 # code always prefixes it with the enum name
742 if enum_member
and membername
[0].isdigit():
743 membername
= 'D' + membername
744 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
745 # and 'q_obj_*' implicit type names.
746 if not valid_name
.match(membername
) or \
747 c_name(membername
, False).startswith('q_'):
748 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
751 def add_name(name
, info
, meta
, implicit
=False):
753 check_name(info
, "'%s'" % meta
, name
)
754 # FIXME should reject names that differ only in '_' vs. '.'
755 # vs. '-', because they're liable to clash in generated C.
756 if name
in all_names
:
757 raise QAPISemError(info
, "%s '%s' is already defined"
758 % (all_names
[name
], name
))
759 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
760 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
761 % (meta
, name
, name
[-4:]))
762 all_names
[name
] = meta
765 def check_if(expr
, info
):
767 def check_if_str(ifcond
, info
):
768 if not isinstance(ifcond
, str):
770 info
, "'if' condition must be a string or a list of strings")
772 raise QAPISemError(info
, "'if' condition '' makes no sense")
774 ifcond
= expr
.get('if')
777 if isinstance(ifcond
, list):
779 raise QAPISemError(info
, "'if' condition [] is useless")
781 check_if_str(elt
, info
)
783 check_if_str(ifcond
, info
)
786 def check_type(info
, source
, value
, allow_array
=False,
787 allow_dict
=False, allow_optional
=False,
794 # Check if array type for value is okay
795 if isinstance(value
, list):
797 raise QAPISemError(info
, "%s cannot be an array" % source
)
798 if len(value
) != 1 or not isinstance(value
[0], str):
799 raise QAPISemError(info
,
800 "%s: array type must contain single type name" %
804 # Check if type name for value is okay
805 if isinstance(value
, str):
806 if value
not in all_names
:
807 raise QAPISemError(info
, "%s uses unknown type '%s'"
809 if not all_names
[value
] in allow_metas
:
810 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
811 (source
, all_names
[value
], value
))
815 raise QAPISemError(info
, "%s should be a type name" % source
)
817 if not isinstance(value
, OrderedDict
):
818 raise QAPISemError(info
,
819 "%s should be a dictionary or type name" % source
)
821 # value is a dictionary, check that each member is okay
822 for (key
, arg
) in value
.items():
823 check_name(info
, "Member of %s" % source
, key
,
824 allow_optional
=allow_optional
)
825 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
826 raise QAPISemError(info
, "Member of %s uses reserved name '%s'"
828 # Todo: allow dictionaries to represent default values of
829 # an optional argument.
830 check_known_keys(info
, "member '%s' of %s" % (key
, source
),
831 arg
, ['type'], ['if'])
832 check_type(info
, "Member '%s' of %s" % (key
, source
),
833 arg
['type'], allow_array
=True,
834 allow_metas
=['built-in', 'union', 'alternate', 'struct',
838 def check_command(expr
, info
):
839 name
= expr
['command']
840 boxed
= expr
.get('boxed', False)
842 args_meta
= ['struct']
844 args_meta
+= ['union', 'alternate']
845 check_type(info
, "'data' for command '%s'" % name
,
846 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
847 allow_metas
=args_meta
)
848 returns_meta
= ['union', 'struct']
849 if name
in returns_whitelist
:
850 returns_meta
+= ['built-in', 'alternate', 'enum']
851 check_type(info
, "'returns' for command '%s'" % name
,
852 expr
.get('returns'), allow_array
=True,
853 allow_optional
=True, allow_metas
=returns_meta
)
856 def check_event(expr
, info
):
858 boxed
= expr
.get('boxed', False)
862 meta
+= ['union', 'alternate']
863 check_type(info
, "'data' for event '%s'" % name
,
864 expr
.get('data'), allow_dict
=not boxed
, allow_optional
=True,
868 def enum_get_names(expr
):
869 return [e
['name'] for e
in expr
['data']]
872 def check_union(expr
, info
):
874 base
= expr
.get('base')
875 discriminator
= expr
.get('discriminator')
876 members
= expr
['data']
878 # Two types of unions, determined by discriminator.
880 # With no discriminator it is a simple union.
881 if discriminator
is None:
883 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
885 raise QAPISemError(info
, "Simple union '%s' must not have a base" %
888 # Else, it's a flat union.
890 # The object must have a string or dictionary 'base'.
891 check_type(info
, "'base' for union '%s'" % name
,
892 base
, allow_dict
=True, allow_optional
=True,
893 allow_metas
=['struct'])
895 raise QAPISemError(info
, "Flat union '%s' must have a base"
897 base_members
= find_base_members(base
)
898 assert base_members
is not None
900 # The value of member 'discriminator' must name a non-optional
901 # member of the base struct.
902 check_name(info
, "Discriminator of flat union '%s'" % name
,
904 discriminator_value
= base_members
.get(discriminator
)
905 if not discriminator_value
:
906 raise QAPISemError(info
,
907 "Discriminator '%s' is not a member of base "
909 % (discriminator
, base
))
910 if discriminator_value
.get('if'):
911 raise QAPISemError(info
, 'The discriminator %s.%s for union %s '
912 'must not be conditional' %
913 (base
, discriminator
, name
))
914 enum_define
= enum_types
.get(discriminator_value
['type'])
915 allow_metas
= ['struct']
916 # Do not allow string discriminator
918 raise QAPISemError(info
,
919 "Discriminator '%s' must be of enumeration "
920 "type" % discriminator
)
922 # Check every branch; don't allow an empty union
923 if len(members
) == 0:
924 raise QAPISemError(info
, "Union '%s' cannot have empty 'data'" % name
)
925 for (key
, value
) in members
.items():
926 check_name(info
, "Member of union '%s'" % name
, key
)
928 check_known_keys(info
, "member '%s' of union '%s'" % (key
, name
),
929 value
, ['type'], ['if'])
930 # Each value must name a known type
931 check_type(info
, "Member '%s' of union '%s'" % (key
, name
),
933 allow_array
=not base
, allow_metas
=allow_metas
)
935 # If the discriminator names an enum type, then all members
936 # of 'data' must also be members of the enum type.
938 if key
not in enum_get_names(enum_define
):
939 raise QAPISemError(info
,
940 "Discriminator value '%s' is not found in "
942 % (key
, enum_define
['enum']))
945 def check_alternate(expr
, info
):
946 name
= expr
['alternate']
947 members
= expr
['data']
950 # Check every branch; require at least two branches
952 raise QAPISemError(info
,
953 "Alternate '%s' should have at least two branches "
955 for (key
, value
) in members
.items():
956 check_name(info
, "Member of alternate '%s'" % name
, key
)
957 check_known_keys(info
,
958 "member '%s' of alternate '%s'" % (key
, name
),
959 value
, ['type'], ['if'])
962 # Ensure alternates have no type conflicts.
963 check_type(info
, "Member '%s' of alternate '%s'" % (key
, name
), typ
,
964 allow_metas
=['built-in', 'union', 'struct', 'enum'])
965 qtype
= find_alternate_member_qtype(typ
)
967 raise QAPISemError(info
, "Alternate '%s' member '%s' cannot use "
968 "type '%s'" % (name
, key
, typ
))
969 conflicting
= set([qtype
])
970 if qtype
== 'QTYPE_QSTRING':
971 enum_expr
= enum_types
.get(typ
)
973 for v
in enum_get_names(enum_expr
):
974 if v
in ['on', 'off']:
975 conflicting
.add('QTYPE_QBOOL')
976 if re
.match(r
'[-+0-9.]', v
): # lazy, could be tightened
977 conflicting
.add('QTYPE_QNUM')
979 conflicting
.add('QTYPE_QNUM')
980 conflicting
.add('QTYPE_QBOOL')
981 for qt
in conflicting
:
983 raise QAPISemError(info
, "Alternate '%s' member '%s' can't "
984 "be distinguished from member '%s'"
985 % (name
, key
, types_seen
[qt
]))
989 def check_enum(expr
, info
):
991 members
= expr
['data']
992 prefix
= expr
.get('prefix')
994 if not isinstance(members
, list):
995 raise QAPISemError(info
,
996 "Enum '%s' requires an array for 'data'" % name
)
997 if prefix
is not None and not isinstance(prefix
, str):
998 raise QAPISemError(info
,
999 "Enum '%s' requires a string for 'prefix'" % name
)
1001 for member
in members
:
1002 source
= "dictionary member of enum '%s'" % name
1003 check_known_keys(info
, source
, member
, ['name'], ['if'])
1004 check_if(member
, info
)
1005 check_name(info
, "Member of enum '%s'" % name
, member
['name'],
1009 def check_struct(expr
, info
):
1010 name
= expr
['struct']
1011 members
= expr
['data']
1012 features
= expr
.get('features')
1014 check_type(info
, "'data' for struct '%s'" % name
, members
,
1015 allow_dict
=True, allow_optional
=True)
1016 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
1017 allow_metas
=['struct'])
1020 if not isinstance(features
, list):
1021 raise QAPISemError(info
,
1022 "Struct '%s' requires an array for 'features'" %
1025 assert isinstance(f
, dict)
1026 check_known_keys(info
, "feature of struct %s" % name
, f
,
1030 check_name(info
, "Feature of struct %s" % name
, f
['name'])
1033 def check_known_keys(info
, source
, keys
, required
, optional
):
1036 return ', '.join("'" + e
+ "'" for e
in sorted(elems
))
1038 missing
= set(required
) - set(keys
)
1040 raise QAPISemError(info
, "Key%s %s %s missing from %s"
1041 % ('s' if len(missing
) > 1 else '', pprint(missing
),
1042 'are' if len(missing
) > 1 else 'is', source
))
1043 allowed
= set(required
+ optional
)
1044 unknown
= set(keys
) - allowed
1046 raise QAPISemError(info
, "Unknown key%s %s in %s\nValid keys are %s."
1047 % ('s' if len(unknown
) > 1 else '', pprint(unknown
),
1048 source
, pprint(allowed
)))
1051 def check_keys(expr_elem
, meta
, required
, optional
=[]):
1052 expr
= expr_elem
['expr']
1053 info
= expr_elem
['info']
1055 if not isinstance(name
, str):
1056 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
1057 required
= required
+ [meta
]
1058 source
= "%s '%s'" % (meta
, name
)
1059 check_known_keys(info
, source
, expr
.keys(), required
, optional
)
1060 for (key
, value
) in expr
.items():
1061 if key
in ['gen', 'success-response'] and value
is not False:
1062 raise QAPISemError(info
,
1063 "'%s' of %s '%s' should only use false value"
1064 % (key
, meta
, name
))
1065 if (key
in ['boxed', 'allow-oob', 'allow-preconfig']
1066 and value
is not True):
1067 raise QAPISemError(info
,
1068 "'%s' of %s '%s' should only use true value"
1069 % (key
, meta
, name
))
1071 check_if(expr
, info
)
1074 def normalize_enum(expr
):
1075 if isinstance(expr
['data'], list):
1076 expr
['data'] = [m
if isinstance(m
, dict) else {'name': m
}
1077 for m
in expr
['data']]
1080 def normalize_members(members
):
1081 if isinstance(members
, OrderedDict
):
1082 for key
, arg
in members
.items():
1083 if isinstance(arg
, dict):
1085 members
[key
] = {'type': arg
}
1088 def normalize_features(features
):
1089 if isinstance(features
, list):
1090 features
[:] = [f
if isinstance(f
, dict) else {'name': f
}
1094 def check_exprs(exprs
):
1097 # Populate name table with names of built-in types
1098 for builtin
in builtin_types
.keys():
1099 all_names
[builtin
] = 'built-in'
1101 # Learn the types and check for valid expression keys
1102 for expr_elem
in exprs
:
1103 expr
= expr_elem
['expr']
1104 info
= expr_elem
['info']
1105 doc
= expr_elem
.get('doc')
1107 if 'include' in expr
:
1110 if not doc
and doc_required
:
1111 raise QAPISemError(info
,
1112 "Expression missing documentation comment")
1116 check_keys(expr_elem
, 'enum', ['data'], ['if', 'prefix'])
1117 normalize_enum(expr
)
1118 enum_types
[expr
[meta
]] = expr
1119 elif 'union' in expr
:
1121 check_keys(expr_elem
, 'union', ['data'],
1122 ['base', 'discriminator', 'if'])
1123 normalize_members(expr
.get('base'))
1124 normalize_members(expr
['data'])
1125 union_types
[expr
[meta
]] = expr
1126 elif 'alternate' in expr
:
1128 check_keys(expr_elem
, 'alternate', ['data'], ['if'])
1129 normalize_members(expr
['data'])
1130 elif 'struct' in expr
:
1132 check_keys(expr_elem
, 'struct', ['data'],
1133 ['base', 'if', 'features'])
1134 normalize_members(expr
['data'])
1135 normalize_features(expr
.get('features'))
1136 struct_types
[expr
[meta
]] = expr
1137 elif 'command' in expr
:
1139 check_keys(expr_elem
, 'command', [],
1140 ['data', 'returns', 'gen', 'success-response',
1141 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
1142 normalize_members(expr
.get('data'))
1143 elif 'event' in expr
:
1145 check_keys(expr_elem
, 'event', [], ['data', 'boxed', 'if'])
1146 normalize_members(expr
.get('data'))
1148 raise QAPISemError(expr_elem
['info'],
1149 "Expression is missing metatype")
1151 add_name(name
, info
, meta
)
1152 if doc
and doc
.symbol
!= name
:
1153 raise QAPISemError(info
, "Definition of '%s' follows documentation"
1154 " for '%s'" % (name
, doc
.symbol
))
1156 # Try again for hidden UnionKind enum
1157 for expr_elem
in exprs
:
1158 expr
= expr_elem
['expr']
1160 if 'include' in expr
:
1162 if 'union' in expr
and not discriminator_find_enum_define(expr
):
1163 name
= '%sKind' % expr
['union']
1164 elif 'alternate' in expr
:
1165 name
= '%sKind' % expr
['alternate']
1168 enum_types
[name
] = {'enum': name
}
1169 add_name(name
, info
, 'enum', implicit
=True)
1171 # Validate that exprs make sense
1172 for expr_elem
in exprs
:
1173 expr
= expr_elem
['expr']
1174 info
= expr_elem
['info']
1175 doc
= expr_elem
.get('doc')
1177 if 'include' in expr
:
1180 check_enum(expr
, info
)
1181 elif 'union' in expr
:
1182 check_union(expr
, info
)
1183 elif 'alternate' in expr
:
1184 check_alternate(expr
, info
)
1185 elif 'struct' in expr
:
1186 check_struct(expr
, info
)
1187 elif 'command' in expr
:
1188 check_command(expr
, info
)
1189 elif 'event' in expr
:
1190 check_event(expr
, info
)
1192 assert False, 'unexpected meta type'
1195 doc
.check_expr(expr
)
1201 # Schema compiler frontend
1204 def listify_cond(ifcond
):
1207 if not isinstance(ifcond
, list):
1212 class QAPISchemaEntity(object):
1213 def __init__(self
, name
, info
, doc
, ifcond
=None):
1214 assert name
is None or isinstance(name
, str)
1217 # For explicitly defined entities, info points to the (explicit)
1218 # definition. For builtins (and their arrays), info is None.
1219 # For implicitly defined entities, info points to a place that
1220 # triggered the implicit definition (there may be more than one
1224 self
._ifcond
= ifcond
# self.ifcond is set only after .check()
1227 return c_name(self
.name
)
1229 def check(self
, schema
):
1230 if isinstance(self
._ifcond
, QAPISchemaType
):
1231 # inherit the condition from a type
1234 self
.ifcond
= typ
.ifcond
1236 self
.ifcond
= listify_cond(self
._ifcond
)
1238 self
.module
= os
.path
.relpath(self
.info
['file'],
1239 os
.path
.dirname(schema
.fname
))
1241 def is_implicit(self
):
1242 return not self
.info
1244 def visit(self
, visitor
):
1248 class QAPISchemaVisitor(object):
1249 def visit_begin(self
, schema
):
1252 def visit_end(self
):
1255 def visit_module(self
, fname
):
1258 def visit_needed(self
, entity
):
1259 # Default to visiting everything
1262 def visit_include(self
, fname
, info
):
1265 def visit_builtin_type(self
, name
, info
, json_type
):
1268 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
1271 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
1274 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
,
1278 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
1282 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
1285 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
1286 success_response
, boxed
, allow_oob
, allow_preconfig
):
1289 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
1293 class QAPISchemaInclude(QAPISchemaEntity
):
1295 def __init__(self
, fname
, info
):
1296 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1299 def visit(self
, visitor
):
1300 visitor
.visit_include(self
.fname
, self
.info
)
1303 class QAPISchemaType(QAPISchemaEntity
):
1304 # Return the C type for common use.
1305 # For the types we commonly box, this is a pointer type.
1309 # Return the C type to be used in a parameter list.
1310 def c_param_type(self
):
1311 return self
.c_type()
1313 # Return the C type to be used where we suppress boxing.
1314 def c_unboxed_type(self
):
1315 return self
.c_type()
1317 def json_type(self
):
1320 def alternate_qtype(self
):
1322 'null': 'QTYPE_QNULL',
1323 'string': 'QTYPE_QSTRING',
1324 'number': 'QTYPE_QNUM',
1325 'int': 'QTYPE_QNUM',
1326 'boolean': 'QTYPE_QBOOL',
1327 'object': 'QTYPE_QDICT'
1329 return json2qtype
.get(self
.json_type())
1332 if self
.is_implicit():
1337 class QAPISchemaBuiltinType(QAPISchemaType
):
1338 def __init__(self
, name
, json_type
, c_type
):
1339 QAPISchemaType
.__init
__(self
, name
, None, None)
1340 assert not c_type
or isinstance(c_type
, str)
1341 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1343 self
._json
_type
_name
= json_type
1344 self
._c
_type
_name
= c_type
1350 return self
._c
_type
_name
1352 def c_param_type(self
):
1353 if self
.name
== 'str':
1354 return 'const ' + self
._c
_type
_name
1355 return self
._c
_type
_name
1357 def json_type(self
):
1358 return self
._json
_type
_name
1361 return self
.json_type()
1363 def visit(self
, visitor
):
1364 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1367 class QAPISchemaEnumType(QAPISchemaType
):
1368 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
1369 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1371 assert isinstance(m
, QAPISchemaMember
)
1373 assert prefix
is None or isinstance(prefix
, str)
1374 self
.members
= members
1375 self
.prefix
= prefix
1377 def check(self
, schema
):
1378 QAPISchemaType
.check(self
, schema
)
1380 for m
in self
.members
:
1381 m
.check_clash(self
.info
, seen
)
1383 self
.doc
.connect_member(m
)
1385 def is_implicit(self
):
1386 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1387 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1390 return c_name(self
.name
)
1392 def member_names(self
):
1393 return [m
.name
for m
in self
.members
]
1395 def json_type(self
):
1398 def visit(self
, visitor
):
1399 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
1400 self
.members
, self
.prefix
)
1403 class QAPISchemaArrayType(QAPISchemaType
):
1404 def __init__(self
, name
, info
, element_type
):
1405 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
1406 assert isinstance(element_type
, str)
1407 self
._element
_type
_name
= element_type
1408 self
.element_type
= None
1410 def check(self
, schema
):
1411 QAPISchemaType
.check(self
, schema
)
1412 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1413 assert self
.element_type
1414 self
.element_type
.check(schema
)
1415 self
.module
= self
.element_type
.module
1416 self
.ifcond
= self
.element_type
.ifcond
1418 def is_implicit(self
):
1422 return c_name(self
.name
) + pointer_suffix
1424 def json_type(self
):
1428 elt_doc_type
= self
.element_type
.doc_type()
1429 if not elt_doc_type
:
1431 return 'array of ' + elt_doc_type
1433 def visit(self
, visitor
):
1434 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
1438 class QAPISchemaObjectType(QAPISchemaType
):
1439 def __init__(self
, name
, info
, doc
, ifcond
,
1440 base
, local_members
, variants
, features
):
1441 # struct has local_members, optional base, and no variants
1442 # flat union has base, variants, and no local_members
1443 # simple union has local_members, variants, and no base
1444 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1445 assert base
is None or isinstance(base
, str)
1446 for m
in local_members
:
1447 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1449 if variants
is not None:
1450 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1451 variants
.set_owner(name
)
1453 assert isinstance(f
, QAPISchemaFeature
)
1455 self
._base
_name
= base
1457 self
.local_members
= local_members
1458 self
.variants
= variants
1460 self
.features
= features
1462 def check(self
, schema
):
1463 QAPISchemaType
.check(self
, schema
)
1464 if self
.members
is False: # check for cycles
1465 raise QAPISemError(self
.info
,
1466 "Object %s contains itself" % self
.name
)
1469 self
.members
= False # mark as being checked
1470 seen
= OrderedDict()
1472 self
.base
= schema
.lookup_type(self
._base
_name
)
1473 assert isinstance(self
.base
, QAPISchemaObjectType
)
1474 self
.base
.check(schema
)
1475 self
.base
.check_clash(self
.info
, seen
)
1476 for m
in self
.local_members
:
1478 m
.check_clash(self
.info
, seen
)
1480 self
.doc
.connect_member(m
)
1481 self
.members
= seen
.values()
1483 self
.variants
.check(schema
, seen
)
1484 assert self
.variants
.tag_member
in self
.members
1485 self
.variants
.check_clash(self
.info
, seen
)
1487 # Features are in a name space separate from members
1489 for f
in self
.features
:
1490 f
.check_clash(self
.info
, seen
)
1495 # Check that the members of this type do not cause duplicate JSON members,
1496 # and update seen to track the members seen so far. Report any errors
1497 # on behalf of info, which is not necessarily self.info
1498 def check_clash(self
, info
, seen
):
1499 assert not self
.variants
# not implemented
1500 for m
in self
.members
:
1501 m
.check_clash(info
, seen
)
1503 def is_implicit(self
):
1504 # See QAPISchema._make_implicit_object_type(), as well as
1505 # _def_predefineds()
1506 return self
.name
.startswith('q_')
1509 assert self
.members
is not None
1510 return not self
.members
and not self
.variants
1513 assert self
.name
!= 'q_empty'
1514 return QAPISchemaType
.c_name(self
)
1517 assert not self
.is_implicit()
1518 return c_name(self
.name
) + pointer_suffix
1520 def c_unboxed_type(self
):
1521 return c_name(self
.name
)
1523 def json_type(self
):
1526 def visit(self
, visitor
):
1527 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
1528 self
.base
, self
.local_members
, self
.variants
,
1530 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
1531 self
.members
, self
.variants
,
1535 class QAPISchemaMember(object):
1536 """ Represents object members, enum members and features """
1539 def __init__(self
, name
, ifcond
=None):
1540 assert isinstance(name
, str)
1542 self
.ifcond
= listify_cond(ifcond
)
1545 def set_owner(self
, name
):
1546 assert not self
.owner
1549 def check_clash(self
, info
, seen
):
1550 cname
= c_name(self
.name
)
1551 if cname
.lower() != cname
and self
.owner
not in name_case_whitelist
:
1552 raise QAPISemError(info
,
1553 "%s should not use uppercase" % self
.describe())
1555 raise QAPISemError(info
, "%s collides with %s" %
1556 (self
.describe(), seen
[cname
].describe()))
1559 def _pretty_owner(self
):
1561 if owner
.startswith('q_obj_'):
1562 # See QAPISchema._make_implicit_object_type() - reverse the
1563 # mapping there to create a nice human-readable description
1565 if owner
.endswith('-arg'):
1566 return '(parameter of %s)' % owner
[:-4]
1567 elif owner
.endswith('-base'):
1568 return '(base of %s)' % owner
[:-5]
1570 assert owner
.endswith('-wrapper')
1571 # Unreachable and not implemented
1573 if owner
.endswith('Kind'):
1574 # See QAPISchema._make_implicit_enum_type()
1575 return '(branch of %s)' % owner
[:-4]
1576 return '(%s of %s)' % (self
.role
, owner
)
1579 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1582 class QAPISchemaFeature(QAPISchemaMember
):
1586 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1587 def __init__(self
, name
, typ
, optional
, ifcond
=None):
1588 QAPISchemaMember
.__init
__(self
, name
, ifcond
)
1589 assert isinstance(typ
, str)
1590 assert isinstance(optional
, bool)
1591 self
._type
_name
= typ
1593 self
.optional
= optional
1595 def check(self
, schema
):
1597 self
.type = schema
.lookup_type(self
._type
_name
)
1601 class QAPISchemaObjectTypeVariants(object):
1602 def __init__(self
, tag_name
, tag_member
, variants
):
1603 # Flat unions pass tag_name but not tag_member.
1604 # Simple unions and alternates pass tag_member but not tag_name.
1605 # After check(), tag_member is always set, and tag_name remains
1606 # a reliable witness of being used by a flat union.
1607 assert bool(tag_member
) != bool(tag_name
)
1608 assert (isinstance(tag_name
, str) or
1609 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1610 assert len(variants
) > 0
1612 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1613 self
._tag
_name
= tag_name
1614 self
.tag_member
= tag_member
1615 self
.variants
= variants
1617 def set_owner(self
, name
):
1618 for v
in self
.variants
:
1621 def check(self
, schema
, seen
):
1622 if not self
.tag_member
: # flat union
1623 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1624 assert self
._tag
_name
== self
.tag_member
.name
1625 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1626 if self
._tag
_name
: # flat union
1627 # branches that are not explicitly covered get an empty type
1628 cases
= set([v
.name
for v
in self
.variants
])
1629 for m
in self
.tag_member
.type.members
:
1630 if m
.name
not in cases
:
1631 v
= QAPISchemaObjectTypeVariant(m
.name
, 'q_empty',
1633 v
.set_owner(self
.tag_member
.owner
)
1634 self
.variants
.append(v
)
1635 for v
in self
.variants
:
1637 # Union names must match enum values; alternate names are
1638 # checked separately. Use 'seen' to tell the two apart.
1640 assert v
.name
in self
.tag_member
.type.member_names()
1641 assert isinstance(v
.type, QAPISchemaObjectType
)
1642 v
.type.check(schema
)
1644 def check_clash(self
, info
, seen
):
1645 for v
in self
.variants
:
1646 # Reset seen map for each variant, since qapi names from one
1647 # branch do not affect another branch
1648 assert isinstance(v
.type, QAPISchemaObjectType
)
1649 v
.type.check_clash(info
, dict(seen
))
1652 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1655 def __init__(self
, name
, typ
, ifcond
=None):
1656 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False, ifcond
)
1659 class QAPISchemaAlternateType(QAPISchemaType
):
1660 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
1661 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1662 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1663 assert variants
.tag_member
1664 variants
.set_owner(name
)
1665 variants
.tag_member
.set_owner(self
.name
)
1666 self
.variants
= variants
1668 def check(self
, schema
):
1669 QAPISchemaType
.check(self
, schema
)
1670 self
.variants
.tag_member
.check(schema
)
1671 # Not calling self.variants.check_clash(), because there's nothing
1673 self
.variants
.check(schema
, {})
1674 # Alternate branch names have no relation to the tag enum values;
1675 # so we have to check for potential name collisions ourselves.
1677 for v
in self
.variants
.variants
:
1678 v
.check_clash(self
.info
, seen
)
1680 self
.doc
.connect_member(v
)
1685 return c_name(self
.name
) + pointer_suffix
1687 def json_type(self
):
1690 def visit(self
, visitor
):
1691 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
1698 class QAPISchemaCommand(QAPISchemaEntity
):
1699 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
1700 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
1701 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1702 assert not arg_type
or isinstance(arg_type
, str)
1703 assert not ret_type
or isinstance(ret_type
, str)
1704 self
._arg
_type
_name
= arg_type
1705 self
.arg_type
= None
1706 self
._ret
_type
_name
= ret_type
1707 self
.ret_type
= None
1709 self
.success_response
= success_response
1711 self
.allow_oob
= allow_oob
1712 self
.allow_preconfig
= allow_preconfig
1714 def check(self
, schema
):
1715 QAPISchemaEntity
.check(self
, schema
)
1716 if self
._arg
_type
_name
:
1717 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1718 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1719 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1720 self
.arg_type
.check(schema
)
1722 if self
.arg_type
.is_empty():
1723 raise QAPISemError(self
.info
,
1724 "Cannot use 'boxed' with empty type")
1726 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1727 assert not self
.arg_type
.variants
1729 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1730 if self
._ret
_type
_name
:
1731 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1732 assert isinstance(self
.ret_type
, QAPISchemaType
)
1734 def visit(self
, visitor
):
1735 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
1736 self
.arg_type
, self
.ret_type
,
1737 self
.gen
, self
.success_response
,
1738 self
.boxed
, self
.allow_oob
,
1739 self
.allow_preconfig
)
1742 class QAPISchemaEvent(QAPISchemaEntity
):
1743 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
1744 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1745 assert not arg_type
or isinstance(arg_type
, str)
1746 self
._arg
_type
_name
= arg_type
1747 self
.arg_type
= None
1750 def check(self
, schema
):
1751 QAPISchemaEntity
.check(self
, schema
)
1752 if self
._arg
_type
_name
:
1753 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1754 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1755 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1756 self
.arg_type
.check(schema
)
1758 if self
.arg_type
.is_empty():
1759 raise QAPISemError(self
.info
,
1760 "Cannot use 'boxed' with empty type")
1762 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1763 assert not self
.arg_type
.variants
1765 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1767 def visit(self
, visitor
):
1768 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
1769 self
.arg_type
, self
.boxed
)
1772 class QAPISchema(object):
1773 def __init__(self
, fname
):
1775 if sys
.version_info
[0] >= 3:
1776 f
= open(fname
, 'r', encoding
='utf-8')
1778 f
= open(fname
, 'r')
1779 parser
= QAPISchemaParser(f
)
1780 exprs
= check_exprs(parser
.exprs
)
1781 self
.docs
= parser
.docs
1782 self
._entity
_list
= []
1783 self
._entity
_dict
= {}
1784 self
._predefining
= True
1785 self
._def
_predefineds
()
1786 self
._predefining
= False
1787 self
._def
_exprs
(exprs
)
1790 def _def_entity(self
, ent
):
1791 # Only the predefined types are allowed to not have info
1792 assert ent
.info
or self
._predefining
1793 assert ent
.name
is None or ent
.name
not in self
._entity
_dict
1794 self
._entity
_list
.append(ent
)
1795 if ent
.name
is not None:
1796 self
._entity
_dict
[ent
.name
] = ent
1798 def lookup_entity(self
, name
, typ
=None):
1799 ent
= self
._entity
_dict
.get(name
)
1800 if typ
and not isinstance(ent
, typ
):
1804 def lookup_type(self
, name
):
1805 return self
.lookup_entity(name
, QAPISchemaType
)
1807 def _def_include(self
, expr
, info
, doc
):
1808 include
= expr
['include']
1811 while main_info
['parent']:
1812 main_info
= main_info
['parent']
1813 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
['file']))
1814 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
1816 def _def_builtin_type(self
, name
, json_type
, c_type
):
1817 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1818 # Instantiating only the arrays that are actually used would
1819 # be nice, but we can't as long as their generated code
1820 # (qapi-builtin-types.[ch]) may be shared by some other
1822 self
._make
_array
_type
(name
, None)
1824 def _def_predefineds(self
):
1825 for t
in [('str', 'string', 'char' + pointer_suffix
),
1826 ('number', 'number', 'double'),
1827 ('int', 'int', 'int64_t'),
1828 ('int8', 'int', 'int8_t'),
1829 ('int16', 'int', 'int16_t'),
1830 ('int32', 'int', 'int32_t'),
1831 ('int64', 'int', 'int64_t'),
1832 ('uint8', 'int', 'uint8_t'),
1833 ('uint16', 'int', 'uint16_t'),
1834 ('uint32', 'int', 'uint32_t'),
1835 ('uint64', 'int', 'uint64_t'),
1836 ('size', 'int', 'uint64_t'),
1837 ('bool', 'boolean', 'bool'),
1838 ('any', 'value', 'QObject' + pointer_suffix
),
1839 ('null', 'null', 'QNull' + pointer_suffix
)]:
1840 self
._def
_builtin
_type
(*t
)
1841 self
.the_empty_object_type
= QAPISchemaObjectType(
1842 'q_empty', None, None, None, None, [], None, [])
1843 self
._def
_entity
(self
.the_empty_object_type
)
1845 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1847 qtype_values
= self
._make
_enum
_members
([{'name': n
} for n
in qtypes
])
1849 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
1850 qtype_values
, 'QTYPE'))
1852 def _make_features(self
, features
):
1853 return [QAPISchemaFeature(f
['name'], f
.get('if')) for f
in features
]
1855 def _make_enum_members(self
, values
):
1856 return [QAPISchemaMember(v
['name'], v
.get('if')) for v
in values
]
1858 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
1859 # See also QAPISchemaObjectTypeMember._pretty_owner()
1860 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1861 self
._def
_entity
(QAPISchemaEnumType(
1862 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
), None))
1865 def _make_array_type(self
, element_type
, info
):
1866 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1867 if not self
.lookup_type(name
):
1868 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1871 def _make_implicit_object_type(self
, name
, info
, doc
, ifcond
,
1875 # See also QAPISchemaObjectTypeMember._pretty_owner()
1876 name
= 'q_obj_%s-%s' % (name
, role
)
1877 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1879 # The implicit object type has multiple users. This can
1880 # happen only for simple unions' implicit wrapper types.
1881 # Its ifcond should be the disjunction of its user's
1882 # ifconds. Not implemented. Instead, we always pass the
1883 # wrapped type's ifcond, which is trivially the same for all
1884 # users. It's also necessary for the wrapper to compile.
1885 # But it's not tight: the disjunction need not imply it. We
1886 # may end up compiling useless wrapper types.
1887 # TODO kill simple unions or implement the disjunction
1888 assert ifcond
== typ
._ifcond
# pylint: disable=protected-access
1890 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
,
1891 None, members
, None, []))
1894 def _def_enum_type(self
, expr
, info
, doc
):
1897 prefix
= expr
.get('prefix')
1898 ifcond
= expr
.get('if')
1899 self
._def
_entity
(QAPISchemaEnumType(
1900 name
, info
, doc
, ifcond
,
1901 self
._make
_enum
_members
(data
), prefix
))
1903 def _make_member(self
, name
, typ
, ifcond
, info
):
1905 if name
.startswith('*'):
1908 if isinstance(typ
, list):
1909 assert len(typ
) == 1
1910 typ
= self
._make
_array
_type
(typ
[0], info
)
1911 return QAPISchemaObjectTypeMember(name
, typ
, optional
, ifcond
)
1913 def _make_members(self
, data
, info
):
1914 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
1915 for (key
, value
) in data
.items()]
1917 def _def_struct_type(self
, expr
, info
, doc
):
1918 name
= expr
['struct']
1919 base
= expr
.get('base')
1921 ifcond
= expr
.get('if')
1922 features
= expr
.get('features', [])
1923 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
,
1924 self
._make
_members
(data
, info
),
1926 self
._make
_features
(features
)))
1928 def _make_variant(self
, case
, typ
, ifcond
):
1929 return QAPISchemaObjectTypeVariant(case
, typ
, ifcond
)
1931 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1932 if isinstance(typ
, list):
1933 assert len(typ
) == 1
1934 typ
= self
._make
_array
_type
(typ
[0], info
)
1935 typ
= self
._make
_implicit
_object
_type
(
1936 typ
, info
, None, self
.lookup_type(typ
),
1937 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
1938 return QAPISchemaObjectTypeVariant(case
, typ
, ifcond
)
1940 def _def_union_type(self
, expr
, info
, doc
):
1941 name
= expr
['union']
1943 base
= expr
.get('base')
1944 ifcond
= expr
.get('if')
1945 tag_name
= expr
.get('discriminator')
1947 if isinstance(base
, dict):
1948 base
= self
._make
_implicit
_object
_type
(
1949 name
, info
, doc
, ifcond
,
1950 'base', self
._make
_members
(base
, info
))
1952 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'))
1953 for (key
, value
) in data
.items()]
1956 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1957 value
.get('if'), info
)
1958 for (key
, value
) in data
.items()]
1959 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1960 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1961 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1962 members
= [tag_member
]
1964 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
1965 QAPISchemaObjectTypeVariants(tag_name
,
1969 def _def_alternate_type(self
, expr
, info
, doc
):
1970 name
= expr
['alternate']
1972 ifcond
= expr
.get('if')
1973 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'))
1974 for (key
, value
) in data
.items()]
1975 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1977 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
1978 QAPISchemaObjectTypeVariants(None,
1982 def _def_command(self
, expr
, info
, doc
):
1983 name
= expr
['command']
1984 data
= expr
.get('data')
1985 rets
= expr
.get('returns')
1986 gen
= expr
.get('gen', True)
1987 success_response
= expr
.get('success-response', True)
1988 boxed
= expr
.get('boxed', False)
1989 allow_oob
= expr
.get('allow-oob', False)
1990 allow_preconfig
= expr
.get('allow-preconfig', False)
1991 ifcond
= expr
.get('if')
1992 if isinstance(data
, OrderedDict
):
1993 data
= self
._make
_implicit
_object
_type
(
1994 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1995 if isinstance(rets
, list):
1996 assert len(rets
) == 1
1997 rets
= self
._make
_array
_type
(rets
[0], info
)
1998 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
1999 gen
, success_response
,
2000 boxed
, allow_oob
, allow_preconfig
))
2002 def _def_event(self
, expr
, info
, doc
):
2003 name
= expr
['event']
2004 data
= expr
.get('data')
2005 boxed
= expr
.get('boxed', False)
2006 ifcond
= expr
.get('if')
2007 if isinstance(data
, OrderedDict
):
2008 data
= self
._make
_implicit
_object
_type
(
2009 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
2010 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
2012 def _def_exprs(self
, exprs
):
2013 for expr_elem
in exprs
:
2014 expr
= expr_elem
['expr']
2015 info
= expr_elem
['info']
2016 doc
= expr_elem
.get('doc')
2018 self
._def
_enum
_type
(expr
, info
, doc
)
2019 elif 'struct' in expr
:
2020 self
._def
_struct
_type
(expr
, info
, doc
)
2021 elif 'union' in expr
:
2022 self
._def
_union
_type
(expr
, info
, doc
)
2023 elif 'alternate' in expr
:
2024 self
._def
_alternate
_type
(expr
, info
, doc
)
2025 elif 'command' in expr
:
2026 self
._def
_command
(expr
, info
, doc
)
2027 elif 'event' in expr
:
2028 self
._def
_event
(expr
, info
, doc
)
2029 elif 'include' in expr
:
2030 self
._def
_include
(expr
, info
, doc
)
2035 for ent
in self
._entity
_list
:
2038 def visit(self
, visitor
):
2039 visitor
.visit_begin(self
)
2041 visitor
.visit_module(module
)
2042 for entity
in self
._entity
_list
:
2043 if visitor
.visit_needed(entity
):
2044 if entity
.module
!= module
:
2045 module
= entity
.module
2046 visitor
.visit_module(module
)
2047 entity
.visit(visitor
)
2052 # Code generation helpers
2055 def camel_case(name
):
2059 if ch
in ['_', '-']:
2062 new_name
+= ch
.upper()
2065 new_name
+= ch
.lower()
2069 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2070 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2071 # ENUM24_Name -> ENUM24_NAME
2072 def camel_to_upper(value
):
2073 c_fun_str
= c_name(value
, False)
2078 length
= len(c_fun_str
)
2079 for i
in range(length
):
2081 # When c is upper and no '_' appears before, do more checks
2082 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
2083 if i
< length
- 1 and c_fun_str
[i
+ 1].islower():
2085 elif c_fun_str
[i
- 1].isdigit():
2088 return new_name
.lstrip('_').upper()
2091 def c_enum_const(type_name
, const_name
, prefix
=None):
2092 if prefix
is not None:
2094 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
2097 if hasattr(str, 'maketrans'):
2098 c_name_trans
= str.maketrans('.-', '__')
2100 c_name_trans
= string
.maketrans('.-', '__')
2103 # Map @name to a valid C identifier.
2104 # If @protect, avoid returning certain ticklish identifiers (like
2105 # C keywords) by prepending 'q_'.
2107 # Used for converting 'name' from a 'name':'type' qapi definition
2108 # into a generated struct member, as well as converting type names
2109 # into substrings of a generated C function name.
2110 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2111 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2112 def c_name(name
, protect
=True):
2113 # ANSI X3J11/88-090, 3.1.1
2114 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
2115 'default', 'do', 'double', 'else', 'enum', 'extern',
2116 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2117 'return', 'short', 'signed', 'sizeof', 'static',
2118 'struct', 'switch', 'typedef', 'union', 'unsigned',
2119 'void', 'volatile', 'while'])
2120 # ISO/IEC 9899:1999, 6.4.1
2121 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2122 # ISO/IEC 9899:2011, 6.4.1
2123 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2124 '_Noreturn', '_Static_assert', '_Thread_local'])
2125 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2127 gcc_words
= set(['asm', 'typeof'])
2128 # C++ ISO/IEC 14882:2003 2.11
2129 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
2130 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2131 'namespace', 'new', 'operator', 'private', 'protected',
2132 'public', 'reinterpret_cast', 'static_cast', 'template',
2133 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2134 'using', 'virtual', 'wchar_t',
2135 # alternative representations
2136 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2137 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2138 # namespace pollution:
2139 polluted_words
= set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2140 name
= name
.translate(c_name_trans
)
2141 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
2142 | cpp_words | polluted_words
):
2147 eatspace
= '\033EATSPACE.'
2148 pointer_suffix
= ' *' + eatspace
2151 def genindent(count
):
2153 for _
in range(count
):
2161 def push_indent(indent_amount
=4):
2163 indent_level
+= indent_amount
2166 def pop_indent(indent_amount
=4):
2168 indent_level
-= indent_amount
2171 # Generate @code with @kwds interpolated.
2172 # Obey indent_level, and strip eatspace.
2173 def cgen(code
, **kwds
):
2176 indent
= genindent(indent_level
)
2177 # re.subn() lacks flags support before Python 2.7, use re.compile()
2178 raw
= re
.subn(re
.compile(r
'^(?!(#|$))', re
.MULTILINE
),
2181 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
2184 def mcgen(code
, **kwds
):
2187 return cgen(code
, **kwds
)
2190 def c_fname(filename
):
2191 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
)
2194 def guardstart(name
):
2200 name
=c_fname(name
).upper())
2206 #endif /* %(name)s */
2208 name
=c_fname(name
).upper())
2220 def gen_endif(ifcond
):
2222 for ifc
in reversed(ifcond
):
2224 #endif /* %(cond)s */
2229 def _wrap_ifcond(ifcond
, before
, after
):
2231 return after
# suppress empty #if ... #endif
2233 assert after
.startswith(before
)
2235 added
= after
[len(before
):]
2236 if added
[0] == '\n':
2239 out
+= gen_if(ifcond
)
2241 out
+= gen_endif(ifcond
)
2245 def gen_enum_lookup(name
, members
, prefix
=None):
2248 const QEnumLookup %(c_name)s_lookup = {
2249 .array = (const char *const[]) {
2251 c_name
=c_name(name
))
2253 ret
+= gen_if(m
.ifcond
)
2254 index
= c_enum_const(name
, m
.name
, prefix
)
2256 [%(index)s] = "%(name)s",
2258 index
=index
, name
=m
.name
)
2259 ret
+= gen_endif(m
.ifcond
)
2263 .size = %(max_index)s
2266 max_index
=c_enum_const(name
, '_MAX', prefix
))
2270 def gen_enum(name
, members
, prefix
=None):
2271 # append automatically generated _MAX value
2272 enum_members
= members
+ [QAPISchemaMember('_MAX')]
2276 typedef enum %(c_name)s {
2278 c_name
=c_name(name
))
2280 for m
in enum_members
:
2281 ret
+= gen_if(m
.ifcond
)
2285 c_enum
=c_enum_const(name
, m
.name
, prefix
))
2286 ret
+= gen_endif(m
.ifcond
)
2291 c_name
=c_name(name
))
2295 #define %(c_name)s_str(val) \\
2296 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2298 extern const QEnumLookup %(c_name)s_lookup;
2300 c_name
=c_name(name
))
2304 def build_params(arg_type
, boxed
, extra
=None):
2309 ret
+= '%s arg' % arg_type
.c_param_type()
2312 assert not arg_type
.variants
2313 for memb
in arg_type
.members
:
2317 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
2318 ret
+= '%s %s' % (memb
.type.c_param_type(),
2322 return ret
if ret
else 'void'
2326 # Accumulate and write output
2329 class QAPIGen(object):
2331 def __init__(self
, fname
):
2336 def preamble_add(self
, text
):
2337 self
._preamble
+= text
2339 def add(self
, text
):
2342 def get_content(self
):
2343 return self
._top
() + self
._preamble
+ self
._body
+ self
._bottom
()
2351 def write(self
, output_dir
):
2352 pathname
= os
.path
.join(output_dir
, self
.fname
)
2353 dir = os
.path
.dirname(pathname
)
2357 except os
.error
as e
:
2358 if e
.errno
!= errno
.EEXIST
:
2360 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2361 if sys
.version_info
[0] >= 3:
2362 f
= open(fd
, 'r+', encoding
='utf-8')
2364 f
= os
.fdopen(fd
, 'r+')
2365 text
= self
.get_content()
2366 oldtext
= f
.read(len(text
) + 1)
2375 def ifcontext(ifcond
, *args
):
2376 """A 'with' statement context manager to wrap with start_if()/end_if()
2378 *args: any number of QAPIGenCCode
2382 with ifcontext(ifcond, self._genh, self._genc):
2383 modify self._genh and self._genc ...
2385 Is equivalent to calling::
2387 self._genh.start_if(ifcond)
2388 self._genc.start_if(ifcond)
2389 modify self._genh and self._genc ...
2394 arg
.start_if(ifcond
)
2400 class QAPIGenCCode(QAPIGen
):
2402 def __init__(self
, fname
):
2403 QAPIGen
.__init
__(self
, fname
)
2404 self
._start
_if
= None
2406 def start_if(self
, ifcond
):
2407 assert self
._start
_if
is None
2408 self
._start
_if
= (ifcond
, self
._body
, self
._preamble
)
2411 assert self
._start
_if
2413 self
._start
_if
= None
2415 def _wrap_ifcond(self
):
2416 self
._body
= _wrap_ifcond(self
._start
_if
[0],
2417 self
._start
_if
[1], self
._body
)
2418 self
._preamble
= _wrap_ifcond(self
._start
_if
[0],
2419 self
._start
_if
[2], self
._preamble
)
2421 def get_content(self
):
2422 assert self
._start
_if
is None
2423 return QAPIGen
.get_content(self
)
2426 class QAPIGenC(QAPIGenCCode
):
2428 def __init__(self
, fname
, blurb
, pydoc
):
2429 QAPIGenCCode
.__init
__(self
, fname
)
2431 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2436 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2443 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2444 * See the COPYING.LIB file in the top-level directory.
2448 blurb
=self
._blurb
, copyright
=self
._copyright
)
2453 /* Dummy declaration to prevent empty .o file */
2454 char qapi_dummy_%(name)s;
2456 name
=c_fname(self
.fname
))
2459 class QAPIGenH(QAPIGenC
):
2462 return QAPIGenC
._top
(self
) + guardstart(self
.fname
)
2465 return guardend(self
.fname
)
2468 class QAPIGenDoc(QAPIGen
):
2471 return (QAPIGen
._top
(self
)
2472 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2475 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2477 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2478 self
._prefix
= prefix
2480 self
._genc
= QAPIGenC(self
._prefix
+ self
._what
+ '.c',
2482 self
._genh
= QAPIGenH(self
._prefix
+ self
._what
+ '.h',
2485 def write(self
, output_dir
):
2486 self
._genc
.write(output_dir
)
2487 self
._genh
.write(output_dir
)
2490 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2492 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2493 self
._prefix
= prefix
2500 self
._main
_module
= None
2503 def _is_user_module(name
):
2504 return name
and not name
.startswith('./')
2507 def _is_builtin_module(name
):
2510 def _module_dirname(self
, what
, name
):
2511 if self
._is
_user
_module
(name
):
2512 return os
.path
.dirname(name
)
2515 def _module_basename(self
, what
, name
):
2516 ret
= '' if self
._is
_builtin
_module
(name
) else self
._prefix
2517 if self
._is
_user
_module
(name
):
2518 basename
= os
.path
.basename(name
)
2520 if name
!= self
._main
_module
:
2521 ret
+= '-' + os
.path
.splitext(basename
)[0]
2523 name
= name
[2:] if name
else 'builtin'
2524 ret
+= re
.sub(r
'-', '-' + name
+ '-', what
)
2527 def _module_filename(self
, what
, name
):
2528 return os
.path
.join(self
._module
_dirname
(what
, name
),
2529 self
._module
_basename
(what
, name
))
2531 def _add_module(self
, name
, blurb
):
2532 basename
= self
._module
_filename
(self
._what
, name
)
2533 genc
= QAPIGenC(basename
+ '.c', blurb
, self
._pydoc
)
2534 genh
= QAPIGenH(basename
+ '.h', blurb
, self
._pydoc
)
2535 self
._module
[name
] = (genc
, genh
)
2536 self
._set
_module
(name
)
2538 def _add_user_module(self
, name
, blurb
):
2539 assert self
._is
_user
_module
(name
)
2540 if self
._main
_module
is None:
2541 self
._main
_module
= name
2542 self
._add
_module
(name
, blurb
)
2544 def _add_system_module(self
, name
, blurb
):
2545 self
._add
_module
(name
and './' + name
, blurb
)
2547 def _set_module(self
, name
):
2548 self
._genc
, self
._genh
= self
._module
[name
]
2550 def write(self
, output_dir
, opt_builtins
=False):
2551 for name
in self
._module
:
2552 if self
._is
_builtin
_module
(name
) and not opt_builtins
:
2554 (genc
, genh
) = self
._module
[name
]
2555 genc
.write(output_dir
)
2556 genh
.write(output_dir
)
2558 def _begin_user_module(self
, name
):
2561 def visit_module(self
, name
):
2562 if name
in self
._module
:
2563 self
._set
_module
(name
)
2564 elif self
._is
_builtin
_module
(name
):
2565 # The built-in module has not been created. No code may
2570 self
._add
_user
_module
(name
, self
._blurb
)
2571 self
._begin
_user
_module
(name
)
2573 def visit_include(self
, name
, info
):
2574 relname
= os
.path
.relpath(self
._module
_filename
(self
._what
, name
),
2575 os
.path
.dirname(self
._genh
.fname
))
2576 self
._genh
.preamble_add(mcgen('''
2577 #include "%(relname)s.h"