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
22 from collections
import OrderedDict
25 'null': 'QTYPE_QNULL',
26 'str': 'QTYPE_QSTRING',
28 'number': 'QTYPE_QNUM',
29 'bool': 'QTYPE_QBOOL',
31 'int16': 'QTYPE_QNUM',
32 'int32': 'QTYPE_QNUM',
33 'int64': 'QTYPE_QNUM',
34 'uint8': 'QTYPE_QNUM',
35 'uint16': 'QTYPE_QNUM',
36 'uint32': 'QTYPE_QNUM',
37 'uint64': 'QTYPE_QNUM',
39 'any': None, # any QType possible, actually
40 'QType': 'QTYPE_QSTRING',
43 # Are documentation comments required?
46 # Whitelist of commands allowed to return a non-dictionary
47 returns_whitelist
= []
49 # Whitelist of entities allowed to violate case conventions
50 name_case_whitelist
= []
59 # Parsing the schema into expressions
62 class QAPISourceInfo(object):
63 def __init__(self
, fname
, line
, parent
):
70 def set_defn(self
, meta
, name
):
75 info
= copy
.copy(self
)
80 return '%s:%d' % (self
.fname
, self
.line
)
84 return "%s: In %s '%s':\n" % (self
.fname
,
85 self
.defn_meta
, self
.defn_name
)
88 def include_path(self
):
92 ret
= 'In file included from %s:\n' % parent
.loc() + ret
93 parent
= parent
.parent
97 return self
.include_path() + self
.in_defn() + self
.loc()
100 class QAPIError(Exception):
101 def __init__(self
, info
, col
, msg
):
102 Exception.__init
__(self
)
109 if self
.col
is not None:
110 assert self
.info
.line
is not None
111 loc
+= ':%s' % self
.col
112 return loc
+ ': ' + self
.msg
115 class QAPIParseError(QAPIError
):
116 def __init__(self
, parser
, msg
):
118 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
120 col
= (col
+ 7) % 8 + 1
123 QAPIError
.__init
__(self
, parser
.info
, col
, msg
)
126 class QAPISemError(QAPIError
):
127 def __init__(self
, info
, msg
):
128 QAPIError
.__init
__(self
, info
, None, msg
)
131 class QAPIDoc(object):
133 A documentation comment block, either definition or free-form
135 Definition documentation blocks consist of
137 * a body section: one line naming the definition, followed by an
138 overview (any number of lines)
140 * argument sections: a description of each argument (for commands
141 and events) or member (for structs, unions and alternates)
143 * features sections: a description of each feature flag
145 * additional (non-argument) sections, possibly tagged
147 Free-form documentation blocks consist only of a body section.
150 class Section(object):
151 def __init__(self
, name
=None):
152 # optional section name (argument/member or section name)
154 # the list of lines for this section
157 def append(self
, line
):
158 self
.text
+= line
.rstrip() + '\n'
160 class ArgSection(Section
):
161 def __init__(self
, name
):
162 QAPIDoc
.Section
.__init
__(self
, name
)
165 def connect(self
, member
):
168 def __init__(self
, parser
, info
):
169 # self._parser is used to report errors with QAPIParseError. The
170 # resulting error position depends on the state of the parser.
171 # It happens to be the beginning of the comment. More or less
172 # servicable, but action at a distance.
173 self
._parser
= parser
176 self
.body
= QAPIDoc
.Section()
177 # dict mapping parameter name to ArgSection
178 self
.args
= OrderedDict()
179 self
.features
= OrderedDict()
182 # the current section
183 self
._section
= self
.body
184 self
._append
_line
= self
._append
_body
_line
186 def has_section(self
, name
):
187 """Return True if we have a section with this name."""
188 for i
in self
.sections
:
193 def append(self
, line
):
195 Parse a comment line and add it to the documentation.
197 The way that the line is dealt with depends on which part of
198 the documentation we're parsing right now:
199 * The body section: ._append_line is ._append_body_line
200 * An argument section: ._append_line is ._append_args_line
201 * A features section: ._append_line is ._append_features_line
202 * An additional section: ._append_line is ._append_various_line
206 self
._append
_freeform
(line
)
210 raise QAPIParseError(self
._parser
, "missing space after #")
212 self
._append
_line
(line
)
214 def end_comment(self
):
218 def _is_section_tag(name
):
219 return name
in ('Returns:', 'Since:',
220 # those are often singular or plural
222 'Example:', 'Examples:',
225 def _append_body_line(self
, line
):
227 Process a line of documentation text in the body section.
229 If this a symbol line and it is the section's first line, this
230 is a definition documentation block for that symbol.
232 If it's a definition documentation block, another symbol line
233 begins the argument section for the argument named by it, and
234 a section tag begins an additional section. Start that
235 section and append the line to it.
237 Else, append the line to the current section.
239 name
= line
.split(' ', 1)[0]
240 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
241 # recognized, and get silently treated as ordinary text
242 if not self
.symbol
and not self
.body
.text
and line
.startswith('@'):
243 if not line
.endswith(':'):
244 raise QAPIParseError(self
._parser
, "line should end with ':'")
245 self
.symbol
= line
[1:-1]
246 # FIXME invalid names other than the empty string aren't flagged
248 raise QAPIParseError(self
._parser
, "invalid name")
250 # This is a definition documentation block
251 if name
.startswith('@') and name
.endswith(':'):
252 self
._append
_line
= self
._append
_args
_line
253 self
._append
_args
_line
(line
)
254 elif line
== 'Features:':
255 self
._append
_line
= self
._append
_features
_line
256 elif self
._is
_section
_tag
(name
):
257 self
._append
_line
= self
._append
_various
_line
258 self
._append
_various
_line
(line
)
260 self
._append
_freeform
(line
.strip())
262 # This is a free-form documentation block
263 self
._append
_freeform
(line
.strip())
265 def _append_args_line(self
, line
):
267 Process a line of documentation text in an argument section.
269 A symbol line begins the next argument section, a section tag
270 section or a non-indented line after a blank line begins an
271 additional section. Start that section and append the line to
274 Else, append the line to the current section.
277 name
= line
.split(' ', 1)[0]
279 if name
.startswith('@') and name
.endswith(':'):
280 line
= line
[len(name
)+1:]
281 self
._start
_args
_section
(name
[1:-1])
282 elif self
._is
_section
_tag
(name
):
283 self
._append
_line
= self
._append
_various
_line
284 self
._append
_various
_line
(line
)
286 elif (self
._section
.text
.endswith('\n\n')
287 and line
and not line
[0].isspace()):
288 if line
== 'Features:':
289 self
._append
_line
= self
._append
_features
_line
291 self
._start
_section
()
292 self
._append
_line
= self
._append
_various
_line
293 self
._append
_various
_line
(line
)
296 self
._append
_freeform
(line
.strip())
298 def _append_features_line(self
, line
):
299 name
= line
.split(' ', 1)[0]
301 if name
.startswith('@') and name
.endswith(':'):
302 line
= line
[len(name
)+1:]
303 self
._start
_features
_section
(name
[1:-1])
304 elif self
._is
_section
_tag
(name
):
305 self
._append
_line
= self
._append
_various
_line
306 self
._append
_various
_line
(line
)
308 elif (self
._section
.text
.endswith('\n\n')
309 and line
and not line
[0].isspace()):
310 self
._start
_section
()
311 self
._append
_line
= self
._append
_various
_line
312 self
._append
_various
_line
(line
)
315 self
._append
_freeform
(line
.strip())
317 def _append_various_line(self
, line
):
319 Process a line of documentation text in an additional section.
321 A symbol line is an error.
323 A section tag begins an additional section. Start that
324 section and append the line to it.
326 Else, append the line to the current section.
328 name
= line
.split(' ', 1)[0]
330 if name
.startswith('@') and name
.endswith(':'):
331 raise QAPIParseError(self
._parser
,
332 "'%s' can't follow '%s' section"
333 % (name
, self
.sections
[0].name
))
334 elif self
._is
_section
_tag
(name
):
335 line
= line
[len(name
)+1:]
336 self
._start
_section
(name
[:-1])
338 if (not self
._section
.name
or
339 not self
._section
.name
.startswith('Example')):
342 self
._append
_freeform
(line
)
344 def _start_symbol_section(self
, symbols_dict
, name
):
345 # FIXME invalid names other than the empty string aren't flagged
347 raise QAPIParseError(self
._parser
, "invalid parameter name")
348 if name
in symbols_dict
:
349 raise QAPIParseError(self
._parser
,
350 "'%s' parameter name duplicated" % name
)
351 assert not self
.sections
353 self
._section
= QAPIDoc
.ArgSection(name
)
354 symbols_dict
[name
] = self
._section
356 def _start_args_section(self
, name
):
357 self
._start
_symbol
_section
(self
.args
, name
)
359 def _start_features_section(self
, name
):
360 self
._start
_symbol
_section
(self
.features
, name
)
362 def _start_section(self
, name
=None):
363 if name
in ('Returns', 'Since') and self
.has_section(name
):
364 raise QAPIParseError(self
._parser
,
365 "duplicated '%s' section" % name
)
367 self
._section
= QAPIDoc
.Section(name
)
368 self
.sections
.append(self
._section
)
370 def _end_section(self
):
372 text
= self
._section
.text
= self
._section
.text
.strip()
373 if self
._section
.name
and (not text
or text
.isspace()):
374 raise QAPIParseError(
376 "empty doc section '%s'" % self
._section
.name
)
379 def _append_freeform(self
, line
):
380 match
= re
.match(r
'(@\S+:)', line
)
382 raise QAPIParseError(self
._parser
,
383 "'%s' not allowed in free-form documentation"
385 self
._section
.append(line
)
387 def connect_member(self
, member
):
388 if member
.name
not in self
.args
:
389 # Undocumented TODO outlaw
390 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
391 self
.args
[member
.name
].connect(member
)
393 def check_expr(self
, expr
):
394 if self
.has_section('Returns') and 'command' not in expr
:
395 raise QAPISemError(self
.info
,
396 "'Returns:' is only valid for commands")
399 bogus
= [name
for name
, section
in self
.args
.items()
400 if not section
.member
]
404 "the following documented members are not in "
405 "the declaration: %s" % ", ".join(bogus
))
408 class QAPISchemaParser(object):
410 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
412 previously_included
.append(os
.path
.abspath(fp
.name
))
414 if self
.src
== '' or self
.src
[-1] != '\n':
417 self
.info
= QAPISourceInfo(self
.fname
, 1, incl_info
)
424 while self
.tok
is not None:
427 self
.reject_expr_doc(cur_doc
)
428 cur_doc
= self
.get_doc(info
)
429 self
.docs
.append(cur_doc
)
432 expr
= self
.get_expr(False)
433 if 'include' in expr
:
434 self
.reject_expr_doc(cur_doc
)
436 raise QAPISemError(info
, "invalid 'include' directive")
437 include
= expr
['include']
438 if not isinstance(include
, str):
439 raise QAPISemError(info
,
440 "value of 'include' must be a string")
441 incl_fname
= os
.path
.join(os
.path
.dirname(self
.fname
),
443 self
.exprs
.append({'expr': {'include': incl_fname
},
445 exprs_include
= self
._include
(include
, info
, incl_fname
,
448 self
.exprs
.extend(exprs_include
.exprs
)
449 self
.docs
.extend(exprs_include
.docs
)
450 elif "pragma" in expr
:
451 self
.reject_expr_doc(cur_doc
)
453 raise QAPISemError(info
, "invalid 'pragma' directive")
454 pragma
= expr
['pragma']
455 if not isinstance(pragma
, dict):
457 info
, "value of 'pragma' must be an object")
458 for name
, value
in pragma
.items():
459 self
._pragma
(name
, value
, info
)
461 expr_elem
= {'expr': expr
,
464 if not cur_doc
.symbol
:
466 cur_doc
.info
, "definition documentation required")
467 expr_elem
['doc'] = cur_doc
468 self
.exprs
.append(expr_elem
)
470 self
.reject_expr_doc(cur_doc
)
473 def reject_expr_doc(doc
):
474 if doc
and doc
.symbol
:
477 "documentation for '%s' is not followed by the definition"
480 def _include(self
, include
, info
, incl_fname
, previously_included
):
481 incl_abs_fname
= os
.path
.abspath(incl_fname
)
482 # catch inclusion cycle
485 if incl_abs_fname
== os
.path
.abspath(inf
.fname
):
486 raise QAPISemError(info
, "inclusion loop for %s" % include
)
489 # skip multiple include of the same file
490 if incl_abs_fname
in previously_included
:
494 if sys
.version_info
[0] >= 3:
495 fobj
= open(incl_fname
, 'r', encoding
='utf-8')
497 fobj
= open(incl_fname
, 'r')
499 raise QAPISemError(info
, "%s: %s" % (e
.strerror
, incl_fname
))
500 return QAPISchemaParser(fobj
, previously_included
, info
)
502 def _pragma(self
, name
, value
, info
):
503 global doc_required
, returns_whitelist
, name_case_whitelist
504 if name
== 'doc-required':
505 if not isinstance(value
, bool):
506 raise QAPISemError(info
,
507 "pragma 'doc-required' must be boolean")
509 elif name
== 'returns-whitelist':
510 if (not isinstance(value
, list)
511 or any([not isinstance(elt
, str) for elt
in value
])):
514 "pragma returns-whitelist must be a list of strings")
515 returns_whitelist
= value
516 elif name
== 'name-case-whitelist':
517 if (not isinstance(value
, list)
518 or any([not isinstance(elt
, str) for elt
in value
])):
521 "pragma name-case-whitelist must be a list of strings")
522 name_case_whitelist
= value
524 raise QAPISemError(info
, "unknown pragma '%s'" % name
)
526 def accept(self
, skip_comment
=True):
528 self
.tok
= self
.src
[self
.cursor
]
529 self
.pos
= self
.cursor
534 if self
.src
[self
.cursor
] == '#':
535 # Start of doc comment
537 self
.cursor
= self
.src
.find('\n', self
.cursor
)
539 self
.val
= self
.src
[self
.pos
:self
.cursor
]
541 elif self
.tok
in '{}:,[]':
543 elif self
.tok
== "'":
544 # Note: we accept only printable ASCII
548 ch
= self
.src
[self
.cursor
]
551 raise QAPIParseError(self
, "missing terminating \"'\"")
553 # Note: we recognize only \\ because we have
554 # no use for funny characters in strings
556 raise QAPIParseError(self
,
557 "unknown escape \\%s" % ch
)
565 if ord(ch
) < 32 or ord(ch
) >= 127:
566 raise QAPIParseError(
567 self
, "funny character in string")
569 elif self
.src
.startswith('true', self
.pos
):
573 elif self
.src
.startswith('false', self
.pos
):
577 elif self
.tok
== '\n':
578 if self
.cursor
== len(self
.src
):
581 self
.info
= self
.info
.next_line()
582 self
.line_pos
= self
.cursor
583 elif not self
.tok
.isspace():
584 # Show up to next structural, whitespace or quote
586 match
= re
.match('[^[\\]{}:,\\s\'"]+',
587 self
.src
[self
.cursor
-1:])
588 raise QAPIParseError(self
, "stray '%s'" % match
.group(0))
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(
622 self
, "expected '{', '[', ']', string, boolean or 'null'")
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(
646 self
, "expected '{', '[', string, boolean or 'null'")
649 def get_doc(self
, info
):
651 raise QAPIParseError(
652 self
, "junk after '##' at start of documentation comment")
654 doc
= QAPIDoc(self
, info
)
656 while self
.tok
== '#':
657 if self
.val
.startswith('##'):
660 raise QAPIParseError(
662 "junk after '##' at end of documentation comment")
670 raise QAPIParseError(self
, "documentation comment must end with '##'")
674 # Semantic analysis of schema expressions
675 # TODO fold into QAPISchema
676 # TODO catching name collisions in generated code would be nice
680 def find_base_members(base
):
681 if isinstance(base
, dict):
683 base_struct_define
= struct_types
.get(base
)
684 if not base_struct_define
:
686 return base_struct_define
['data']
689 # Return the qtype of an alternate branch, or None on error.
690 def find_alternate_member_qtype(qapi_type
):
691 if qapi_type
in builtin_types
:
692 return builtin_types
[qapi_type
]
693 elif qapi_type
in struct_types
:
695 elif qapi_type
in enum_types
:
696 return 'QTYPE_QSTRING'
697 elif qapi_type
in union_types
:
702 # Names must be letters, numbers, -, and _. They must start with letter,
703 # except for downstream extensions which must start with __RFQDN_.
704 # Dots are only valid in the downstream extension prefix.
705 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
706 '[a-zA-Z][a-zA-Z0-9_-]*$')
709 def check_name(name
, info
, source
,
710 allow_optional
=False, enum_member
=False, permit_upper
=False):
711 check_name_is_str(name
, info
, source
)
712 check_name_str(name
, info
, source
,
713 allow_optional
, enum_member
, permit_upper
)
716 def check_name_is_str(name
, info
, source
):
717 if not isinstance(name
, str):
718 raise QAPISemError(info
, "%s requires a string name" % source
)
721 def check_name_str(name
, info
, source
,
722 allow_optional
=False, enum_member
=False,
727 if allow_optional
and name
.startswith('*'):
728 membername
= name
[1:]
729 # Enum members can start with a digit, because the generated C
730 # code always prefixes it with the enum name
731 if enum_member
and membername
[0].isdigit():
732 membername
= 'D' + membername
733 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
734 # and 'q_obj_*' implicit type names.
735 if not valid_name
.match(membername
) or \
736 c_name(membername
, False).startswith('q_'):
737 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
738 if not permit_upper
and name
.lower() != name
:
740 info
, "%s uses uppercase in name '%s'" % (source
, name
))
741 assert not membername
.startswith('*')
744 def check_defn_name_str(name
, info
, meta
):
745 check_name_str(name
, info
, meta
, permit_upper
=True)
746 if name
.endswith('Kind') or name
.endswith('List'):
748 info
, "%s '%s' should not end in '%s'" % (meta
, name
, name
[-4:]))
751 def add_name(name
, info
, meta
):
753 # FIXME should reject names that differ only in '_' vs. '.'
754 # vs. '-', because they're liable to clash in generated C.
755 if name
in all_names
:
756 raise QAPISemError(info
, "%s '%s' is already defined"
757 % (all_names
[name
], name
))
758 all_names
[name
] = meta
761 def check_if(expr
, info
):
763 def check_if_str(ifcond
, info
):
764 if not isinstance(ifcond
, str):
766 info
, "'if' condition must be a string or a list of strings")
767 if ifcond
.strip() == '':
768 raise QAPISemError(info
, "'if' condition '%s' makes no sense"
771 ifcond
= expr
.get('if')
774 if isinstance(ifcond
, list):
776 raise QAPISemError(info
, "'if' condition [] is useless")
778 check_if_str(elt
, info
)
780 check_if_str(ifcond
, info
)
783 def check_type(value
, info
, source
,
784 allow_array
=False, allow_dict
=False, allow_metas
=[]):
790 # Check if array type for value is okay
791 if isinstance(value
, list):
793 raise QAPISemError(info
, "%s cannot be an array" % source
)
794 if len(value
) != 1 or not isinstance(value
[0], str):
795 raise QAPISemError(info
,
796 "%s: array type must contain single type name" %
798 check_type(value
[0], info
, source
, allow_metas
=allow_metas
)
801 # Check if type name for value is okay
802 if isinstance(value
, str):
803 if value
not in all_names
:
804 raise QAPISemError(info
, "%s uses unknown type '%s'"
806 if not all_names
[value
] in allow_metas
:
807 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
808 (source
, all_names
[value
], value
))
812 raise QAPISemError(info
, "%s should be a type name" % source
)
814 if not isinstance(value
, OrderedDict
):
815 raise QAPISemError(info
,
816 "%s should be an object or type name" % source
)
818 permit_upper
= allow_dict
in name_case_whitelist
820 # value is a dictionary, check that each member is okay
821 for (key
, arg
) in value
.items():
822 check_name_str(key
, info
, "member of %s" % source
,
823 allow_optional
=True, permit_upper
=permit_upper
)
824 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
826 info
, "member of %s uses reserved name '%s'" % (source
, key
))
827 check_known_keys(arg
, info
, "member '%s' of %s" % (key
, source
),
831 check_type(arg
['type'], info
, "member '%s' of %s" % (key
, source
),
833 allow_metas
=['built-in', 'union', 'alternate', 'struct',
837 def check_command(expr
, info
):
838 name
= expr
['command']
839 boxed
= expr
.get('boxed', False)
841 args_meta
= ['struct']
843 args_meta
+= ['union']
844 check_type(expr
.get('data'), info
,
845 "'data' for command '%s'" % name
,
846 allow_dict
=not boxed
, allow_metas
=args_meta
)
847 returns_meta
= ['union', 'struct']
848 if name
in returns_whitelist
:
849 returns_meta
+= ['built-in', 'alternate', 'enum']
850 check_type(expr
.get('returns'), info
,
851 "'returns' for command '%s'" % name
,
852 allow_array
=True, allow_metas
=returns_meta
)
855 def check_event(expr
, info
):
857 boxed
= expr
.get('boxed', False)
862 check_type(expr
.get('data'), info
,
863 "'data' for event '%s'" % name
,
864 allow_dict
=not boxed
, allow_metas
=meta
)
867 def enum_get_names(expr
):
868 return [e
['name'] for e
in expr
['data']]
871 def check_union(expr
, info
):
873 base
= expr
.get('base')
874 discriminator
= expr
.get('discriminator')
875 members
= expr
['data']
877 # Two types of unions, determined by discriminator.
879 # With no discriminator it is a simple union.
880 if discriminator
is None:
881 enum_values
= members
.keys()
882 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
885 info
, "simple union '%s' must not have a base" % name
)
887 # Else, it's a flat union.
889 # The object must have a string or dictionary 'base'.
890 check_type(base
, info
, "'base' for union '%s'" % name
,
891 allow_dict
=name
, allow_metas
=['struct'])
894 info
, "flat union '%s' must have a base" % name
)
895 base_members
= find_base_members(base
)
896 assert base_members
is not None
898 # The value of member 'discriminator' must name a non-optional
899 # member of the base struct.
900 check_name(discriminator
, info
,
901 "discriminator of flat union '%s'" % name
)
902 discriminator_value
= base_members
.get(discriminator
)
903 if not discriminator_value
:
904 raise QAPISemError(info
,
905 "discriminator '%s' is not a member of 'base'"
907 if discriminator_value
.get('if'):
910 "the discriminator '%s' for union %s must not be conditional"
911 % (discriminator
, name
))
912 enum_define
= enum_types
.get(discriminator_value
['type'])
913 # Do not allow string discriminator
917 "discriminator '%s' must be of enumeration type"
919 enum_values
= enum_get_names(enum_define
)
920 allow_metas
= ['struct']
922 if (len(enum_values
) == 0):
923 raise QAPISemError(info
, "union '%s' has no branches" % name
)
925 for (key
, value
) in members
.items():
926 check_name_str(key
, info
, "member of union '%s'" % name
)
927 check_known_keys(value
, info
,
928 "member '%s' of union '%s'" % (key
, name
),
930 check_if(value
, info
)
932 # Each value must name a known type
933 check_type(value
['type'], info
,
934 "member '%s' of union '%s'" % (key
, name
),
935 allow_array
=not base
, allow_metas
=allow_metas
)
937 # If the discriminator names an enum type, then all members
938 # of 'data' must also be members of the enum type.
939 if discriminator
is not None:
940 if key
not in enum_values
:
943 "discriminator value '%s' is not found in enum '%s'"
944 % (key
, enum_define
['enum']))
947 def check_alternate(expr
, info
):
948 name
= expr
['alternate']
949 members
= expr
['data']
952 if len(members
) == 0:
953 raise QAPISemError(info
,
954 "alternate '%s' cannot have empty 'data'" % name
)
955 for (key
, value
) in members
.items():
956 check_name_str(key
, info
, "member of alternate '%s'" % name
)
957 check_known_keys(value
, info
,
958 "member '%s' of alternate '%s'" % (key
, name
),
960 check_if(value
, info
)
964 # Ensure alternates have no type conflicts.
965 check_type(typ
, info
, "member '%s' of alternate '%s'" % (key
, name
),
966 allow_metas
=['built-in', 'union', 'struct', 'enum'])
967 qtype
= find_alternate_member_qtype(typ
)
971 "alternate '%s' member '%s' cannot use type '%s'"
973 conflicting
= set([qtype
])
974 if qtype
== 'QTYPE_QSTRING':
975 enum_expr
= enum_types
.get(typ
)
977 for v
in enum_get_names(enum_expr
):
978 if v
in ['on', 'off']:
979 conflicting
.add('QTYPE_QBOOL')
980 if re
.match(r
'[-+0-9.]', v
): # lazy, could be tightened
981 conflicting
.add('QTYPE_QNUM')
983 conflicting
.add('QTYPE_QNUM')
984 conflicting
.add('QTYPE_QBOOL')
985 for qt
in conflicting
:
989 "alternate '%s' member '%s' can't be distinguished "
991 % (name
, key
, types_seen
[qt
]))
995 def check_enum(expr
, info
):
997 members
= expr
['data']
998 prefix
= expr
.get('prefix')
1000 if not isinstance(members
, list):
1001 raise QAPISemError(info
,
1002 "enum '%s' requires an array for 'data'" % name
)
1003 if prefix
is not None and not isinstance(prefix
, str):
1004 raise QAPISemError(info
,
1005 "enum '%s' requires a string for 'prefix'" % name
)
1007 permit_upper
= name
in name_case_whitelist
1009 for member
in members
:
1010 check_known_keys(member
, info
, "member of enum '%s'" % name
,
1012 check_if(member
, info
)
1013 normalize_if(member
)
1014 check_name(member
['name'], info
, "member of enum '%s'" % name
,
1015 enum_member
=True, permit_upper
=permit_upper
)
1018 def check_struct(expr
, info
):
1019 name
= expr
['struct']
1020 members
= expr
['data']
1021 features
= expr
.get('features')
1023 check_type(members
, info
, "'data' for struct '%s'" % name
,
1025 check_type(expr
.get('base'), info
, "'base' for struct '%s'" % name
,
1026 allow_metas
=['struct'])
1029 if not isinstance(features
, list):
1031 info
, "struct '%s' requires an array for 'features'" % name
)
1033 assert isinstance(f
, dict)
1034 check_known_keys(f
, info
, "feature of struct %s" % name
,
1039 check_name(f
['name'], info
, "feature of struct %s" % name
)
1042 def check_known_keys(value
, info
, source
, required
, optional
):
1045 return ', '.join("'" + e
+ "'" for e
in sorted(elems
))
1047 missing
= set(required
) - set(value
)
1051 "key%s %s %s missing from %s"
1052 % ('s' if len(missing
) > 1 else '', pprint(missing
),
1053 'are' if len(missing
) > 1 else 'is', source
))
1054 allowed
= set(required
+ optional
)
1055 unknown
= set(value
) - allowed
1059 "unknown key%s %s in %s\nValid keys are %s."
1060 % ('s' if len(unknown
) > 1 else '', pprint(unknown
),
1061 source
, pprint(allowed
)))
1064 def check_keys(expr
, info
, meta
, required
, optional
=[]):
1066 if not isinstance(name
, str):
1067 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
1068 required
= required
+ [meta
]
1069 source
= "%s '%s'" % (meta
, name
)
1070 check_known_keys(expr
, info
, source
, required
, optional
)
1071 for (key
, value
) in expr
.items():
1072 if key
in ['gen', 'success-response'] and value
is not False:
1073 raise QAPISemError(info
,
1074 "'%s' of %s '%s' should only use false value"
1075 % (key
, meta
, name
))
1076 if (key
in ['boxed', 'allow-oob', 'allow-preconfig']
1077 and value
is not True):
1078 raise QAPISemError(info
,
1079 "'%s' of %s '%s' should only use true value"
1080 % (key
, meta
, name
))
1082 check_if(expr
, info
)
1085 def normalize_enum(expr
):
1086 if isinstance(expr
['data'], list):
1087 expr
['data'] = [m
if isinstance(m
, dict) else {'name': m
}
1088 for m
in expr
['data']]
1091 def normalize_members(members
):
1092 if isinstance(members
, OrderedDict
):
1093 for key
, arg
in members
.items():
1094 if isinstance(arg
, dict):
1096 members
[key
] = {'type': arg
}
1099 def normalize_features(features
):
1100 if isinstance(features
, list):
1101 features
[:] = [f
if isinstance(f
, dict) else {'name': f
}
1105 def normalize_if(expr
):
1106 ifcond
= expr
.get('if')
1107 if isinstance(ifcond
, str):
1108 expr
['if'] = [ifcond
]
1111 def check_exprs(exprs
):
1114 # Populate name table with names of built-in types
1115 for builtin
in builtin_types
.keys():
1116 all_names
[builtin
] = 'built-in'
1118 # Learn the types and check for valid expression keys
1119 for expr_elem
in exprs
:
1120 expr
= expr_elem
['expr']
1121 info
= expr_elem
['info']
1122 doc
= expr_elem
.get('doc')
1124 if 'include' in expr
:
1127 if not doc
and doc_required
:
1128 raise QAPISemError(info
,
1129 "definition missing documentation comment")
1133 check_keys(expr
, info
, 'enum', ['data'], ['if', 'prefix'])
1134 normalize_enum(expr
)
1135 enum_types
[expr
[meta
]] = expr
1136 elif 'union' in expr
:
1138 check_keys(expr
, info
, 'union', ['data'],
1139 ['base', 'discriminator', 'if'])
1140 normalize_members(expr
.get('base'))
1141 normalize_members(expr
['data'])
1142 union_types
[expr
[meta
]] = expr
1143 elif 'alternate' in expr
:
1145 check_keys(expr
, info
, 'alternate', ['data'], ['if'])
1146 normalize_members(expr
['data'])
1147 elif 'struct' in expr
:
1149 check_keys(expr
, info
, 'struct', ['data'],
1150 ['base', 'if', 'features'])
1151 normalize_members(expr
['data'])
1152 normalize_features(expr
.get('features'))
1153 struct_types
[expr
[meta
]] = expr
1154 elif 'command' in expr
:
1156 check_keys(expr
, info
, 'command', [],
1157 ['data', 'returns', 'gen', 'success-response',
1158 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
1159 normalize_members(expr
.get('data'))
1160 elif 'event' in expr
:
1162 check_keys(expr
, info
, 'event', [], ['data', 'boxed', 'if'])
1163 normalize_members(expr
.get('data'))
1165 raise QAPISemError(info
, "expression is missing metatype")
1168 check_name_is_str(name
, info
, "'%s'" % meta
)
1169 info
.set_defn(meta
, name
)
1170 check_defn_name_str(name
, info
, meta
)
1171 add_name(name
, info
, meta
)
1172 if doc
and doc
.symbol
!= name
:
1175 "definition of '%s' follows documentation for '%s'"
1176 % (name
, doc
.symbol
))
1178 # Validate that exprs make sense
1179 for expr_elem
in exprs
:
1180 expr
= expr_elem
['expr']
1181 info
= expr_elem
['info']
1182 doc
= expr_elem
.get('doc')
1184 if 'include' in expr
:
1187 check_enum(expr
, info
)
1188 elif 'union' in expr
:
1189 check_union(expr
, info
)
1190 elif 'alternate' in expr
:
1191 check_alternate(expr
, info
)
1192 elif 'struct' in expr
:
1193 check_struct(expr
, info
)
1194 elif 'command' in expr
:
1195 check_command(expr
, info
)
1196 elif 'event' in expr
:
1197 check_event(expr
, info
)
1199 assert False, 'unexpected meta type'
1202 doc
.check_expr(expr
)
1208 # Schema compiler frontend
1211 class QAPISchemaEntity(object):
1212 def __init__(self
, name
, info
, doc
, ifcond
=None):
1213 assert name
is None or isinstance(name
, str)
1216 # For explicitly defined entities, info points to the (explicit)
1217 # definition. For builtins (and their arrays), info is None.
1218 # For implicitly defined entities, info points to a place that
1219 # triggered the implicit definition (there may be more than one
1223 self
._ifcond
= ifcond
or []
1224 self
._checked
= False
1227 return c_name(self
.name
)
1229 def check(self
, schema
):
1230 assert not self
._checked
1232 self
._module
= os
.path
.relpath(self
.info
.fname
,
1233 os
.path
.dirname(schema
.fname
))
1234 self
._checked
= True
1238 assert self
._checked
1243 assert self
._checked
1246 def is_implicit(self
):
1247 return not self
.info
1249 def visit(self
, visitor
):
1250 assert self
._checked
1253 class QAPISchemaVisitor(object):
1254 def visit_begin(self
, schema
):
1257 def visit_end(self
):
1260 def visit_module(self
, fname
):
1263 def visit_needed(self
, entity
):
1264 # Default to visiting everything
1267 def visit_include(self
, fname
, info
):
1270 def visit_builtin_type(self
, name
, info
, json_type
):
1273 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
1276 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
1279 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
,
1283 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
1287 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
1290 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
1291 success_response
, boxed
, allow_oob
, allow_preconfig
):
1294 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
1298 class QAPISchemaInclude(QAPISchemaEntity
):
1300 def __init__(self
, fname
, info
):
1301 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1304 def visit(self
, visitor
):
1305 QAPISchemaEntity
.visit(self
, visitor
)
1306 visitor
.visit_include(self
.fname
, self
.info
)
1309 class QAPISchemaType(QAPISchemaEntity
):
1310 # Return the C type for common use.
1311 # For the types we commonly box, this is a pointer type.
1315 # Return the C type to be used in a parameter list.
1316 def c_param_type(self
):
1317 return self
.c_type()
1319 # Return the C type to be used where we suppress boxing.
1320 def c_unboxed_type(self
):
1321 return self
.c_type()
1323 def json_type(self
):
1326 def alternate_qtype(self
):
1328 'null': 'QTYPE_QNULL',
1329 'string': 'QTYPE_QSTRING',
1330 'number': 'QTYPE_QNUM',
1331 'int': 'QTYPE_QNUM',
1332 'boolean': 'QTYPE_QBOOL',
1333 'object': 'QTYPE_QDICT'
1335 return json2qtype
.get(self
.json_type())
1338 if self
.is_implicit():
1343 class QAPISchemaBuiltinType(QAPISchemaType
):
1344 def __init__(self
, name
, json_type
, c_type
):
1345 QAPISchemaType
.__init
__(self
, name
, None, None)
1346 assert not c_type
or isinstance(c_type
, str)
1347 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1349 self
._json
_type
_name
= json_type
1350 self
._c
_type
_name
= c_type
1356 return self
._c
_type
_name
1358 def c_param_type(self
):
1359 if self
.name
== 'str':
1360 return 'const ' + self
._c
_type
_name
1361 return self
._c
_type
_name
1363 def json_type(self
):
1364 return self
._json
_type
_name
1367 return self
.json_type()
1369 def visit(self
, visitor
):
1370 QAPISchemaType
.visit(self
, visitor
)
1371 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1374 class QAPISchemaEnumType(QAPISchemaType
):
1375 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
1376 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1378 assert isinstance(m
, QAPISchemaEnumMember
)
1379 m
.set_defined_in(name
)
1380 assert prefix
is None or isinstance(prefix
, str)
1381 self
.members
= members
1382 self
.prefix
= prefix
1384 def check(self
, schema
):
1385 QAPISchemaType
.check(self
, schema
)
1387 for m
in self
.members
:
1388 m
.check_clash(self
.info
, seen
)
1390 self
.doc
.connect_member(m
)
1392 def is_implicit(self
):
1393 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1394 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1397 return c_name(self
.name
)
1399 def member_names(self
):
1400 return [m
.name
for m
in self
.members
]
1402 def json_type(self
):
1405 def visit(self
, visitor
):
1406 QAPISchemaType
.visit(self
, visitor
)
1407 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
1408 self
.members
, self
.prefix
)
1411 class QAPISchemaArrayType(QAPISchemaType
):
1412 def __init__(self
, name
, info
, element_type
):
1413 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
1414 assert isinstance(element_type
, str)
1415 self
._element
_type
_name
= element_type
1416 self
.element_type
= None
1418 def check(self
, schema
):
1419 QAPISchemaType
.check(self
, schema
)
1420 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1421 assert self
.element_type
1422 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
1426 assert self
._checked
1427 return self
.element_type
.ifcond
1431 assert self
._checked
1432 return self
.element_type
.module
1434 def is_implicit(self
):
1438 return c_name(self
.name
) + pointer_suffix
1440 def json_type(self
):
1444 elt_doc_type
= self
.element_type
.doc_type()
1445 if not elt_doc_type
:
1447 return 'array of ' + elt_doc_type
1449 def visit(self
, visitor
):
1450 QAPISchemaType
.visit(self
, visitor
)
1451 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
1455 class QAPISchemaObjectType(QAPISchemaType
):
1456 def __init__(self
, name
, info
, doc
, ifcond
,
1457 base
, local_members
, variants
, features
):
1458 # struct has local_members, optional base, and no variants
1459 # flat union has base, variants, and no local_members
1460 # simple union has local_members, variants, and no base
1461 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1462 assert base
is None or isinstance(base
, str)
1463 for m
in local_members
:
1464 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1465 m
.set_defined_in(name
)
1466 if variants
is not None:
1467 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1468 variants
.set_defined_in(name
)
1470 assert isinstance(f
, QAPISchemaFeature
)
1471 f
.set_defined_in(name
)
1472 self
._base
_name
= base
1474 self
.local_members
= local_members
1475 self
.variants
= variants
1477 self
.features
= features
1479 def check(self
, schema
):
1480 # This calls another type T's .check() exactly when the C
1481 # struct emitted by gen_object() contains that T's C struct
1482 # (pointers don't count).
1483 if self
.members
is not None:
1484 # A previous .check() completed: nothing to do
1487 # Recursed: C struct contains itself
1488 raise QAPISemError(self
.info
,
1489 "object %s contains itself" % self
.name
)
1491 QAPISchemaType
.check(self
, schema
)
1492 assert self
._checked
and self
.members
is None
1494 seen
= OrderedDict()
1496 self
.base
= schema
.lookup_type(self
._base
_name
)
1497 assert isinstance(self
.base
, QAPISchemaObjectType
)
1498 self
.base
.check(schema
)
1499 self
.base
.check_clash(self
.info
, seen
)
1500 for m
in self
.local_members
:
1502 m
.check_clash(self
.info
, seen
)
1504 self
.doc
.connect_member(m
)
1505 members
= seen
.values()
1508 self
.variants
.check(schema
, seen
)
1509 assert self
.variants
.tag_member
in members
1510 self
.variants
.check_clash(self
.info
, seen
)
1512 # Features are in a name space separate from members
1514 for f
in self
.features
:
1515 f
.check_clash(self
.info
, seen
)
1520 self
.members
= members
# mark completed
1522 # Check that the members of this type do not cause duplicate JSON members,
1523 # and update seen to track the members seen so far. Report any errors
1524 # on behalf of info, which is not necessarily self.info
1525 def check_clash(self
, info
, seen
):
1526 assert self
._checked
1527 assert not self
.variants
# not implemented
1528 for m
in self
.members
:
1529 m
.check_clash(info
, seen
)
1533 assert self
._checked
1534 if isinstance(self
._ifcond
, QAPISchemaType
):
1535 # Simple union wrapper type inherits from wrapped type;
1536 # see _make_implicit_object_type()
1537 return self
._ifcond
.ifcond
1540 def is_implicit(self
):
1541 # See QAPISchema._make_implicit_object_type(), as well as
1542 # _def_predefineds()
1543 return self
.name
.startswith('q_')
1546 assert self
.members
is not None
1547 return not self
.members
and not self
.variants
1550 assert self
.name
!= 'q_empty'
1551 return QAPISchemaType
.c_name(self
)
1554 assert not self
.is_implicit()
1555 return c_name(self
.name
) + pointer_suffix
1557 def c_unboxed_type(self
):
1558 return c_name(self
.name
)
1560 def json_type(self
):
1563 def visit(self
, visitor
):
1564 QAPISchemaType
.visit(self
, visitor
)
1565 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
1566 self
.base
, self
.local_members
, self
.variants
,
1568 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
1569 self
.members
, self
.variants
,
1573 class QAPISchemaMember(object):
1574 """ Represents object members, enum members and features """
1577 def __init__(self
, name
, info
, ifcond
=None):
1578 assert isinstance(name
, str)
1581 self
.ifcond
= ifcond
or []
1582 self
.defined_in
= None
1584 def set_defined_in(self
, name
):
1585 assert not self
.defined_in
1586 self
.defined_in
= name
1588 def check_clash(self
, info
, seen
):
1589 cname
= c_name(self
.name
)
1593 "%s collides with %s"
1594 % (self
.describe(info
), seen
[cname
].describe(info
)))
1597 def describe(self
, info
):
1599 defined_in
= self
.defined_in
1602 if defined_in
.startswith('q_obj_'):
1603 # See QAPISchema._make_implicit_object_type() - reverse the
1604 # mapping there to create a nice human-readable description
1605 defined_in
= defined_in
[6:]
1606 if defined_in
.endswith('-arg'):
1607 # Implicit type created for a command's dict 'data'
1608 assert role
== 'member'
1610 elif defined_in
.endswith('-base'):
1611 # Implicit type created for a flat union's dict 'base'
1612 role
= 'base ' + role
1614 # Implicit type created for a simple union's branch
1615 assert defined_in
.endswith('-wrapper')
1616 # Unreachable and not implemented
1618 elif defined_in
.endswith('Kind'):
1619 # See QAPISchema._make_implicit_enum_type()
1620 # Implicit enum created for simple union's branches
1621 assert role
== 'value'
1623 elif defined_in
!= info
.defn_name
:
1624 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
1625 return "%s '%s'" % (role
, self
.name
)
1628 class QAPISchemaEnumMember(QAPISchemaMember
):
1632 class QAPISchemaFeature(QAPISchemaMember
):
1636 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1637 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None):
1638 QAPISchemaMember
.__init
__(self
, name
, info
, ifcond
)
1639 assert isinstance(typ
, str)
1640 assert isinstance(optional
, bool)
1641 self
._type
_name
= typ
1643 self
.optional
= optional
1645 def check(self
, schema
):
1646 assert self
.defined_in
1647 self
.type = schema
.lookup_type(self
._type
_name
)
1651 class QAPISchemaObjectTypeVariants(object):
1652 def __init__(self
, tag_name
, info
, tag_member
, variants
):
1653 # Flat unions pass tag_name but not tag_member.
1654 # Simple unions and alternates pass tag_member but not tag_name.
1655 # After check(), tag_member is always set, and tag_name remains
1656 # a reliable witness of being used by a flat union.
1657 assert bool(tag_member
) != bool(tag_name
)
1658 assert (isinstance(tag_name
, str) or
1659 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1661 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1662 self
._tag
_name
= tag_name
1664 self
.tag_member
= tag_member
1665 self
.variants
= variants
1667 def set_defined_in(self
, name
):
1668 for v
in self
.variants
:
1669 v
.set_defined_in(name
)
1671 def check(self
, schema
, seen
):
1672 if not self
.tag_member
: # flat union
1673 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1674 assert self
._tag
_name
== self
.tag_member
.name
1675 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1676 assert not self
.tag_member
.optional
1677 assert self
.tag_member
.ifcond
== []
1678 if self
._tag
_name
: # flat union
1679 # branches that are not explicitly covered get an empty type
1680 cases
= set([v
.name
for v
in self
.variants
])
1681 for m
in self
.tag_member
.type.members
:
1682 if m
.name
not in cases
:
1683 v
= QAPISchemaObjectTypeVariant(m
.name
, self
.info
,
1684 'q_empty', m
.ifcond
)
1685 v
.set_defined_in(self
.tag_member
.defined_in
)
1686 self
.variants
.append(v
)
1687 assert self
.variants
1688 for v
in self
.variants
:
1690 # Union names must match enum values; alternate names are
1691 # checked separately. Use 'seen' to tell the two apart.
1693 assert v
.name
in self
.tag_member
.type.member_names()
1694 assert (isinstance(v
.type, QAPISchemaObjectType
)
1695 and not v
.type.variants
)
1696 v
.type.check(schema
)
1698 def check_clash(self
, info
, seen
):
1699 for v
in self
.variants
:
1700 # Reset seen map for each variant, since qapi names from one
1701 # branch do not affect another branch
1702 v
.type.check_clash(info
, dict(seen
))
1705 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1708 def __init__(self
, name
, info
, typ
, ifcond
=None):
1709 QAPISchemaObjectTypeMember
.__init
__(self
, name
, info
, typ
,
1713 class QAPISchemaAlternateType(QAPISchemaType
):
1714 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
1715 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1716 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1717 assert variants
.tag_member
1718 variants
.set_defined_in(name
)
1719 variants
.tag_member
.set_defined_in(self
.name
)
1720 self
.variants
= variants
1722 def check(self
, schema
):
1723 QAPISchemaType
.check(self
, schema
)
1724 self
.variants
.tag_member
.check(schema
)
1725 # Not calling self.variants.check_clash(), because there's nothing
1727 self
.variants
.check(schema
, {})
1728 # Alternate branch names have no relation to the tag enum values;
1729 # so we have to check for potential name collisions ourselves.
1731 for v
in self
.variants
.variants
:
1732 v
.check_clash(self
.info
, seen
)
1733 # TODO check conflicting qtypes
1735 self
.doc
.connect_member(v
)
1740 return c_name(self
.name
) + pointer_suffix
1742 def json_type(self
):
1745 def visit(self
, visitor
):
1746 QAPISchemaType
.visit(self
, visitor
)
1747 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
1751 class QAPISchemaCommand(QAPISchemaEntity
):
1752 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
1753 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
1754 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1755 assert not arg_type
or isinstance(arg_type
, str)
1756 assert not ret_type
or isinstance(ret_type
, str)
1757 self
._arg
_type
_name
= arg_type
1758 self
.arg_type
= None
1759 self
._ret
_type
_name
= ret_type
1760 self
.ret_type
= None
1762 self
.success_response
= success_response
1764 self
.allow_oob
= allow_oob
1765 self
.allow_preconfig
= allow_preconfig
1767 def check(self
, schema
):
1768 QAPISchemaEntity
.check(self
, schema
)
1769 if self
._arg
_type
_name
:
1770 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1771 assert isinstance(self
.arg_type
, QAPISchemaObjectType
)
1772 assert not self
.arg_type
.variants
or self
.boxed
1774 raise QAPISemError(self
.info
, "use of 'boxed' requires 'data'")
1775 if self
._ret
_type
_name
:
1776 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1777 assert isinstance(self
.ret_type
, QAPISchemaType
)
1779 def visit(self
, visitor
):
1780 QAPISchemaEntity
.visit(self
, visitor
)
1781 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
1782 self
.arg_type
, self
.ret_type
,
1783 self
.gen
, self
.success_response
,
1784 self
.boxed
, self
.allow_oob
,
1785 self
.allow_preconfig
)
1788 class QAPISchemaEvent(QAPISchemaEntity
):
1789 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
1790 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1791 assert not arg_type
or isinstance(arg_type
, str)
1792 self
._arg
_type
_name
= arg_type
1793 self
.arg_type
= None
1796 def check(self
, schema
):
1797 QAPISchemaEntity
.check(self
, schema
)
1798 if self
._arg
_type
_name
:
1799 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1800 assert isinstance(self
.arg_type
, QAPISchemaObjectType
)
1801 assert not self
.arg_type
.variants
or self
.boxed
1803 raise QAPISemError(self
.info
, "use of 'boxed' requires 'data'")
1805 def visit(self
, visitor
):
1806 QAPISchemaEntity
.visit(self
, visitor
)
1807 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
1808 self
.arg_type
, self
.boxed
)
1811 class QAPISchema(object):
1812 def __init__(self
, fname
):
1814 if sys
.version_info
[0] >= 3:
1815 f
= open(fname
, 'r', encoding
='utf-8')
1817 f
= open(fname
, 'r')
1818 parser
= QAPISchemaParser(f
)
1819 exprs
= check_exprs(parser
.exprs
)
1820 self
.docs
= parser
.docs
1821 self
._entity
_list
= []
1822 self
._entity
_dict
= {}
1823 self
._predefining
= True
1824 self
._def
_predefineds
()
1825 self
._predefining
= False
1826 self
._def
_exprs
(exprs
)
1829 def _def_entity(self
, ent
):
1830 # Only the predefined types are allowed to not have info
1831 assert ent
.info
or self
._predefining
1832 assert ent
.name
is None or ent
.name
not in self
._entity
_dict
1833 self
._entity
_list
.append(ent
)
1834 if ent
.name
is not None:
1835 self
._entity
_dict
[ent
.name
] = ent
1837 def lookup_entity(self
, name
, typ
=None):
1838 ent
= self
._entity
_dict
.get(name
)
1839 if typ
and not isinstance(ent
, typ
):
1843 def lookup_type(self
, name
):
1844 return self
.lookup_entity(name
, QAPISchemaType
)
1846 def _def_include(self
, expr
, info
, doc
):
1847 include
= expr
['include']
1850 while main_info
.parent
:
1851 main_info
= main_info
.parent
1852 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
.fname
))
1853 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
1855 def _def_builtin_type(self
, name
, json_type
, c_type
):
1856 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1857 # Instantiating only the arrays that are actually used would
1858 # be nice, but we can't as long as their generated code
1859 # (qapi-builtin-types.[ch]) may be shared by some other
1861 self
._make
_array
_type
(name
, None)
1863 def _def_predefineds(self
):
1864 for t
in [('str', 'string', 'char' + pointer_suffix
),
1865 ('number', 'number', 'double'),
1866 ('int', 'int', 'int64_t'),
1867 ('int8', 'int', 'int8_t'),
1868 ('int16', 'int', 'int16_t'),
1869 ('int32', 'int', 'int32_t'),
1870 ('int64', 'int', 'int64_t'),
1871 ('uint8', 'int', 'uint8_t'),
1872 ('uint16', 'int', 'uint16_t'),
1873 ('uint32', 'int', 'uint32_t'),
1874 ('uint64', 'int', 'uint64_t'),
1875 ('size', 'int', 'uint64_t'),
1876 ('bool', 'boolean', 'bool'),
1877 ('any', 'value', 'QObject' + pointer_suffix
),
1878 ('null', 'null', 'QNull' + pointer_suffix
)]:
1879 self
._def
_builtin
_type
(*t
)
1880 self
.the_empty_object_type
= QAPISchemaObjectType(
1881 'q_empty', None, None, None, None, [], None, [])
1882 self
._def
_entity
(self
.the_empty_object_type
)
1884 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1886 qtype_values
= self
._make
_enum
_members
(
1887 [{'name': n
} for n
in qtypes
], None)
1889 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
1890 qtype_values
, 'QTYPE'))
1892 def _make_features(self
, features
, info
):
1893 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
1896 def _make_enum_members(self
, values
, info
):
1897 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
1900 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
1901 # See also QAPISchemaObjectTypeMember.describe()
1902 name
= name
+ 'Kind' # reserved by check_defn_name_str()
1903 self
._def
_entity
(QAPISchemaEnumType(
1904 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
, info
),
1908 def _make_array_type(self
, element_type
, info
):
1909 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1910 if not self
.lookup_type(name
):
1911 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1914 def _make_implicit_object_type(self
, name
, info
, doc
, ifcond
,
1918 # See also QAPISchemaObjectTypeMember.describe()
1919 name
= 'q_obj_%s-%s' % (name
, role
)
1920 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1922 # The implicit object type has multiple users. This can
1923 # happen only for simple unions' implicit wrapper types.
1924 # Its ifcond should be the disjunction of its user's
1925 # ifconds. Not implemented. Instead, we always pass the
1926 # wrapped type's ifcond, which is trivially the same for all
1927 # users. It's also necessary for the wrapper to compile.
1928 # But it's not tight: the disjunction need not imply it. We
1929 # may end up compiling useless wrapper types.
1930 # TODO kill simple unions or implement the disjunction
1931 assert ifcond
== typ
._ifcond
# pylint: disable=protected-access
1933 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
,
1934 None, members
, None, []))
1937 def _def_enum_type(self
, expr
, info
, doc
):
1940 prefix
= expr
.get('prefix')
1941 ifcond
= expr
.get('if')
1942 self
._def
_entity
(QAPISchemaEnumType(
1943 name
, info
, doc
, ifcond
,
1944 self
._make
_enum
_members
(data
, info
), prefix
))
1946 def _make_member(self
, name
, typ
, ifcond
, info
):
1948 if name
.startswith('*'):
1951 if isinstance(typ
, list):
1952 assert len(typ
) == 1
1953 typ
= self
._make
_array
_type
(typ
[0], info
)
1954 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
)
1956 def _make_members(self
, data
, info
):
1957 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
1958 for (key
, value
) in data
.items()]
1960 def _def_struct_type(self
, expr
, info
, doc
):
1961 name
= expr
['struct']
1962 base
= expr
.get('base')
1964 ifcond
= expr
.get('if')
1965 features
= expr
.get('features', [])
1966 self
._def
_entity
(QAPISchemaObjectType(
1967 name
, info
, doc
, ifcond
, base
,
1968 self
._make
_members
(data
, info
),
1970 self
._make
_features
(features
, info
)))
1972 def _make_variant(self
, case
, typ
, ifcond
, info
):
1973 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1975 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1976 if isinstance(typ
, list):
1977 assert len(typ
) == 1
1978 typ
= self
._make
_array
_type
(typ
[0], info
)
1979 typ
= self
._make
_implicit
_object
_type
(
1980 typ
, info
, None, self
.lookup_type(typ
),
1981 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
1982 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1984 def _def_union_type(self
, expr
, info
, doc
):
1985 name
= expr
['union']
1987 base
= expr
.get('base')
1988 ifcond
= expr
.get('if')
1989 tag_name
= expr
.get('discriminator')
1991 if isinstance(base
, dict):
1992 base
= self
._make
_implicit
_object
_type
(
1993 name
, info
, doc
, ifcond
,
1994 'base', self
._make
_members
(base
, info
))
1996 variants
= [self
._make
_variant
(key
, value
['type'],
1997 value
.get('if'), info
)
1998 for (key
, value
) in data
.items()]
2001 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
2002 value
.get('if'), info
)
2003 for (key
, value
) in data
.items()]
2004 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
2005 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
2006 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
2007 members
= [tag_member
]
2009 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
2010 QAPISchemaObjectTypeVariants(
2011 tag_name
, info
, tag_member
, variants
),
2014 def _def_alternate_type(self
, expr
, info
, doc
):
2015 name
= expr
['alternate']
2017 ifcond
= expr
.get('if')
2018 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
2020 for (key
, value
) in data
.items()]
2021 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
2023 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
2024 QAPISchemaObjectTypeVariants(
2025 None, info
, tag_member
, variants
)))
2027 def _def_command(self
, expr
, info
, doc
):
2028 name
= expr
['command']
2029 data
= expr
.get('data')
2030 rets
= expr
.get('returns')
2031 gen
= expr
.get('gen', True)
2032 success_response
= expr
.get('success-response', True)
2033 boxed
= expr
.get('boxed', False)
2034 allow_oob
= expr
.get('allow-oob', False)
2035 allow_preconfig
= expr
.get('allow-preconfig', False)
2036 ifcond
= expr
.get('if')
2037 if isinstance(data
, OrderedDict
):
2038 data
= self
._make
_implicit
_object
_type
(
2039 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
2040 if isinstance(rets
, list):
2041 assert len(rets
) == 1
2042 rets
= self
._make
_array
_type
(rets
[0], info
)
2043 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
2044 gen
, success_response
,
2045 boxed
, allow_oob
, allow_preconfig
))
2047 def _def_event(self
, expr
, info
, doc
):
2048 name
= expr
['event']
2049 data
= expr
.get('data')
2050 boxed
= expr
.get('boxed', False)
2051 ifcond
= expr
.get('if')
2052 if isinstance(data
, OrderedDict
):
2053 data
= self
._make
_implicit
_object
_type
(
2054 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
2055 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
2057 def _def_exprs(self
, exprs
):
2058 for expr_elem
in exprs
:
2059 expr
= expr_elem
['expr']
2060 info
= expr_elem
['info']
2061 doc
= expr_elem
.get('doc')
2063 self
._def
_enum
_type
(expr
, info
, doc
)
2064 elif 'struct' in expr
:
2065 self
._def
_struct
_type
(expr
, info
, doc
)
2066 elif 'union' in expr
:
2067 self
._def
_union
_type
(expr
, info
, doc
)
2068 elif 'alternate' in expr
:
2069 self
._def
_alternate
_type
(expr
, info
, doc
)
2070 elif 'command' in expr
:
2071 self
._def
_command
(expr
, info
, doc
)
2072 elif 'event' in expr
:
2073 self
._def
_event
(expr
, info
, doc
)
2074 elif 'include' in expr
:
2075 self
._def
_include
(expr
, info
, doc
)
2080 for ent
in self
._entity
_list
:
2083 def visit(self
, visitor
):
2084 visitor
.visit_begin(self
)
2086 visitor
.visit_module(module
)
2087 for entity
in self
._entity
_list
:
2088 if visitor
.visit_needed(entity
):
2089 if entity
.module
!= module
:
2090 module
= entity
.module
2091 visitor
.visit_module(module
)
2092 entity
.visit(visitor
)
2097 # Code generation helpers
2100 def camel_case(name
):
2104 if ch
in ['_', '-']:
2107 new_name
+= ch
.upper()
2110 new_name
+= ch
.lower()
2114 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2115 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2116 # ENUM24_Name -> ENUM24_NAME
2117 def camel_to_upper(value
):
2118 c_fun_str
= c_name(value
, False)
2123 length
= len(c_fun_str
)
2124 for i
in range(length
):
2126 # When c is upper and no '_' appears before, do more checks
2127 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
2128 if i
< length
- 1 and c_fun_str
[i
+ 1].islower():
2130 elif c_fun_str
[i
- 1].isdigit():
2133 return new_name
.lstrip('_').upper()
2136 def c_enum_const(type_name
, const_name
, prefix
=None):
2137 if prefix
is not None:
2139 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
2142 if hasattr(str, 'maketrans'):
2143 c_name_trans
= str.maketrans('.-', '__')
2145 c_name_trans
= string
.maketrans('.-', '__')
2148 # Map @name to a valid C identifier.
2149 # If @protect, avoid returning certain ticklish identifiers (like
2150 # C keywords) by prepending 'q_'.
2152 # Used for converting 'name' from a 'name':'type' qapi definition
2153 # into a generated struct member, as well as converting type names
2154 # into substrings of a generated C function name.
2155 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2156 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2157 def c_name(name
, protect
=True):
2158 # ANSI X3J11/88-090, 3.1.1
2159 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
2160 'default', 'do', 'double', 'else', 'enum', 'extern',
2161 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2162 'return', 'short', 'signed', 'sizeof', 'static',
2163 'struct', 'switch', 'typedef', 'union', 'unsigned',
2164 'void', 'volatile', 'while'])
2165 # ISO/IEC 9899:1999, 6.4.1
2166 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2167 # ISO/IEC 9899:2011, 6.4.1
2168 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2169 '_Noreturn', '_Static_assert', '_Thread_local'])
2170 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2172 gcc_words
= set(['asm', 'typeof'])
2173 # C++ ISO/IEC 14882:2003 2.11
2174 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
2175 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2176 'namespace', 'new', 'operator', 'private', 'protected',
2177 'public', 'reinterpret_cast', 'static_cast', 'template',
2178 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2179 'using', 'virtual', 'wchar_t',
2180 # alternative representations
2181 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2182 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2183 # namespace pollution:
2184 polluted_words
= set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2185 name
= name
.translate(c_name_trans
)
2186 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
2187 | cpp_words | polluted_words
):
2192 eatspace
= '\033EATSPACE.'
2193 pointer_suffix
= ' *' + eatspace
2196 def genindent(count
):
2198 for _
in range(count
):
2206 def push_indent(indent_amount
=4):
2208 indent_level
+= indent_amount
2211 def pop_indent(indent_amount
=4):
2213 indent_level
-= indent_amount
2216 # Generate @code with @kwds interpolated.
2217 # Obey indent_level, and strip eatspace.
2218 def cgen(code
, **kwds
):
2221 indent
= genindent(indent_level
)
2222 # re.subn() lacks flags support before Python 2.7, use re.compile()
2223 raw
= re
.subn(re
.compile(r
'^(?!(#|$))', re
.MULTILINE
),
2226 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
2229 def mcgen(code
, **kwds
):
2232 return cgen(code
, **kwds
)
2235 def c_fname(filename
):
2236 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
)
2239 def guardstart(name
):
2245 name
=c_fname(name
).upper())
2251 #endif /* %(name)s */
2253 name
=c_fname(name
).upper())
2265 def gen_endif(ifcond
):
2267 for ifc
in reversed(ifcond
):
2269 #endif /* %(cond)s */
2274 def _wrap_ifcond(ifcond
, before
, after
):
2276 return after
# suppress empty #if ... #endif
2278 assert after
.startswith(before
)
2280 added
= after
[len(before
):]
2281 if added
[0] == '\n':
2284 out
+= gen_if(ifcond
)
2286 out
+= gen_endif(ifcond
)
2290 def gen_enum_lookup(name
, members
, prefix
=None):
2293 const QEnumLookup %(c_name)s_lookup = {
2294 .array = (const char *const[]) {
2296 c_name
=c_name(name
))
2298 ret
+= gen_if(m
.ifcond
)
2299 index
= c_enum_const(name
, m
.name
, prefix
)
2301 [%(index)s] = "%(name)s",
2303 index
=index
, name
=m
.name
)
2304 ret
+= gen_endif(m
.ifcond
)
2308 .size = %(max_index)s
2311 max_index
=c_enum_const(name
, '_MAX', prefix
))
2315 def gen_enum(name
, members
, prefix
=None):
2316 # append automatically generated _MAX value
2317 enum_members
= members
+ [QAPISchemaEnumMember('_MAX', None)]
2321 typedef enum %(c_name)s {
2323 c_name
=c_name(name
))
2325 for m
in enum_members
:
2326 ret
+= gen_if(m
.ifcond
)
2330 c_enum
=c_enum_const(name
, m
.name
, prefix
))
2331 ret
+= gen_endif(m
.ifcond
)
2336 c_name
=c_name(name
))
2340 #define %(c_name)s_str(val) \\
2341 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2343 extern const QEnumLookup %(c_name)s_lookup;
2345 c_name
=c_name(name
))
2349 def build_params(arg_type
, boxed
, extra
=None):
2354 ret
+= '%s arg' % arg_type
.c_param_type()
2357 assert not arg_type
.variants
2358 for memb
in arg_type
.members
:
2362 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
2363 ret
+= '%s %s' % (memb
.type.c_param_type(),
2367 return ret
if ret
else 'void'
2371 # Accumulate and write output
2374 class QAPIGen(object):
2376 def __init__(self
, fname
):
2381 def preamble_add(self
, text
):
2382 self
._preamble
+= text
2384 def add(self
, text
):
2387 def get_content(self
):
2388 return self
._top
() + self
._preamble
+ self
._body
+ self
._bottom
()
2396 def write(self
, output_dir
):
2397 pathname
= os
.path
.join(output_dir
, self
.fname
)
2398 dir = os
.path
.dirname(pathname
)
2402 except os
.error
as e
:
2403 if e
.errno
!= errno
.EEXIST
:
2405 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2406 if sys
.version_info
[0] >= 3:
2407 f
= open(fd
, 'r+', encoding
='utf-8')
2409 f
= os
.fdopen(fd
, 'r+')
2410 text
= self
.get_content()
2411 oldtext
= f
.read(len(text
) + 1)
2420 def ifcontext(ifcond
, *args
):
2421 """A 'with' statement context manager to wrap with start_if()/end_if()
2423 *args: any number of QAPIGenCCode
2427 with ifcontext(ifcond, self._genh, self._genc):
2428 modify self._genh and self._genc ...
2430 Is equivalent to calling::
2432 self._genh.start_if(ifcond)
2433 self._genc.start_if(ifcond)
2434 modify self._genh and self._genc ...
2439 arg
.start_if(ifcond
)
2445 class QAPIGenCCode(QAPIGen
):
2447 def __init__(self
, fname
):
2448 QAPIGen
.__init
__(self
, fname
)
2449 self
._start
_if
= None
2451 def start_if(self
, ifcond
):
2452 assert self
._start
_if
is None
2453 self
._start
_if
= (ifcond
, self
._body
, self
._preamble
)
2456 assert self
._start
_if
2458 self
._start
_if
= None
2460 def _wrap_ifcond(self
):
2461 self
._body
= _wrap_ifcond(self
._start
_if
[0],
2462 self
._start
_if
[1], self
._body
)
2463 self
._preamble
= _wrap_ifcond(self
._start
_if
[0],
2464 self
._start
_if
[2], self
._preamble
)
2466 def get_content(self
):
2467 assert self
._start
_if
is None
2468 return QAPIGen
.get_content(self
)
2471 class QAPIGenC(QAPIGenCCode
):
2473 def __init__(self
, fname
, blurb
, pydoc
):
2474 QAPIGenCCode
.__init
__(self
, fname
)
2476 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2481 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2488 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2489 * See the COPYING.LIB file in the top-level directory.
2493 blurb
=self
._blurb
, copyright
=self
._copyright
)
2498 /* Dummy declaration to prevent empty .o file */
2499 char qapi_dummy_%(name)s;
2501 name
=c_fname(self
.fname
))
2504 class QAPIGenH(QAPIGenC
):
2507 return QAPIGenC
._top
(self
) + guardstart(self
.fname
)
2510 return guardend(self
.fname
)
2513 class QAPIGenDoc(QAPIGen
):
2516 return (QAPIGen
._top
(self
)
2517 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2520 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2522 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2523 self
._prefix
= prefix
2525 self
._genc
= QAPIGenC(self
._prefix
+ self
._what
+ '.c',
2527 self
._genh
= QAPIGenH(self
._prefix
+ self
._what
+ '.h',
2530 def write(self
, output_dir
):
2531 self
._genc
.write(output_dir
)
2532 self
._genh
.write(output_dir
)
2535 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2537 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2538 self
._prefix
= prefix
2545 self
._main
_module
= None
2548 def _is_user_module(name
):
2549 return name
and not name
.startswith('./')
2552 def _is_builtin_module(name
):
2555 def _module_dirname(self
, what
, name
):
2556 if self
._is
_user
_module
(name
):
2557 return os
.path
.dirname(name
)
2560 def _module_basename(self
, what
, name
):
2561 ret
= '' if self
._is
_builtin
_module
(name
) else self
._prefix
2562 if self
._is
_user
_module
(name
):
2563 basename
= os
.path
.basename(name
)
2565 if name
!= self
._main
_module
:
2566 ret
+= '-' + os
.path
.splitext(basename
)[0]
2568 name
= name
[2:] if name
else 'builtin'
2569 ret
+= re
.sub(r
'-', '-' + name
+ '-', what
)
2572 def _module_filename(self
, what
, name
):
2573 return os
.path
.join(self
._module
_dirname
(what
, name
),
2574 self
._module
_basename
(what
, name
))
2576 def _add_module(self
, name
, blurb
):
2577 basename
= self
._module
_filename
(self
._what
, name
)
2578 genc
= QAPIGenC(basename
+ '.c', blurb
, self
._pydoc
)
2579 genh
= QAPIGenH(basename
+ '.h', blurb
, self
._pydoc
)
2580 self
._module
[name
] = (genc
, genh
)
2581 self
._set
_module
(name
)
2583 def _add_user_module(self
, name
, blurb
):
2584 assert self
._is
_user
_module
(name
)
2585 if self
._main
_module
is None:
2586 self
._main
_module
= name
2587 self
._add
_module
(name
, blurb
)
2589 def _add_system_module(self
, name
, blurb
):
2590 self
._add
_module
(name
and './' + name
, blurb
)
2592 def _set_module(self
, name
):
2593 self
._genc
, self
._genh
= self
._module
[name
]
2595 def write(self
, output_dir
, opt_builtins
=False):
2596 for name
in self
._module
:
2597 if self
._is
_builtin
_module
(name
) and not opt_builtins
:
2599 (genc
, genh
) = self
._module
[name
]
2600 genc
.write(output_dir
)
2601 genh
.write(output_dir
)
2603 def _begin_user_module(self
, name
):
2606 def visit_module(self
, name
):
2607 if name
in self
._module
:
2608 self
._set
_module
(name
)
2609 elif self
._is
_builtin
_module
(name
):
2610 # The built-in module has not been created. No code may
2615 self
._add
_user
_module
(name
, self
._blurb
)
2616 self
._begin
_user
_module
(name
)
2618 def visit_include(self
, name
, info
):
2619 relname
= os
.path
.relpath(self
._module
_filename
(self
._what
, name
),
2620 os
.path
.dirname(self
._genh
.fname
))
2621 self
._genh
.preamble_add(mcgen('''
2622 #include "%(relname)s.h"