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(info
, source
, name
,
710 allow_optional
=False, enum_member
=False, permit_upper
=False):
714 if not isinstance(name
, str):
715 raise QAPISemError(info
, "%s requires a string name" % source
)
716 if name
.startswith('*'):
717 membername
= name
[1:]
718 if not allow_optional
:
719 raise QAPISemError(info
, "%s does not allow optional name '%s'"
721 # Enum members can start with a digit, because the generated C
722 # code always prefixes it with the enum name
723 if enum_member
and membername
[0].isdigit():
724 membername
= 'D' + membername
725 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
726 # and 'q_obj_*' implicit type names.
727 if not valid_name
.match(membername
) or \
728 c_name(membername
, False).startswith('q_'):
729 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
730 if not permit_upper
and name
.lower() != name
:
732 info
, "%s uses uppercase in name '%s'" % (source
, name
))
735 def add_name(name
, info
, meta
):
737 check_name(info
, "'%s'" % meta
, name
, permit_upper
=True)
738 # FIXME should reject names that differ only in '_' vs. '.'
739 # vs. '-', because they're liable to clash in generated C.
740 if name
in all_names
:
741 raise QAPISemError(info
, "%s '%s' is already defined"
742 % (all_names
[name
], name
))
743 if name
.endswith('Kind') or name
.endswith('List'):
744 raise QAPISemError(info
, "%s '%s' should not end in '%s'"
745 % (meta
, name
, name
[-4:]))
746 all_names
[name
] = meta
749 def check_if(expr
, info
):
751 def check_if_str(ifcond
, info
):
752 if not isinstance(ifcond
, str):
754 info
, "'if' condition must be a string or a list of strings")
755 if ifcond
.strip() == '':
756 raise QAPISemError(info
, "'if' condition '%s' makes no sense"
759 ifcond
= expr
.get('if')
762 if isinstance(ifcond
, list):
764 raise QAPISemError(info
, "'if' condition [] is useless")
766 check_if_str(elt
, info
)
768 check_if_str(ifcond
, info
)
771 def check_type(info
, source
, value
,
772 allow_array
=False, allow_dict
=False, allow_metas
=[]):
778 # Check if array type for value is okay
779 if isinstance(value
, list):
781 raise QAPISemError(info
, "%s cannot be an array" % source
)
782 if len(value
) != 1 or not isinstance(value
[0], str):
783 raise QAPISemError(info
,
784 "%s: array type must contain single type name" %
788 # Check if type name for value is okay
789 if isinstance(value
, str):
790 if value
not in all_names
:
791 raise QAPISemError(info
, "%s uses unknown type '%s'"
793 if not all_names
[value
] in allow_metas
:
794 raise QAPISemError(info
, "%s cannot use %s type '%s'" %
795 (source
, all_names
[value
], value
))
799 raise QAPISemError(info
, "%s should be a type name" % source
)
801 if not isinstance(value
, OrderedDict
):
802 raise QAPISemError(info
,
803 "%s should be an object or type name" % source
)
805 permit_upper
= allow_dict
in name_case_whitelist
807 # value is a dictionary, check that each member is okay
808 for (key
, arg
) in value
.items():
809 check_name(info
, "member of %s" % source
, key
,
810 allow_optional
=True, permit_upper
=permit_upper
)
811 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
813 info
, "member of %s uses reserved name '%s'" % (source
, key
))
814 # Todo: allow dictionaries to represent default values of
815 # an optional argument.
816 check_known_keys(info
, "member '%s' of %s" % (key
, source
),
817 arg
, ['type'], ['if'])
820 check_type(info
, "member '%s' of %s" % (key
, source
),
821 arg
['type'], allow_array
=True,
822 allow_metas
=['built-in', 'union', 'alternate', 'struct',
826 def check_command(expr
, info
):
827 name
= expr
['command']
828 boxed
= expr
.get('boxed', False)
830 args_meta
= ['struct']
832 args_meta
+= ['union']
833 check_type(info
, "'data' for command '%s'" % name
,
834 expr
.get('data'), allow_dict
=not boxed
,
835 allow_metas
=args_meta
)
836 returns_meta
= ['union', 'struct']
837 if name
in returns_whitelist
:
838 returns_meta
+= ['built-in', 'alternate', 'enum']
839 check_type(info
, "'returns' for command '%s'" % name
,
840 expr
.get('returns'), allow_array
=True,
841 allow_metas
=returns_meta
)
844 def check_event(expr
, info
):
846 boxed
= expr
.get('boxed', False)
851 check_type(info
, "'data' for event '%s'" % name
,
852 expr
.get('data'), allow_dict
=not boxed
,
856 def enum_get_names(expr
):
857 return [e
['name'] for e
in expr
['data']]
860 def check_union(expr
, info
):
862 base
= expr
.get('base')
863 discriminator
= expr
.get('discriminator')
864 members
= expr
['data']
866 # Two types of unions, determined by discriminator.
868 # With no discriminator it is a simple union.
869 if discriminator
is None:
870 enum_values
= members
.keys()
871 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
874 info
, "simple union '%s' must not have a base" % name
)
876 # Else, it's a flat union.
878 # The object must have a string or dictionary 'base'.
879 check_type(info
, "'base' for union '%s'" % name
,
880 base
, allow_dict
=name
,
881 allow_metas
=['struct'])
884 info
, "flat union '%s' must have a base" % name
)
885 base_members
= find_base_members(base
)
886 assert base_members
is not None
888 # The value of member 'discriminator' must name a non-optional
889 # member of the base struct.
890 check_name(info
, "discriminator of flat union '%s'" % name
,
892 discriminator_value
= base_members
.get(discriminator
)
893 if not discriminator_value
:
894 raise QAPISemError(info
,
895 "discriminator '%s' is not a member of 'base'"
897 if discriminator_value
.get('if'):
900 "the discriminator '%s' for union %s must not be conditional"
901 % (discriminator
, name
))
902 enum_define
= enum_types
.get(discriminator_value
['type'])
903 # Do not allow string discriminator
907 "discriminator '%s' must be of enumeration type"
909 enum_values
= enum_get_names(enum_define
)
910 allow_metas
= ['struct']
912 if (len(enum_values
) == 0):
913 raise QAPISemError(info
, "union '%s' has no branches" % name
)
915 for (key
, value
) in members
.items():
916 check_name(info
, "member of union '%s'" % name
, key
)
918 check_known_keys(info
, "member '%s' of union '%s'" % (key
, name
),
919 value
, ['type'], ['if'])
920 check_if(value
, info
)
922 # Each value must name a known type
923 check_type(info
, "member '%s' of union '%s'" % (key
, name
),
925 allow_array
=not base
, allow_metas
=allow_metas
)
927 # If the discriminator names an enum type, then all members
928 # of 'data' must also be members of the enum type.
929 if discriminator
is not None:
930 if key
not in enum_values
:
933 "discriminator value '%s' is not found in enum '%s'"
934 % (key
, enum_define
['enum']))
937 def check_alternate(expr
, info
):
938 name
= expr
['alternate']
939 members
= expr
['data']
942 if len(members
) == 0:
943 raise QAPISemError(info
,
944 "alternate '%s' cannot have empty 'data'" % name
)
945 for (key
, value
) in members
.items():
946 check_name(info
, "member of alternate '%s'" % name
, key
)
947 check_known_keys(info
,
948 "member '%s' of alternate '%s'" % (key
, name
),
949 value
, ['type'], ['if'])
950 check_if(value
, info
)
954 # Ensure alternates have no type conflicts.
955 check_type(info
, "member '%s' of alternate '%s'" % (key
, name
), typ
,
956 allow_metas
=['built-in', 'union', 'struct', 'enum'])
957 qtype
= find_alternate_member_qtype(typ
)
961 "alternate '%s' member '%s' cannot use type '%s'"
963 conflicting
= set([qtype
])
964 if qtype
== 'QTYPE_QSTRING':
965 enum_expr
= enum_types
.get(typ
)
967 for v
in enum_get_names(enum_expr
):
968 if v
in ['on', 'off']:
969 conflicting
.add('QTYPE_QBOOL')
970 if re
.match(r
'[-+0-9.]', v
): # lazy, could be tightened
971 conflicting
.add('QTYPE_QNUM')
973 conflicting
.add('QTYPE_QNUM')
974 conflicting
.add('QTYPE_QBOOL')
975 for qt
in conflicting
:
979 "alternate '%s' member '%s' can't be distinguished "
981 % (name
, key
, types_seen
[qt
]))
985 def check_enum(expr
, info
):
987 members
= expr
['data']
988 prefix
= expr
.get('prefix')
990 if not isinstance(members
, list):
991 raise QAPISemError(info
,
992 "enum '%s' requires an array for 'data'" % name
)
993 if prefix
is not None and not isinstance(prefix
, str):
994 raise QAPISemError(info
,
995 "enum '%s' requires a string for 'prefix'" % name
)
997 permit_upper
= name
in name_case_whitelist
999 for member
in members
:
1000 check_known_keys(info
, "member of enum '%s'" % name
, member
,
1002 check_if(member
, info
)
1003 normalize_if(member
)
1004 check_name(info
, "member of enum '%s'" % name
, member
['name'],
1005 enum_member
=True, permit_upper
=permit_upper
)
1008 def check_struct(expr
, info
):
1009 name
= expr
['struct']
1010 members
= expr
['data']
1011 features
= expr
.get('features')
1013 check_type(info
, "'data' for struct '%s'" % name
, members
,
1015 check_type(info
, "'base' for struct '%s'" % name
, expr
.get('base'),
1016 allow_metas
=['struct'])
1019 if not isinstance(features
, list):
1021 info
, "struct '%s' requires an array for 'features'" % name
)
1023 assert isinstance(f
, dict)
1024 check_known_keys(info
, "feature of struct %s" % name
, f
,
1029 check_name(info
, "feature of struct %s" % name
, f
['name'])
1032 def check_known_keys(info
, source
, value
, required
, optional
):
1035 return ', '.join("'" + e
+ "'" for e
in sorted(elems
))
1037 missing
= set(required
) - set(value
)
1041 "key%s %s %s missing from %s"
1042 % ('s' if len(missing
) > 1 else '', pprint(missing
),
1043 'are' if len(missing
) > 1 else 'is', source
))
1044 allowed
= set(required
+ optional
)
1045 unknown
= set(value
) - allowed
1049 "unknown key%s %s in %s\nValid keys are %s."
1050 % ('s' if len(unknown
) > 1 else '', pprint(unknown
),
1051 source
, pprint(allowed
)))
1054 def check_keys(expr
, info
, meta
, required
, optional
=[]):
1056 if not isinstance(name
, str):
1057 raise QAPISemError(info
, "'%s' key must have a string value" % meta
)
1058 required
= required
+ [meta
]
1059 source
= "%s '%s'" % (meta
, name
)
1060 check_known_keys(info
, source
, expr
, required
, optional
)
1061 for (key
, value
) in expr
.items():
1062 if key
in ['gen', 'success-response'] and value
is not False:
1063 raise QAPISemError(info
,
1064 "'%s' of %s '%s' should only use false value"
1065 % (key
, meta
, name
))
1066 if (key
in ['boxed', 'allow-oob', 'allow-preconfig']
1067 and value
is not True):
1068 raise QAPISemError(info
,
1069 "'%s' of %s '%s' should only use true value"
1070 % (key
, meta
, name
))
1072 check_if(expr
, info
)
1075 def normalize_enum(expr
):
1076 if isinstance(expr
['data'], list):
1077 expr
['data'] = [m
if isinstance(m
, dict) else {'name': m
}
1078 for m
in expr
['data']]
1081 def normalize_members(members
):
1082 if isinstance(members
, OrderedDict
):
1083 for key
, arg
in members
.items():
1084 if isinstance(arg
, dict):
1086 members
[key
] = {'type': arg
}
1089 def normalize_features(features
):
1090 if isinstance(features
, list):
1091 features
[:] = [f
if isinstance(f
, dict) else {'name': f
}
1095 def normalize_if(expr
):
1096 ifcond
= expr
.get('if')
1097 if isinstance(ifcond
, str):
1098 expr
['if'] = [ifcond
]
1101 def check_exprs(exprs
):
1104 # Populate name table with names of built-in types
1105 for builtin
in builtin_types
.keys():
1106 all_names
[builtin
] = 'built-in'
1108 # Learn the types and check for valid expression keys
1109 for expr_elem
in exprs
:
1110 expr
= expr_elem
['expr']
1111 info
= expr_elem
['info']
1112 doc
= expr_elem
.get('doc')
1114 if 'include' in expr
:
1117 if not doc
and doc_required
:
1118 raise QAPISemError(info
,
1119 "definition missing documentation comment")
1123 check_keys(expr
, info
, 'enum', ['data'], ['if', 'prefix'])
1124 normalize_enum(expr
)
1125 enum_types
[expr
[meta
]] = expr
1126 elif 'union' in expr
:
1128 check_keys(expr
, info
, 'union', ['data'],
1129 ['base', 'discriminator', 'if'])
1130 normalize_members(expr
.get('base'))
1131 normalize_members(expr
['data'])
1132 union_types
[expr
[meta
]] = expr
1133 elif 'alternate' in expr
:
1135 check_keys(expr
, info
, 'alternate', ['data'], ['if'])
1136 normalize_members(expr
['data'])
1137 elif 'struct' in expr
:
1139 check_keys(expr
, info
, 'struct', ['data'],
1140 ['base', 'if', 'features'])
1141 normalize_members(expr
['data'])
1142 normalize_features(expr
.get('features'))
1143 struct_types
[expr
[meta
]] = expr
1144 elif 'command' in expr
:
1146 check_keys(expr
, info
, 'command', [],
1147 ['data', 'returns', 'gen', 'success-response',
1148 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
1149 normalize_members(expr
.get('data'))
1150 elif 'event' in expr
:
1152 check_keys(expr
, info
, 'event', [], ['data', 'boxed', 'if'])
1153 normalize_members(expr
.get('data'))
1155 raise QAPISemError(info
, "expression is missing metatype")
1158 add_name(name
, info
, meta
)
1159 info
.set_defn(meta
, name
)
1160 if doc
and doc
.symbol
!= name
:
1163 "definition of '%s' follows documentation for '%s'"
1164 % (name
, doc
.symbol
))
1166 # Validate that exprs make sense
1167 for expr_elem
in exprs
:
1168 expr
= expr_elem
['expr']
1169 info
= expr_elem
['info']
1170 doc
= expr_elem
.get('doc')
1172 if 'include' in expr
:
1175 check_enum(expr
, info
)
1176 elif 'union' in expr
:
1177 check_union(expr
, info
)
1178 elif 'alternate' in expr
:
1179 check_alternate(expr
, info
)
1180 elif 'struct' in expr
:
1181 check_struct(expr
, info
)
1182 elif 'command' in expr
:
1183 check_command(expr
, info
)
1184 elif 'event' in expr
:
1185 check_event(expr
, info
)
1187 assert False, 'unexpected meta type'
1190 doc
.check_expr(expr
)
1196 # Schema compiler frontend
1199 class QAPISchemaEntity(object):
1200 def __init__(self
, name
, info
, doc
, ifcond
=None):
1201 assert name
is None or isinstance(name
, str)
1204 # For explicitly defined entities, info points to the (explicit)
1205 # definition. For builtins (and their arrays), info is None.
1206 # For implicitly defined entities, info points to a place that
1207 # triggered the implicit definition (there may be more than one
1211 self
._ifcond
= ifcond
or []
1212 self
._checked
= False
1215 return c_name(self
.name
)
1217 def check(self
, schema
):
1218 assert not self
._checked
1220 self
._module
= os
.path
.relpath(self
.info
.fname
,
1221 os
.path
.dirname(schema
.fname
))
1222 self
._checked
= True
1226 assert self
._checked
1231 assert self
._checked
1234 def is_implicit(self
):
1235 return not self
.info
1237 def visit(self
, visitor
):
1238 assert self
._checked
1241 class QAPISchemaVisitor(object):
1242 def visit_begin(self
, schema
):
1245 def visit_end(self
):
1248 def visit_module(self
, fname
):
1251 def visit_needed(self
, entity
):
1252 # Default to visiting everything
1255 def visit_include(self
, fname
, info
):
1258 def visit_builtin_type(self
, name
, info
, json_type
):
1261 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
1264 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
1267 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
,
1271 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
1275 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
1278 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
1279 success_response
, boxed
, allow_oob
, allow_preconfig
):
1282 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
1286 class QAPISchemaInclude(QAPISchemaEntity
):
1288 def __init__(self
, fname
, info
):
1289 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1292 def visit(self
, visitor
):
1293 QAPISchemaEntity
.visit(self
, visitor
)
1294 visitor
.visit_include(self
.fname
, self
.info
)
1297 class QAPISchemaType(QAPISchemaEntity
):
1298 # Return the C type for common use.
1299 # For the types we commonly box, this is a pointer type.
1303 # Return the C type to be used in a parameter list.
1304 def c_param_type(self
):
1305 return self
.c_type()
1307 # Return the C type to be used where we suppress boxing.
1308 def c_unboxed_type(self
):
1309 return self
.c_type()
1311 def json_type(self
):
1314 def alternate_qtype(self
):
1316 'null': 'QTYPE_QNULL',
1317 'string': 'QTYPE_QSTRING',
1318 'number': 'QTYPE_QNUM',
1319 'int': 'QTYPE_QNUM',
1320 'boolean': 'QTYPE_QBOOL',
1321 'object': 'QTYPE_QDICT'
1323 return json2qtype
.get(self
.json_type())
1326 if self
.is_implicit():
1331 class QAPISchemaBuiltinType(QAPISchemaType
):
1332 def __init__(self
, name
, json_type
, c_type
):
1333 QAPISchemaType
.__init
__(self
, name
, None, None)
1334 assert not c_type
or isinstance(c_type
, str)
1335 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1337 self
._json
_type
_name
= json_type
1338 self
._c
_type
_name
= c_type
1344 return self
._c
_type
_name
1346 def c_param_type(self
):
1347 if self
.name
== 'str':
1348 return 'const ' + self
._c
_type
_name
1349 return self
._c
_type
_name
1351 def json_type(self
):
1352 return self
._json
_type
_name
1355 return self
.json_type()
1357 def visit(self
, visitor
):
1358 QAPISchemaType
.visit(self
, visitor
)
1359 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1362 class QAPISchemaEnumType(QAPISchemaType
):
1363 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
1364 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1366 assert isinstance(m
, QAPISchemaEnumMember
)
1367 m
.set_defined_in(name
)
1368 assert prefix
is None or isinstance(prefix
, str)
1369 self
.members
= members
1370 self
.prefix
= prefix
1372 def check(self
, schema
):
1373 QAPISchemaType
.check(self
, schema
)
1375 for m
in self
.members
:
1376 m
.check_clash(self
.info
, seen
)
1378 self
.doc
.connect_member(m
)
1380 def is_implicit(self
):
1381 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1382 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1385 return c_name(self
.name
)
1387 def member_names(self
):
1388 return [m
.name
for m
in self
.members
]
1390 def json_type(self
):
1393 def visit(self
, visitor
):
1394 QAPISchemaType
.visit(self
, visitor
)
1395 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
1396 self
.members
, self
.prefix
)
1399 class QAPISchemaArrayType(QAPISchemaType
):
1400 def __init__(self
, name
, info
, element_type
):
1401 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
1402 assert isinstance(element_type
, str)
1403 self
._element
_type
_name
= element_type
1404 self
.element_type
= None
1406 def check(self
, schema
):
1407 QAPISchemaType
.check(self
, schema
)
1408 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1409 assert self
.element_type
1410 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
1414 assert self
._checked
1415 return self
.element_type
.ifcond
1419 assert self
._checked
1420 return self
.element_type
.module
1422 def is_implicit(self
):
1426 return c_name(self
.name
) + pointer_suffix
1428 def json_type(self
):
1432 elt_doc_type
= self
.element_type
.doc_type()
1433 if not elt_doc_type
:
1435 return 'array of ' + elt_doc_type
1437 def visit(self
, visitor
):
1438 QAPISchemaType
.visit(self
, visitor
)
1439 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
1443 class QAPISchemaObjectType(QAPISchemaType
):
1444 def __init__(self
, name
, info
, doc
, ifcond
,
1445 base
, local_members
, variants
, features
):
1446 # struct has local_members, optional base, and no variants
1447 # flat union has base, variants, and no local_members
1448 # simple union has local_members, variants, and no base
1449 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1450 assert base
is None or isinstance(base
, str)
1451 for m
in local_members
:
1452 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1453 m
.set_defined_in(name
)
1454 if variants
is not None:
1455 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1456 variants
.set_defined_in(name
)
1458 assert isinstance(f
, QAPISchemaFeature
)
1459 f
.set_defined_in(name
)
1460 self
._base
_name
= base
1462 self
.local_members
= local_members
1463 self
.variants
= variants
1465 self
.features
= features
1467 def check(self
, schema
):
1468 # This calls another type T's .check() exactly when the C
1469 # struct emitted by gen_object() contains that T's C struct
1470 # (pointers don't count).
1471 if self
.members
is not None:
1472 # A previous .check() completed: nothing to do
1475 # Recursed: C struct contains itself
1476 raise QAPISemError(self
.info
,
1477 "object %s contains itself" % self
.name
)
1479 QAPISchemaType
.check(self
, schema
)
1480 assert self
._checked
and self
.members
is None
1482 seen
= OrderedDict()
1484 self
.base
= schema
.lookup_type(self
._base
_name
)
1485 assert isinstance(self
.base
, QAPISchemaObjectType
)
1486 self
.base
.check(schema
)
1487 self
.base
.check_clash(self
.info
, seen
)
1488 for m
in self
.local_members
:
1490 m
.check_clash(self
.info
, seen
)
1492 self
.doc
.connect_member(m
)
1493 members
= seen
.values()
1496 self
.variants
.check(schema
, seen
)
1497 assert self
.variants
.tag_member
in members
1498 self
.variants
.check_clash(self
.info
, seen
)
1500 # Features are in a name space separate from members
1502 for f
in self
.features
:
1503 f
.check_clash(self
.info
, seen
)
1508 self
.members
= members
# mark completed
1510 # Check that the members of this type do not cause duplicate JSON members,
1511 # and update seen to track the members seen so far. Report any errors
1512 # on behalf of info, which is not necessarily self.info
1513 def check_clash(self
, info
, seen
):
1514 assert self
._checked
1515 assert not self
.variants
# not implemented
1516 for m
in self
.members
:
1517 m
.check_clash(info
, seen
)
1521 assert self
._checked
1522 if isinstance(self
._ifcond
, QAPISchemaType
):
1523 # Simple union wrapper type inherits from wrapped type;
1524 # see _make_implicit_object_type()
1525 return self
._ifcond
.ifcond
1528 def is_implicit(self
):
1529 # See QAPISchema._make_implicit_object_type(), as well as
1530 # _def_predefineds()
1531 return self
.name
.startswith('q_')
1534 assert self
.members
is not None
1535 return not self
.members
and not self
.variants
1538 assert self
.name
!= 'q_empty'
1539 return QAPISchemaType
.c_name(self
)
1542 assert not self
.is_implicit()
1543 return c_name(self
.name
) + pointer_suffix
1545 def c_unboxed_type(self
):
1546 return c_name(self
.name
)
1548 def json_type(self
):
1551 def visit(self
, visitor
):
1552 QAPISchemaType
.visit(self
, visitor
)
1553 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
1554 self
.base
, self
.local_members
, self
.variants
,
1556 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
1557 self
.members
, self
.variants
,
1561 class QAPISchemaMember(object):
1562 """ Represents object members, enum members and features """
1565 def __init__(self
, name
, ifcond
=None):
1566 assert isinstance(name
, str)
1568 self
.ifcond
= ifcond
or []
1569 self
.defined_in
= None
1571 def set_defined_in(self
, name
):
1572 assert not self
.defined_in
1573 self
.defined_in
= name
1575 def check_clash(self
, info
, seen
):
1576 cname
= c_name(self
.name
)
1578 raise QAPISemError(info
, "%s collides with %s" %
1579 (self
.describe(), seen
[cname
].describe()))
1582 def _pretty_defined_in(self
):
1583 defined_in
= self
.defined_in
1584 if defined_in
.startswith('q_obj_'):
1585 # See QAPISchema._make_implicit_object_type() - reverse the
1586 # mapping there to create a nice human-readable description
1587 defined_in
= defined_in
[6:]
1588 if defined_in
.endswith('-arg'):
1589 return '(parameter of %s)' % defined_in
[:-4]
1590 elif defined_in
.endswith('-base'):
1591 return '(base of %s)' % defined_in
[:-5]
1593 assert defined_in
.endswith('-wrapper')
1594 # Unreachable and not implemented
1596 if defined_in
.endswith('Kind'):
1597 # See QAPISchema._make_implicit_enum_type()
1598 return '(branch of %s)' % defined_in
[:-4]
1599 return '(%s of %s)' % (self
.role
, defined_in
)
1602 return "'%s' %s" % (self
.name
, self
._pretty
_defined
_in
())
1605 class QAPISchemaEnumMember(QAPISchemaMember
):
1609 class QAPISchemaFeature(QAPISchemaMember
):
1613 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1614 def __init__(self
, name
, typ
, optional
, ifcond
=None):
1615 QAPISchemaMember
.__init
__(self
, name
, ifcond
)
1616 assert isinstance(typ
, str)
1617 assert isinstance(optional
, bool)
1618 self
._type
_name
= typ
1620 self
.optional
= optional
1622 def check(self
, schema
):
1623 assert self
.defined_in
1624 self
.type = schema
.lookup_type(self
._type
_name
)
1628 class QAPISchemaObjectTypeVariants(object):
1629 def __init__(self
, tag_name
, tag_member
, variants
):
1630 # Flat unions pass tag_name but not tag_member.
1631 # Simple unions and alternates pass tag_member but not tag_name.
1632 # After check(), tag_member is always set, and tag_name remains
1633 # a reliable witness of being used by a flat union.
1634 assert bool(tag_member
) != bool(tag_name
)
1635 assert (isinstance(tag_name
, str) or
1636 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1638 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1639 self
._tag
_name
= tag_name
1640 self
.tag_member
= tag_member
1641 self
.variants
= variants
1643 def set_defined_in(self
, name
):
1644 for v
in self
.variants
:
1645 v
.set_defined_in(name
)
1647 def check(self
, schema
, seen
):
1648 if not self
.tag_member
: # flat union
1649 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1650 assert self
._tag
_name
== self
.tag_member
.name
1651 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1652 assert not self
.tag_member
.optional
1653 assert self
.tag_member
.ifcond
== []
1654 if self
._tag
_name
: # flat union
1655 # branches that are not explicitly covered get an empty type
1656 cases
= set([v
.name
for v
in self
.variants
])
1657 for m
in self
.tag_member
.type.members
:
1658 if m
.name
not in cases
:
1659 v
= QAPISchemaObjectTypeVariant(m
.name
, 'q_empty',
1661 v
.set_defined_in(self
.tag_member
.defined_in
)
1662 self
.variants
.append(v
)
1663 assert self
.variants
1664 for v
in self
.variants
:
1666 # Union names must match enum values; alternate names are
1667 # checked separately. Use 'seen' to tell the two apart.
1669 assert v
.name
in self
.tag_member
.type.member_names()
1670 assert (isinstance(v
.type, QAPISchemaObjectType
)
1671 and not v
.type.variants
)
1672 v
.type.check(schema
)
1674 def check_clash(self
, info
, seen
):
1675 for v
in self
.variants
:
1676 # Reset seen map for each variant, since qapi names from one
1677 # branch do not affect another branch
1678 v
.type.check_clash(info
, dict(seen
))
1681 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1684 def __init__(self
, name
, typ
, ifcond
=None):
1685 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False, ifcond
)
1688 class QAPISchemaAlternateType(QAPISchemaType
):
1689 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
1690 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1691 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1692 assert variants
.tag_member
1693 variants
.set_defined_in(name
)
1694 variants
.tag_member
.set_defined_in(self
.name
)
1695 self
.variants
= variants
1697 def check(self
, schema
):
1698 QAPISchemaType
.check(self
, schema
)
1699 self
.variants
.tag_member
.check(schema
)
1700 # Not calling self.variants.check_clash(), because there's nothing
1702 self
.variants
.check(schema
, {})
1703 # Alternate branch names have no relation to the tag enum values;
1704 # so we have to check for potential name collisions ourselves.
1706 for v
in self
.variants
.variants
:
1707 v
.check_clash(self
.info
, seen
)
1708 # TODO check conflicting qtypes
1710 self
.doc
.connect_member(v
)
1715 return c_name(self
.name
) + pointer_suffix
1717 def json_type(self
):
1720 def visit(self
, visitor
):
1721 QAPISchemaType
.visit(self
, visitor
)
1722 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
1726 class QAPISchemaCommand(QAPISchemaEntity
):
1727 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
1728 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
1729 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1730 assert not arg_type
or isinstance(arg_type
, str)
1731 assert not ret_type
or isinstance(ret_type
, str)
1732 self
._arg
_type
_name
= arg_type
1733 self
.arg_type
= None
1734 self
._ret
_type
_name
= ret_type
1735 self
.ret_type
= None
1737 self
.success_response
= success_response
1739 self
.allow_oob
= allow_oob
1740 self
.allow_preconfig
= allow_preconfig
1742 def check(self
, schema
):
1743 QAPISchemaEntity
.check(self
, schema
)
1744 if self
._arg
_type
_name
:
1745 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1746 assert isinstance(self
.arg_type
, QAPISchemaObjectType
)
1747 assert not self
.arg_type
.variants
or self
.boxed
1749 raise QAPISemError(self
.info
, "use of 'boxed' requires 'data'")
1750 if self
._ret
_type
_name
:
1751 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1752 assert isinstance(self
.ret_type
, QAPISchemaType
)
1754 def visit(self
, visitor
):
1755 QAPISchemaEntity
.visit(self
, visitor
)
1756 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
1757 self
.arg_type
, self
.ret_type
,
1758 self
.gen
, self
.success_response
,
1759 self
.boxed
, self
.allow_oob
,
1760 self
.allow_preconfig
)
1763 class QAPISchemaEvent(QAPISchemaEntity
):
1764 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
1765 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1766 assert not arg_type
or isinstance(arg_type
, str)
1767 self
._arg
_type
_name
= arg_type
1768 self
.arg_type
= None
1771 def check(self
, schema
):
1772 QAPISchemaEntity
.check(self
, schema
)
1773 if self
._arg
_type
_name
:
1774 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1775 assert isinstance(self
.arg_type
, QAPISchemaObjectType
)
1776 assert not self
.arg_type
.variants
or self
.boxed
1778 raise QAPISemError(self
.info
, "use of 'boxed' requires 'data'")
1780 def visit(self
, visitor
):
1781 QAPISchemaEntity
.visit(self
, visitor
)
1782 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
1783 self
.arg_type
, self
.boxed
)
1786 class QAPISchema(object):
1787 def __init__(self
, fname
):
1789 if sys
.version_info
[0] >= 3:
1790 f
= open(fname
, 'r', encoding
='utf-8')
1792 f
= open(fname
, 'r')
1793 parser
= QAPISchemaParser(f
)
1794 exprs
= check_exprs(parser
.exprs
)
1795 self
.docs
= parser
.docs
1796 self
._entity
_list
= []
1797 self
._entity
_dict
= {}
1798 self
._predefining
= True
1799 self
._def
_predefineds
()
1800 self
._predefining
= False
1801 self
._def
_exprs
(exprs
)
1804 def _def_entity(self
, ent
):
1805 # Only the predefined types are allowed to not have info
1806 assert ent
.info
or self
._predefining
1807 assert ent
.name
is None or ent
.name
not in self
._entity
_dict
1808 self
._entity
_list
.append(ent
)
1809 if ent
.name
is not None:
1810 self
._entity
_dict
[ent
.name
] = ent
1812 def lookup_entity(self
, name
, typ
=None):
1813 ent
= self
._entity
_dict
.get(name
)
1814 if typ
and not isinstance(ent
, typ
):
1818 def lookup_type(self
, name
):
1819 return self
.lookup_entity(name
, QAPISchemaType
)
1821 def _def_include(self
, expr
, info
, doc
):
1822 include
= expr
['include']
1825 while main_info
.parent
:
1826 main_info
= main_info
.parent
1827 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
.fname
))
1828 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
1830 def _def_builtin_type(self
, name
, json_type
, c_type
):
1831 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1832 # Instantiating only the arrays that are actually used would
1833 # be nice, but we can't as long as their generated code
1834 # (qapi-builtin-types.[ch]) may be shared by some other
1836 self
._make
_array
_type
(name
, None)
1838 def _def_predefineds(self
):
1839 for t
in [('str', 'string', 'char' + pointer_suffix
),
1840 ('number', 'number', 'double'),
1841 ('int', 'int', 'int64_t'),
1842 ('int8', 'int', 'int8_t'),
1843 ('int16', 'int', 'int16_t'),
1844 ('int32', 'int', 'int32_t'),
1845 ('int64', 'int', 'int64_t'),
1846 ('uint8', 'int', 'uint8_t'),
1847 ('uint16', 'int', 'uint16_t'),
1848 ('uint32', 'int', 'uint32_t'),
1849 ('uint64', 'int', 'uint64_t'),
1850 ('size', 'int', 'uint64_t'),
1851 ('bool', 'boolean', 'bool'),
1852 ('any', 'value', 'QObject' + pointer_suffix
),
1853 ('null', 'null', 'QNull' + pointer_suffix
)]:
1854 self
._def
_builtin
_type
(*t
)
1855 self
.the_empty_object_type
= QAPISchemaObjectType(
1856 'q_empty', None, None, None, None, [], None, [])
1857 self
._def
_entity
(self
.the_empty_object_type
)
1859 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1861 qtype_values
= self
._make
_enum
_members
([{'name': n
} for n
in qtypes
])
1863 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
1864 qtype_values
, 'QTYPE'))
1866 def _make_features(self
, features
):
1867 return [QAPISchemaFeature(f
['name'], f
.get('if')) for f
in features
]
1869 def _make_enum_members(self
, values
):
1870 return [QAPISchemaEnumMember(v
['name'], v
.get('if'))
1873 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
1874 # See also QAPISchemaObjectTypeMember._pretty_defined_in()
1875 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1876 self
._def
_entity
(QAPISchemaEnumType(
1877 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
), None))
1880 def _make_array_type(self
, element_type
, info
):
1881 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1882 if not self
.lookup_type(name
):
1883 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1886 def _make_implicit_object_type(self
, name
, info
, doc
, ifcond
,
1890 # See also QAPISchemaObjectTypeMember._pretty_defined_in()
1891 name
= 'q_obj_%s-%s' % (name
, role
)
1892 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1894 # The implicit object type has multiple users. This can
1895 # happen only for simple unions' implicit wrapper types.
1896 # Its ifcond should be the disjunction of its user's
1897 # ifconds. Not implemented. Instead, we always pass the
1898 # wrapped type's ifcond, which is trivially the same for all
1899 # users. It's also necessary for the wrapper to compile.
1900 # But it's not tight: the disjunction need not imply it. We
1901 # may end up compiling useless wrapper types.
1902 # TODO kill simple unions or implement the disjunction
1903 assert ifcond
== typ
._ifcond
# pylint: disable=protected-access
1905 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
,
1906 None, members
, None, []))
1909 def _def_enum_type(self
, expr
, info
, doc
):
1912 prefix
= expr
.get('prefix')
1913 ifcond
= expr
.get('if')
1914 self
._def
_entity
(QAPISchemaEnumType(
1915 name
, info
, doc
, ifcond
,
1916 self
._make
_enum
_members
(data
), prefix
))
1918 def _make_member(self
, name
, typ
, ifcond
, info
):
1920 if name
.startswith('*'):
1923 if isinstance(typ
, list):
1924 assert len(typ
) == 1
1925 typ
= self
._make
_array
_type
(typ
[0], info
)
1926 return QAPISchemaObjectTypeMember(name
, typ
, optional
, ifcond
)
1928 def _make_members(self
, data
, info
):
1929 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
1930 for (key
, value
) in data
.items()]
1932 def _def_struct_type(self
, expr
, info
, doc
):
1933 name
= expr
['struct']
1934 base
= expr
.get('base')
1936 ifcond
= expr
.get('if')
1937 features
= expr
.get('features', [])
1938 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
,
1939 self
._make
_members
(data
, info
),
1941 self
._make
_features
(features
)))
1943 def _make_variant(self
, case
, typ
, ifcond
):
1944 return QAPISchemaObjectTypeVariant(case
, typ
, ifcond
)
1946 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1947 if isinstance(typ
, list):
1948 assert len(typ
) == 1
1949 typ
= self
._make
_array
_type
(typ
[0], info
)
1950 typ
= self
._make
_implicit
_object
_type
(
1951 typ
, info
, None, self
.lookup_type(typ
),
1952 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
1953 return QAPISchemaObjectTypeVariant(case
, typ
, ifcond
)
1955 def _def_union_type(self
, expr
, info
, doc
):
1956 name
= expr
['union']
1958 base
= expr
.get('base')
1959 ifcond
= expr
.get('if')
1960 tag_name
= expr
.get('discriminator')
1962 if isinstance(base
, dict):
1963 base
= self
._make
_implicit
_object
_type
(
1964 name
, info
, doc
, ifcond
,
1965 'base', self
._make
_members
(base
, info
))
1967 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'))
1968 for (key
, value
) in data
.items()]
1971 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1972 value
.get('if'), info
)
1973 for (key
, value
) in data
.items()]
1974 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1975 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1976 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1977 members
= [tag_member
]
1979 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
1980 QAPISchemaObjectTypeVariants(tag_name
,
1984 def _def_alternate_type(self
, expr
, info
, doc
):
1985 name
= expr
['alternate']
1987 ifcond
= expr
.get('if')
1988 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'))
1989 for (key
, value
) in data
.items()]
1990 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1992 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
1993 QAPISchemaObjectTypeVariants(None,
1997 def _def_command(self
, expr
, info
, doc
):
1998 name
= expr
['command']
1999 data
= expr
.get('data')
2000 rets
= expr
.get('returns')
2001 gen
= expr
.get('gen', True)
2002 success_response
= expr
.get('success-response', True)
2003 boxed
= expr
.get('boxed', False)
2004 allow_oob
= expr
.get('allow-oob', False)
2005 allow_preconfig
= expr
.get('allow-preconfig', 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 if isinstance(rets
, list):
2011 assert len(rets
) == 1
2012 rets
= self
._make
_array
_type
(rets
[0], info
)
2013 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
2014 gen
, success_response
,
2015 boxed
, allow_oob
, allow_preconfig
))
2017 def _def_event(self
, expr
, info
, doc
):
2018 name
= expr
['event']
2019 data
= expr
.get('data')
2020 boxed
= expr
.get('boxed', False)
2021 ifcond
= expr
.get('if')
2022 if isinstance(data
, OrderedDict
):
2023 data
= self
._make
_implicit
_object
_type
(
2024 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
2025 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
2027 def _def_exprs(self
, exprs
):
2028 for expr_elem
in exprs
:
2029 expr
= expr_elem
['expr']
2030 info
= expr_elem
['info']
2031 doc
= expr_elem
.get('doc')
2033 self
._def
_enum
_type
(expr
, info
, doc
)
2034 elif 'struct' in expr
:
2035 self
._def
_struct
_type
(expr
, info
, doc
)
2036 elif 'union' in expr
:
2037 self
._def
_union
_type
(expr
, info
, doc
)
2038 elif 'alternate' in expr
:
2039 self
._def
_alternate
_type
(expr
, info
, doc
)
2040 elif 'command' in expr
:
2041 self
._def
_command
(expr
, info
, doc
)
2042 elif 'event' in expr
:
2043 self
._def
_event
(expr
, info
, doc
)
2044 elif 'include' in expr
:
2045 self
._def
_include
(expr
, info
, doc
)
2050 for ent
in self
._entity
_list
:
2053 def visit(self
, visitor
):
2054 visitor
.visit_begin(self
)
2056 visitor
.visit_module(module
)
2057 for entity
in self
._entity
_list
:
2058 if visitor
.visit_needed(entity
):
2059 if entity
.module
!= module
:
2060 module
= entity
.module
2061 visitor
.visit_module(module
)
2062 entity
.visit(visitor
)
2067 # Code generation helpers
2070 def camel_case(name
):
2074 if ch
in ['_', '-']:
2077 new_name
+= ch
.upper()
2080 new_name
+= ch
.lower()
2084 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2085 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2086 # ENUM24_Name -> ENUM24_NAME
2087 def camel_to_upper(value
):
2088 c_fun_str
= c_name(value
, False)
2093 length
= len(c_fun_str
)
2094 for i
in range(length
):
2096 # When c is upper and no '_' appears before, do more checks
2097 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
2098 if i
< length
- 1 and c_fun_str
[i
+ 1].islower():
2100 elif c_fun_str
[i
- 1].isdigit():
2103 return new_name
.lstrip('_').upper()
2106 def c_enum_const(type_name
, const_name
, prefix
=None):
2107 if prefix
is not None:
2109 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
2112 if hasattr(str, 'maketrans'):
2113 c_name_trans
= str.maketrans('.-', '__')
2115 c_name_trans
= string
.maketrans('.-', '__')
2118 # Map @name to a valid C identifier.
2119 # If @protect, avoid returning certain ticklish identifiers (like
2120 # C keywords) by prepending 'q_'.
2122 # Used for converting 'name' from a 'name':'type' qapi definition
2123 # into a generated struct member, as well as converting type names
2124 # into substrings of a generated C function name.
2125 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2126 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2127 def c_name(name
, protect
=True):
2128 # ANSI X3J11/88-090, 3.1.1
2129 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
2130 'default', 'do', 'double', 'else', 'enum', 'extern',
2131 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2132 'return', 'short', 'signed', 'sizeof', 'static',
2133 'struct', 'switch', 'typedef', 'union', 'unsigned',
2134 'void', 'volatile', 'while'])
2135 # ISO/IEC 9899:1999, 6.4.1
2136 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2137 # ISO/IEC 9899:2011, 6.4.1
2138 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2139 '_Noreturn', '_Static_assert', '_Thread_local'])
2140 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2142 gcc_words
= set(['asm', 'typeof'])
2143 # C++ ISO/IEC 14882:2003 2.11
2144 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
2145 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2146 'namespace', 'new', 'operator', 'private', 'protected',
2147 'public', 'reinterpret_cast', 'static_cast', 'template',
2148 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2149 'using', 'virtual', 'wchar_t',
2150 # alternative representations
2151 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2152 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2153 # namespace pollution:
2154 polluted_words
= set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2155 name
= name
.translate(c_name_trans
)
2156 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
2157 | cpp_words | polluted_words
):
2162 eatspace
= '\033EATSPACE.'
2163 pointer_suffix
= ' *' + eatspace
2166 def genindent(count
):
2168 for _
in range(count
):
2176 def push_indent(indent_amount
=4):
2178 indent_level
+= indent_amount
2181 def pop_indent(indent_amount
=4):
2183 indent_level
-= indent_amount
2186 # Generate @code with @kwds interpolated.
2187 # Obey indent_level, and strip eatspace.
2188 def cgen(code
, **kwds
):
2191 indent
= genindent(indent_level
)
2192 # re.subn() lacks flags support before Python 2.7, use re.compile()
2193 raw
= re
.subn(re
.compile(r
'^(?!(#|$))', re
.MULTILINE
),
2196 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
2199 def mcgen(code
, **kwds
):
2202 return cgen(code
, **kwds
)
2205 def c_fname(filename
):
2206 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
)
2209 def guardstart(name
):
2215 name
=c_fname(name
).upper())
2221 #endif /* %(name)s */
2223 name
=c_fname(name
).upper())
2235 def gen_endif(ifcond
):
2237 for ifc
in reversed(ifcond
):
2239 #endif /* %(cond)s */
2244 def _wrap_ifcond(ifcond
, before
, after
):
2246 return after
# suppress empty #if ... #endif
2248 assert after
.startswith(before
)
2250 added
= after
[len(before
):]
2251 if added
[0] == '\n':
2254 out
+= gen_if(ifcond
)
2256 out
+= gen_endif(ifcond
)
2260 def gen_enum_lookup(name
, members
, prefix
=None):
2263 const QEnumLookup %(c_name)s_lookup = {
2264 .array = (const char *const[]) {
2266 c_name
=c_name(name
))
2268 ret
+= gen_if(m
.ifcond
)
2269 index
= c_enum_const(name
, m
.name
, prefix
)
2271 [%(index)s] = "%(name)s",
2273 index
=index
, name
=m
.name
)
2274 ret
+= gen_endif(m
.ifcond
)
2278 .size = %(max_index)s
2281 max_index
=c_enum_const(name
, '_MAX', prefix
))
2285 def gen_enum(name
, members
, prefix
=None):
2286 # append automatically generated _MAX value
2287 enum_members
= members
+ [QAPISchemaEnumMember('_MAX')]
2291 typedef enum %(c_name)s {
2293 c_name
=c_name(name
))
2295 for m
in enum_members
:
2296 ret
+= gen_if(m
.ifcond
)
2300 c_enum
=c_enum_const(name
, m
.name
, prefix
))
2301 ret
+= gen_endif(m
.ifcond
)
2306 c_name
=c_name(name
))
2310 #define %(c_name)s_str(val) \\
2311 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2313 extern const QEnumLookup %(c_name)s_lookup;
2315 c_name
=c_name(name
))
2319 def build_params(arg_type
, boxed
, extra
=None):
2324 ret
+= '%s arg' % arg_type
.c_param_type()
2327 assert not arg_type
.variants
2328 for memb
in arg_type
.members
:
2332 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
2333 ret
+= '%s %s' % (memb
.type.c_param_type(),
2337 return ret
if ret
else 'void'
2341 # Accumulate and write output
2344 class QAPIGen(object):
2346 def __init__(self
, fname
):
2351 def preamble_add(self
, text
):
2352 self
._preamble
+= text
2354 def add(self
, text
):
2357 def get_content(self
):
2358 return self
._top
() + self
._preamble
+ self
._body
+ self
._bottom
()
2366 def write(self
, output_dir
):
2367 pathname
= os
.path
.join(output_dir
, self
.fname
)
2368 dir = os
.path
.dirname(pathname
)
2372 except os
.error
as e
:
2373 if e
.errno
!= errno
.EEXIST
:
2375 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2376 if sys
.version_info
[0] >= 3:
2377 f
= open(fd
, 'r+', encoding
='utf-8')
2379 f
= os
.fdopen(fd
, 'r+')
2380 text
= self
.get_content()
2381 oldtext
= f
.read(len(text
) + 1)
2390 def ifcontext(ifcond
, *args
):
2391 """A 'with' statement context manager to wrap with start_if()/end_if()
2393 *args: any number of QAPIGenCCode
2397 with ifcontext(ifcond, self._genh, self._genc):
2398 modify self._genh and self._genc ...
2400 Is equivalent to calling::
2402 self._genh.start_if(ifcond)
2403 self._genc.start_if(ifcond)
2404 modify self._genh and self._genc ...
2409 arg
.start_if(ifcond
)
2415 class QAPIGenCCode(QAPIGen
):
2417 def __init__(self
, fname
):
2418 QAPIGen
.__init
__(self
, fname
)
2419 self
._start
_if
= None
2421 def start_if(self
, ifcond
):
2422 assert self
._start
_if
is None
2423 self
._start
_if
= (ifcond
, self
._body
, self
._preamble
)
2426 assert self
._start
_if
2428 self
._start
_if
= None
2430 def _wrap_ifcond(self
):
2431 self
._body
= _wrap_ifcond(self
._start
_if
[0],
2432 self
._start
_if
[1], self
._body
)
2433 self
._preamble
= _wrap_ifcond(self
._start
_if
[0],
2434 self
._start
_if
[2], self
._preamble
)
2436 def get_content(self
):
2437 assert self
._start
_if
is None
2438 return QAPIGen
.get_content(self
)
2441 class QAPIGenC(QAPIGenCCode
):
2443 def __init__(self
, fname
, blurb
, pydoc
):
2444 QAPIGenCCode
.__init
__(self
, fname
)
2446 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2451 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2458 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2459 * See the COPYING.LIB file in the top-level directory.
2463 blurb
=self
._blurb
, copyright
=self
._copyright
)
2468 /* Dummy declaration to prevent empty .o file */
2469 char qapi_dummy_%(name)s;
2471 name
=c_fname(self
.fname
))
2474 class QAPIGenH(QAPIGenC
):
2477 return QAPIGenC
._top
(self
) + guardstart(self
.fname
)
2480 return guardend(self
.fname
)
2483 class QAPIGenDoc(QAPIGen
):
2486 return (QAPIGen
._top
(self
)
2487 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2490 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2492 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2493 self
._prefix
= prefix
2495 self
._genc
= QAPIGenC(self
._prefix
+ self
._what
+ '.c',
2497 self
._genh
= QAPIGenH(self
._prefix
+ self
._what
+ '.h',
2500 def write(self
, output_dir
):
2501 self
._genc
.write(output_dir
)
2502 self
._genh
.write(output_dir
)
2505 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2507 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2508 self
._prefix
= prefix
2515 self
._main
_module
= None
2518 def _is_user_module(name
):
2519 return name
and not name
.startswith('./')
2522 def _is_builtin_module(name
):
2525 def _module_dirname(self
, what
, name
):
2526 if self
._is
_user
_module
(name
):
2527 return os
.path
.dirname(name
)
2530 def _module_basename(self
, what
, name
):
2531 ret
= '' if self
._is
_builtin
_module
(name
) else self
._prefix
2532 if self
._is
_user
_module
(name
):
2533 basename
= os
.path
.basename(name
)
2535 if name
!= self
._main
_module
:
2536 ret
+= '-' + os
.path
.splitext(basename
)[0]
2538 name
= name
[2:] if name
else 'builtin'
2539 ret
+= re
.sub(r
'-', '-' + name
+ '-', what
)
2542 def _module_filename(self
, what
, name
):
2543 return os
.path
.join(self
._module
_dirname
(what
, name
),
2544 self
._module
_basename
(what
, name
))
2546 def _add_module(self
, name
, blurb
):
2547 basename
= self
._module
_filename
(self
._what
, name
)
2548 genc
= QAPIGenC(basename
+ '.c', blurb
, self
._pydoc
)
2549 genh
= QAPIGenH(basename
+ '.h', blurb
, self
._pydoc
)
2550 self
._module
[name
] = (genc
, genh
)
2551 self
._set
_module
(name
)
2553 def _add_user_module(self
, name
, blurb
):
2554 assert self
._is
_user
_module
(name
)
2555 if self
._main
_module
is None:
2556 self
._main
_module
= name
2557 self
._add
_module
(name
, blurb
)
2559 def _add_system_module(self
, name
, blurb
):
2560 self
._add
_module
(name
and './' + name
, blurb
)
2562 def _set_module(self
, name
):
2563 self
._genc
, self
._genh
= self
._module
[name
]
2565 def write(self
, output_dir
, opt_builtins
=False):
2566 for name
in self
._module
:
2567 if self
._is
_builtin
_module
(name
) and not opt_builtins
:
2569 (genc
, genh
) = self
._module
[name
]
2570 genc
.write(output_dir
)
2571 genh
.write(output_dir
)
2573 def _begin_user_module(self
, name
):
2576 def visit_module(self
, name
):
2577 if name
in self
._module
:
2578 self
._set
_module
(name
)
2579 elif self
._is
_builtin
_module
(name
):
2580 # The built-in module has not been created. No code may
2585 self
._add
_user
_module
(name
, self
._blurb
)
2586 self
._begin
_user
_module
(name
)
2588 def visit_include(self
, name
, info
):
2589 relname
= os
.path
.relpath(self
._module
_filename
(self
._what
, name
),
2590 os
.path
.dirname(self
._genh
.fname
))
2591 self
._genh
.preamble_add(mcgen('''
2592 #include "%(relname)s.h"