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
24 # Are documentation comments required?
27 # Whitelist of commands allowed to return a non-dictionary
28 returns_whitelist
= []
30 # Whitelist of entities allowed to violate case conventions
31 name_case_whitelist
= []
35 # Parsing the schema into expressions
38 class QAPISourceInfo(object):
39 def __init__(self
, fname
, line
, parent
):
46 def set_defn(self
, meta
, name
):
51 info
= copy
.copy(self
)
56 return '%s:%d' % (self
.fname
, self
.line
)
60 return "%s: In %s '%s':\n" % (self
.fname
,
61 self
.defn_meta
, self
.defn_name
)
64 def include_path(self
):
68 ret
= 'In file included from %s:\n' % parent
.loc() + ret
69 parent
= parent
.parent
73 return self
.include_path() + self
.in_defn() + self
.loc()
76 class QAPIError(Exception):
77 def __init__(self
, info
, col
, msg
):
78 Exception.__init
__(self
)
85 if self
.col
is not None:
86 assert self
.info
.line
is not None
87 loc
+= ':%s' % self
.col
88 return loc
+ ': ' + self
.msg
91 class QAPIParseError(QAPIError
):
92 def __init__(self
, parser
, msg
):
94 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
96 col
= (col
+ 7) % 8 + 1
99 QAPIError
.__init
__(self
, parser
.info
, col
, msg
)
102 class QAPISemError(QAPIError
):
103 def __init__(self
, info
, msg
):
104 QAPIError
.__init
__(self
, info
, None, msg
)
107 class QAPIDoc(object):
109 A documentation comment block, either definition or free-form
111 Definition documentation blocks consist of
113 * a body section: one line naming the definition, followed by an
114 overview (any number of lines)
116 * argument sections: a description of each argument (for commands
117 and events) or member (for structs, unions and alternates)
119 * features sections: a description of each feature flag
121 * additional (non-argument) sections, possibly tagged
123 Free-form documentation blocks consist only of a body section.
126 class Section(object):
127 def __init__(self
, name
=None):
128 # optional section name (argument/member or section name)
130 # the list of lines for this section
133 def append(self
, line
):
134 self
.text
+= line
.rstrip() + '\n'
136 class ArgSection(Section
):
137 def __init__(self
, name
):
138 QAPIDoc
.Section
.__init
__(self
, name
)
141 def connect(self
, member
):
144 def __init__(self
, parser
, info
):
145 # self._parser is used to report errors with QAPIParseError. The
146 # resulting error position depends on the state of the parser.
147 # It happens to be the beginning of the comment. More or less
148 # servicable, but action at a distance.
149 self
._parser
= parser
152 self
.body
= QAPIDoc
.Section()
153 # dict mapping parameter name to ArgSection
154 self
.args
= OrderedDict()
155 self
.features
= OrderedDict()
158 # the current section
159 self
._section
= self
.body
160 self
._append
_line
= self
._append
_body
_line
162 def has_section(self
, name
):
163 """Return True if we have a section with this name."""
164 for i
in self
.sections
:
169 def append(self
, line
):
171 Parse a comment line and add it to the documentation.
173 The way that the line is dealt with depends on which part of
174 the documentation we're parsing right now:
175 * The body section: ._append_line is ._append_body_line
176 * An argument section: ._append_line is ._append_args_line
177 * A features section: ._append_line is ._append_features_line
178 * An additional section: ._append_line is ._append_various_line
182 self
._append
_freeform
(line
)
186 raise QAPIParseError(self
._parser
, "missing space after #")
188 self
._append
_line
(line
)
190 def end_comment(self
):
194 def _is_section_tag(name
):
195 return name
in ('Returns:', 'Since:',
196 # those are often singular or plural
198 'Example:', 'Examples:',
201 def _append_body_line(self
, line
):
203 Process a line of documentation text in the body section.
205 If this a symbol line and it is the section's first line, this
206 is a definition documentation block for that symbol.
208 If it's a definition documentation block, another symbol line
209 begins the argument section for the argument named by it, and
210 a section tag begins an additional section. Start that
211 section and append the line to it.
213 Else, append the line to the current section.
215 name
= line
.split(' ', 1)[0]
216 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
217 # recognized, and get silently treated as ordinary text
218 if not self
.symbol
and not self
.body
.text
and line
.startswith('@'):
219 if not line
.endswith(':'):
220 raise QAPIParseError(self
._parser
, "line should end with ':'")
221 self
.symbol
= line
[1:-1]
222 # FIXME invalid names other than the empty string aren't flagged
224 raise QAPIParseError(self
._parser
, "invalid name")
226 # This is a definition documentation block
227 if name
.startswith('@') and name
.endswith(':'):
228 self
._append
_line
= self
._append
_args
_line
229 self
._append
_args
_line
(line
)
230 elif line
== 'Features:':
231 self
._append
_line
= self
._append
_features
_line
232 elif self
._is
_section
_tag
(name
):
233 self
._append
_line
= self
._append
_various
_line
234 self
._append
_various
_line
(line
)
236 self
._append
_freeform
(line
.strip())
238 # This is a free-form documentation block
239 self
._append
_freeform
(line
.strip())
241 def _append_args_line(self
, line
):
243 Process a line of documentation text in an argument section.
245 A symbol line begins the next argument section, a section tag
246 section or a non-indented line after a blank line begins an
247 additional section. Start that section and append the line to
250 Else, append the line to the current section.
253 name
= line
.split(' ', 1)[0]
255 if name
.startswith('@') and name
.endswith(':'):
256 line
= line
[len(name
)+1:]
257 self
._start
_args
_section
(name
[1:-1])
258 elif self
._is
_section
_tag
(name
):
259 self
._append
_line
= self
._append
_various
_line
260 self
._append
_various
_line
(line
)
262 elif (self
._section
.text
.endswith('\n\n')
263 and line
and not line
[0].isspace()):
264 if line
== 'Features:':
265 self
._append
_line
= self
._append
_features
_line
267 self
._start
_section
()
268 self
._append
_line
= self
._append
_various
_line
269 self
._append
_various
_line
(line
)
272 self
._append
_freeform
(line
.strip())
274 def _append_features_line(self
, line
):
275 name
= line
.split(' ', 1)[0]
277 if name
.startswith('@') and name
.endswith(':'):
278 line
= line
[len(name
)+1:]
279 self
._start
_features
_section
(name
[1:-1])
280 elif self
._is
_section
_tag
(name
):
281 self
._append
_line
= self
._append
_various
_line
282 self
._append
_various
_line
(line
)
284 elif (self
._section
.text
.endswith('\n\n')
285 and line
and not line
[0].isspace()):
286 self
._start
_section
()
287 self
._append
_line
= self
._append
_various
_line
288 self
._append
_various
_line
(line
)
291 self
._append
_freeform
(line
.strip())
293 def _append_various_line(self
, line
):
295 Process a line of documentation text in an additional section.
297 A symbol line is an error.
299 A section tag begins an additional section. Start that
300 section and append the line to it.
302 Else, append the line to the current section.
304 name
= line
.split(' ', 1)[0]
306 if name
.startswith('@') and name
.endswith(':'):
307 raise QAPIParseError(self
._parser
,
308 "'%s' can't follow '%s' section"
309 % (name
, self
.sections
[0].name
))
310 elif self
._is
_section
_tag
(name
):
311 line
= line
[len(name
)+1:]
312 self
._start
_section
(name
[:-1])
314 if (not self
._section
.name
or
315 not self
._section
.name
.startswith('Example')):
318 self
._append
_freeform
(line
)
320 def _start_symbol_section(self
, symbols_dict
, name
):
321 # FIXME invalid names other than the empty string aren't flagged
323 raise QAPIParseError(self
._parser
, "invalid parameter name")
324 if name
in symbols_dict
:
325 raise QAPIParseError(self
._parser
,
326 "'%s' parameter name duplicated" % name
)
327 assert not self
.sections
329 self
._section
= QAPIDoc
.ArgSection(name
)
330 symbols_dict
[name
] = self
._section
332 def _start_args_section(self
, name
):
333 self
._start
_symbol
_section
(self
.args
, name
)
335 def _start_features_section(self
, name
):
336 self
._start
_symbol
_section
(self
.features
, name
)
338 def _start_section(self
, name
=None):
339 if name
in ('Returns', 'Since') and self
.has_section(name
):
340 raise QAPIParseError(self
._parser
,
341 "duplicated '%s' section" % name
)
343 self
._section
= QAPIDoc
.Section(name
)
344 self
.sections
.append(self
._section
)
346 def _end_section(self
):
348 text
= self
._section
.text
= self
._section
.text
.strip()
349 if self
._section
.name
and (not text
or text
.isspace()):
350 raise QAPIParseError(
352 "empty doc section '%s'" % self
._section
.name
)
355 def _append_freeform(self
, line
):
356 match
= re
.match(r
'(@\S+:)', line
)
358 raise QAPIParseError(self
._parser
,
359 "'%s' not allowed in free-form documentation"
361 self
._section
.append(line
)
363 def connect_member(self
, member
):
364 if member
.name
not in self
.args
:
365 # Undocumented TODO outlaw
366 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
367 self
.args
[member
.name
].connect(member
)
369 def check_expr(self
, expr
):
370 if self
.has_section('Returns') and 'command' not in expr
:
371 raise QAPISemError(self
.info
,
372 "'Returns:' is only valid for commands")
375 bogus
= [name
for name
, section
in self
.args
.items()
376 if not section
.member
]
380 "the following documented members are not in "
381 "the declaration: %s" % ", ".join(bogus
))
384 class QAPISchemaParser(object):
386 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
388 previously_included
.append(os
.path
.abspath(fp
.name
))
390 if self
.src
== '' or self
.src
[-1] != '\n':
393 self
.info
= QAPISourceInfo(self
.fname
, 1, incl_info
)
400 while self
.tok
is not None:
403 self
.reject_expr_doc(cur_doc
)
404 cur_doc
= self
.get_doc(info
)
405 self
.docs
.append(cur_doc
)
408 expr
= self
.get_expr(False)
409 if 'include' in expr
:
410 self
.reject_expr_doc(cur_doc
)
412 raise QAPISemError(info
, "invalid 'include' directive")
413 include
= expr
['include']
414 if not isinstance(include
, str):
415 raise QAPISemError(info
,
416 "value of 'include' must be a string")
417 incl_fname
= os
.path
.join(os
.path
.dirname(self
.fname
),
419 self
.exprs
.append({'expr': {'include': incl_fname
},
421 exprs_include
= self
._include
(include
, info
, incl_fname
,
424 self
.exprs
.extend(exprs_include
.exprs
)
425 self
.docs
.extend(exprs_include
.docs
)
426 elif "pragma" in expr
:
427 self
.reject_expr_doc(cur_doc
)
429 raise QAPISemError(info
, "invalid 'pragma' directive")
430 pragma
= expr
['pragma']
431 if not isinstance(pragma
, dict):
433 info
, "value of 'pragma' must be an object")
434 for name
, value
in pragma
.items():
435 self
._pragma
(name
, value
, info
)
437 expr_elem
= {'expr': expr
,
440 if not cur_doc
.symbol
:
442 cur_doc
.info
, "definition documentation required")
443 expr_elem
['doc'] = cur_doc
444 self
.exprs
.append(expr_elem
)
446 self
.reject_expr_doc(cur_doc
)
449 def reject_expr_doc(doc
):
450 if doc
and doc
.symbol
:
453 "documentation for '%s' is not followed by the definition"
456 def _include(self
, include
, info
, incl_fname
, previously_included
):
457 incl_abs_fname
= os
.path
.abspath(incl_fname
)
458 # catch inclusion cycle
461 if incl_abs_fname
== os
.path
.abspath(inf
.fname
):
462 raise QAPISemError(info
, "inclusion loop for %s" % include
)
465 # skip multiple include of the same file
466 if incl_abs_fname
in previously_included
:
470 if sys
.version_info
[0] >= 3:
471 fobj
= open(incl_fname
, 'r', encoding
='utf-8')
473 fobj
= open(incl_fname
, 'r')
475 raise QAPISemError(info
, "%s: %s" % (e
.strerror
, incl_fname
))
476 return QAPISchemaParser(fobj
, previously_included
, info
)
478 def _pragma(self
, name
, value
, info
):
479 global doc_required
, returns_whitelist
, name_case_whitelist
480 if name
== 'doc-required':
481 if not isinstance(value
, bool):
482 raise QAPISemError(info
,
483 "pragma 'doc-required' must be boolean")
485 elif name
== 'returns-whitelist':
486 if (not isinstance(value
, list)
487 or any([not isinstance(elt
, str) for elt
in value
])):
490 "pragma returns-whitelist must be a list of strings")
491 returns_whitelist
= value
492 elif name
== 'name-case-whitelist':
493 if (not isinstance(value
, list)
494 or any([not isinstance(elt
, str) for elt
in value
])):
497 "pragma name-case-whitelist must be a list of strings")
498 name_case_whitelist
= value
500 raise QAPISemError(info
, "unknown pragma '%s'" % name
)
502 def accept(self
, skip_comment
=True):
504 self
.tok
= self
.src
[self
.cursor
]
505 self
.pos
= self
.cursor
510 if self
.src
[self
.cursor
] == '#':
511 # Start of doc comment
513 self
.cursor
= self
.src
.find('\n', self
.cursor
)
515 self
.val
= self
.src
[self
.pos
:self
.cursor
]
517 elif self
.tok
in '{}:,[]':
519 elif self
.tok
== "'":
520 # Note: we accept only printable ASCII
524 ch
= self
.src
[self
.cursor
]
527 raise QAPIParseError(self
, "missing terminating \"'\"")
529 # Note: we recognize only \\ because we have
530 # no use for funny characters in strings
532 raise QAPIParseError(self
,
533 "unknown escape \\%s" % ch
)
541 if ord(ch
) < 32 or ord(ch
) >= 127:
542 raise QAPIParseError(
543 self
, "funny character in string")
545 elif self
.src
.startswith('true', self
.pos
):
549 elif self
.src
.startswith('false', self
.pos
):
553 elif self
.tok
== '\n':
554 if self
.cursor
== len(self
.src
):
557 self
.info
= self
.info
.next_line()
558 self
.line_pos
= self
.cursor
559 elif not self
.tok
.isspace():
560 # Show up to next structural, whitespace or quote
562 match
= re
.match('[^[\\]{}:,\\s\'"]+',
563 self
.src
[self
.cursor
-1:])
564 raise QAPIParseError(self
, "stray '%s'" % match
.group(0))
566 def get_members(self
):
572 raise QAPIParseError(self
, "expected string or '}'")
577 raise QAPIParseError(self
, "expected ':'")
580 raise QAPIParseError(self
, "duplicate key '%s'" % key
)
581 expr
[key
] = self
.get_expr(True)
586 raise QAPIParseError(self
, "expected ',' or '}'")
589 raise QAPIParseError(self
, "expected string")
591 def get_values(self
):
596 if self
.tok
not in "{['tfn":
597 raise QAPIParseError(
598 self
, "expected '{', '[', ']', string, boolean or 'null'")
600 expr
.append(self
.get_expr(True))
605 raise QAPIParseError(self
, "expected ',' or ']'")
608 def get_expr(self
, nested
):
609 if self
.tok
!= '{' and not nested
:
610 raise QAPIParseError(self
, "expected '{'")
613 expr
= self
.get_members()
614 elif self
.tok
== '[':
616 expr
= self
.get_values()
617 elif self
.tok
in "'tfn":
621 raise QAPIParseError(
622 self
, "expected '{', '[', string, boolean or 'null'")
625 def get_doc(self
, info
):
627 raise QAPIParseError(
628 self
, "junk after '##' at start of documentation comment")
630 doc
= QAPIDoc(self
, info
)
632 while self
.tok
== '#':
633 if self
.val
.startswith('##'):
636 raise QAPIParseError(
638 "junk after '##' at end of documentation comment")
646 raise QAPIParseError(self
, "documentation comment must end with '##'")
650 # Check (context-free) schema expression structure
653 # Names must be letters, numbers, -, and _. They must start with letter,
654 # except for downstream extensions which must start with __RFQDN_.
655 # Dots are only valid in the downstream extension prefix.
656 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
657 '[a-zA-Z][a-zA-Z0-9_-]*$')
660 def check_name_is_str(name
, info
, source
):
661 if not isinstance(name
, str):
662 raise QAPISemError(info
, "%s requires a string name" % source
)
665 def check_name_str(name
, info
, source
,
666 allow_optional
=False, enum_member
=False,
671 if allow_optional
and name
.startswith('*'):
672 membername
= name
[1:]
673 # Enum members can start with a digit, because the generated C
674 # code always prefixes it with the enum name
675 if enum_member
and membername
[0].isdigit():
676 membername
= 'D' + membername
677 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
678 # and 'q_obj_*' implicit type names.
679 if not valid_name
.match(membername
) or \
680 c_name(membername
, False).startswith('q_'):
681 raise QAPISemError(info
, "%s has an invalid name" % source
)
682 if not permit_upper
and name
.lower() != name
:
684 info
, "%s uses uppercase in name" % source
)
685 assert not membername
.startswith('*')
688 def check_defn_name_str(name
, info
, meta
):
689 check_name_str(name
, info
, meta
, permit_upper
=True)
690 if name
.endswith('Kind') or name
.endswith('List'):
692 info
, "%s name should not end in '%s'" % (meta
, name
[-4:]))
695 def check_if(expr
, info
, source
):
697 def check_if_str(ifcond
, info
):
698 if not isinstance(ifcond
, str):
701 "'if' condition of %s must be a string or a list of strings"
703 if ifcond
.strip() == '':
706 "'if' condition '%s' of %s makes no sense"
709 ifcond
= expr
.get('if')
712 if isinstance(ifcond
, list):
715 info
, "'if' condition [] of %s is useless" % source
)
717 check_if_str(elt
, info
)
719 check_if_str(ifcond
, info
)
722 def check_type(value
, info
, source
,
723 allow_array
=False, allow_dict
=False):
728 if isinstance(value
, list):
730 raise QAPISemError(info
, "%s cannot be an array" % source
)
731 if len(value
) != 1 or not isinstance(value
[0], str):
732 raise QAPISemError(info
,
733 "%s: array type must contain single type name" %
738 if isinstance(value
, str):
744 raise QAPISemError(info
, "%s should be a type name" % source
)
746 if not isinstance(value
, OrderedDict
):
747 raise QAPISemError(info
,
748 "%s should be an object or type name" % source
)
750 permit_upper
= allow_dict
in name_case_whitelist
752 # value is a dictionary, check that each member is okay
753 for (key
, arg
) in value
.items():
754 key_source
= "%s member '%s'" % (source
, key
)
755 check_name_str(key
, info
, key_source
,
756 allow_optional
=True, permit_upper
=permit_upper
)
757 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
758 raise QAPISemError(info
, "%s uses reserved name" % key_source
)
759 check_known_keys(arg
, info
, key_source
, ['type'], ['if'])
760 check_if(arg
, info
, key_source
)
762 check_type(arg
['type'], info
, key_source
, allow_array
=True)
765 def check_command(expr
, info
):
766 args
= expr
.get('data')
767 rets
= expr
.get('returns')
768 boxed
= expr
.get('boxed', False)
770 if boxed
and args
is None:
771 raise QAPISemError(info
, "'boxed': true requires 'data'")
772 check_type(args
, info
, "'data'", allow_dict
=not boxed
)
773 check_type(rets
, info
, "'returns'", allow_array
=True)
776 def check_event(expr
, info
):
777 args
= expr
.get('data')
778 boxed
= expr
.get('boxed', False)
780 if boxed
and args
is None:
781 raise QAPISemError(info
, "'boxed': true requires 'data'")
782 check_type(args
, info
, "'data'", allow_dict
=not boxed
)
785 def check_union(expr
, info
):
787 base
= expr
.get('base')
788 discriminator
= expr
.get('discriminator')
789 members
= expr
['data']
791 if discriminator
is None: # simple union
793 raise QAPISemError(info
, "'base' requires 'discriminator'")
795 check_type(base
, info
, "'base'", allow_dict
=name
)
797 raise QAPISemError(info
, "'discriminator' requires 'base'")
798 check_name_is_str(discriminator
, info
, "'discriminator'")
800 for (key
, value
) in members
.items():
801 source
= "'data' member '%s'" % key
802 check_name_str(key
, info
, source
)
803 check_known_keys(value
, info
, source
, ['type'], ['if'])
804 check_if(value
, info
, source
)
806 check_type(value
['type'], info
, source
, allow_array
=not base
)
809 def check_alternate(expr
, info
):
810 members
= expr
['data']
812 if len(members
) == 0:
813 raise QAPISemError(info
, "'data' must not be empty")
814 for (key
, value
) in members
.items():
815 source
= "'data' member '%s'" % key
816 check_name_str(key
, info
, source
)
817 check_known_keys(value
, info
, source
, ['type'], ['if'])
818 check_if(value
, info
, source
)
820 check_type(value
['type'], info
, source
)
823 def check_enum(expr
, info
):
825 members
= expr
['data']
826 prefix
= expr
.get('prefix')
828 if not isinstance(members
, list):
829 raise QAPISemError(info
, "'data' must be an array")
830 if prefix
is not None and not isinstance(prefix
, str):
831 raise QAPISemError(info
, "'prefix' must be a string")
833 permit_upper
= name
in name_case_whitelist
835 for member
in members
:
836 source
= "'data' member"
837 check_known_keys(member
, info
, source
, ['name'], ['if'])
838 check_name_is_str(member
['name'], info
, source
)
839 source
= "%s '%s'" % (source
, member
['name'])
840 check_name_str(member
['name'], info
, source
,
841 enum_member
=True, permit_upper
=permit_upper
)
842 check_if(member
, info
, source
)
846 def check_struct(expr
, info
):
847 name
= expr
['struct']
848 members
= expr
['data']
849 features
= expr
.get('features')
851 check_type(members
, info
, "'data'", allow_dict
=name
)
852 check_type(expr
.get('base'), info
, "'base'")
855 if not isinstance(features
, list):
856 raise QAPISemError(info
, "'features' must be an array")
858 source
= "'features' member"
859 assert isinstance(f
, dict)
860 check_known_keys(f
, info
, source
, ['name'], ['if'])
861 check_name_is_str(f
['name'], info
, source
)
862 source
= "%s '%s'" % (source
, f
['name'])
863 check_name_str(f
['name'], info
, source
)
864 check_if(f
, info
, source
)
868 def check_known_keys(value
, info
, source
, required
, optional
):
871 return ', '.join("'" + e
+ "'" for e
in sorted(elems
))
873 missing
= set(required
) - set(value
)
878 % (source
, 's' if len(missing
) > 1 else '',
880 allowed
= set(required
+ optional
)
881 unknown
= set(value
) - allowed
885 "%s has unknown key%s %s\nValid keys are %s."
886 % (source
, 's' if len(unknown
) > 1 else '',
887 pprint(unknown
), pprint(allowed
)))
890 def check_keys(expr
, info
, meta
, required
, optional
=[]):
891 check_known_keys(expr
, info
, meta
, required
+ [meta
], optional
)
894 def check_flags(expr
, info
):
895 for key
in ['gen', 'success-response']:
896 if key
in expr
and expr
[key
] is not False:
898 info
, "flag '%s' may only use false value" % key
)
899 for key
in ['boxed', 'allow-oob', 'allow-preconfig']:
900 if key
in expr
and expr
[key
] is not True:
902 info
, "flag '%s' may only use true value" % key
)
905 def normalize_enum(expr
):
906 if isinstance(expr
['data'], list):
907 expr
['data'] = [m
if isinstance(m
, dict) else {'name': m
}
908 for m
in expr
['data']]
911 def normalize_members(members
):
912 if isinstance(members
, OrderedDict
):
913 for key
, arg
in members
.items():
914 if isinstance(arg
, dict):
916 members
[key
] = {'type': arg
}
919 def normalize_features(features
):
920 if isinstance(features
, list):
921 features
[:] = [f
if isinstance(f
, dict) else {'name': f
}
925 def normalize_if(expr
):
926 ifcond
= expr
.get('if')
927 if isinstance(ifcond
, str):
928 expr
['if'] = [ifcond
]
931 def check_exprs(exprs
):
932 for expr_elem
in exprs
:
933 expr
= expr_elem
['expr']
934 info
= expr_elem
['info']
935 doc
= expr_elem
.get('doc')
937 if 'include' in expr
:
940 if not doc
and doc_required
:
941 raise QAPISemError(info
,
942 "definition missing documentation comment")
946 elif 'union' in expr
:
948 elif 'alternate' in expr
:
950 elif 'struct' in expr
:
952 elif 'command' in expr
:
954 elif 'event' in expr
:
957 raise QAPISemError(info
, "expression is missing metatype")
960 check_name_is_str(name
, info
, "'%s'" % meta
)
961 info
.set_defn(meta
, name
)
962 check_defn_name_str(name
, info
, meta
)
964 if doc
and doc
.symbol
!= name
:
966 info
, "documentation comment is for '%s'" % doc
.symbol
)
969 check_keys(expr
, info
, 'enum', ['data'], ['if', 'prefix'])
971 check_enum(expr
, info
)
972 elif meta
== 'union':
973 check_keys(expr
, info
, 'union', ['data'],
974 ['base', 'discriminator', 'if'])
975 normalize_members(expr
.get('base'))
976 normalize_members(expr
['data'])
977 check_union(expr
, info
)
978 elif meta
== 'alternate':
979 check_keys(expr
, info
, 'alternate', ['data'], ['if'])
980 normalize_members(expr
['data'])
981 check_alternate(expr
, info
)
982 elif meta
== 'struct':
983 check_keys(expr
, info
, 'struct', ['data'],
984 ['base', 'if', 'features'])
985 normalize_members(expr
['data'])
986 normalize_features(expr
.get('features'))
987 check_struct(expr
, info
)
988 elif meta
== 'command':
989 check_keys(expr
, info
, 'command', [],
990 ['data', 'returns', 'gen', 'success-response',
991 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
992 normalize_members(expr
.get('data'))
993 check_command(expr
, info
)
994 elif meta
== 'event':
995 check_keys(expr
, info
, 'event', [], ['data', 'boxed', 'if'])
996 normalize_members(expr
.get('data'))
997 check_event(expr
, info
)
999 assert False, 'unexpected meta type'
1002 check_if(expr
, info
, meta
)
1003 check_flags(expr
, info
)
1006 doc
.check_expr(expr
)
1012 # Schema compiler frontend
1013 # TODO catching name collisions in generated code would be nice
1016 class QAPISchemaEntity(object):
1019 def __init__(self
, name
, info
, doc
, ifcond
=None):
1020 assert name
is None or isinstance(name
, str)
1023 # For explicitly defined entities, info points to the (explicit)
1024 # definition. For builtins (and their arrays), info is None.
1025 # For implicitly defined entities, info points to a place that
1026 # triggered the implicit definition (there may be more than one
1030 self
._ifcond
= ifcond
or []
1031 self
._checked
= False
1034 return c_name(self
.name
)
1036 def check(self
, schema
):
1037 assert not self
._checked
1039 self
._module
= os
.path
.relpath(self
.info
.fname
,
1040 os
.path
.dirname(schema
.fname
))
1041 self
._checked
= True
1045 assert self
._checked
1050 assert self
._checked
1053 def is_implicit(self
):
1054 return not self
.info
1056 def visit(self
, visitor
):
1057 assert self
._checked
1061 return "%s '%s'" % (self
.meta
, self
.name
)
1064 class QAPISchemaVisitor(object):
1065 def visit_begin(self
, schema
):
1068 def visit_end(self
):
1071 def visit_module(self
, fname
):
1074 def visit_needed(self
, entity
):
1075 # Default to visiting everything
1078 def visit_include(self
, fname
, info
):
1081 def visit_builtin_type(self
, name
, info
, json_type
):
1084 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
1087 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
1090 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
,
1094 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
1098 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
1101 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
1102 success_response
, boxed
, allow_oob
, allow_preconfig
):
1105 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
1109 class QAPISchemaInclude(QAPISchemaEntity
):
1111 def __init__(self
, fname
, info
):
1112 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1115 def visit(self
, visitor
):
1116 QAPISchemaEntity
.visit(self
, visitor
)
1117 visitor
.visit_include(self
.fname
, self
.info
)
1120 class QAPISchemaType(QAPISchemaEntity
):
1121 # Return the C type for common use.
1122 # For the types we commonly box, this is a pointer type.
1126 # Return the C type to be used in a parameter list.
1127 def c_param_type(self
):
1128 return self
.c_type()
1130 # Return the C type to be used where we suppress boxing.
1131 def c_unboxed_type(self
):
1132 return self
.c_type()
1134 def json_type(self
):
1137 def alternate_qtype(self
):
1139 'null': 'QTYPE_QNULL',
1140 'string': 'QTYPE_QSTRING',
1141 'number': 'QTYPE_QNUM',
1142 'int': 'QTYPE_QNUM',
1143 'boolean': 'QTYPE_QBOOL',
1144 'object': 'QTYPE_QDICT'
1146 return json2qtype
.get(self
.json_type())
1149 if self
.is_implicit():
1155 return "%s type '%s'" % (self
.meta
, self
.name
)
1158 class QAPISchemaBuiltinType(QAPISchemaType
):
1161 def __init__(self
, name
, json_type
, c_type
):
1162 QAPISchemaType
.__init
__(self
, name
, None, None)
1163 assert not c_type
or isinstance(c_type
, str)
1164 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1166 self
._json
_type
_name
= json_type
1167 self
._c
_type
_name
= c_type
1173 return self
._c
_type
_name
1175 def c_param_type(self
):
1176 if self
.name
== 'str':
1177 return 'const ' + self
._c
_type
_name
1178 return self
._c
_type
_name
1180 def json_type(self
):
1181 return self
._json
_type
_name
1184 return self
.json_type()
1186 def visit(self
, visitor
):
1187 QAPISchemaType
.visit(self
, visitor
)
1188 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1191 class QAPISchemaEnumType(QAPISchemaType
):
1194 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
1195 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1197 assert isinstance(m
, QAPISchemaEnumMember
)
1198 m
.set_defined_in(name
)
1199 assert prefix
is None or isinstance(prefix
, str)
1200 self
.members
= members
1201 self
.prefix
= prefix
1203 def check(self
, schema
):
1204 QAPISchemaType
.check(self
, schema
)
1206 for m
in self
.members
:
1207 m
.check_clash(self
.info
, seen
)
1209 self
.doc
.connect_member(m
)
1211 def is_implicit(self
):
1212 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1213 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1216 return c_name(self
.name
)
1218 def member_names(self
):
1219 return [m
.name
for m
in self
.members
]
1221 def json_type(self
):
1224 def visit(self
, visitor
):
1225 QAPISchemaType
.visit(self
, visitor
)
1226 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
1227 self
.members
, self
.prefix
)
1230 class QAPISchemaArrayType(QAPISchemaType
):
1233 def __init__(self
, name
, info
, element_type
):
1234 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
1235 assert isinstance(element_type
, str)
1236 self
._element
_type
_name
= element_type
1237 self
.element_type
= None
1239 def check(self
, schema
):
1240 QAPISchemaType
.check(self
, schema
)
1241 self
.element_type
= schema
.resolve_type(
1242 self
._element
_type
_name
, self
.info
,
1243 self
.info
and self
.info
.defn_meta
)
1244 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
1248 assert self
._checked
1249 return self
.element_type
.ifcond
1253 assert self
._checked
1254 return self
.element_type
.module
1256 def is_implicit(self
):
1260 return c_name(self
.name
) + pointer_suffix
1262 def json_type(self
):
1266 elt_doc_type
= self
.element_type
.doc_type()
1267 if not elt_doc_type
:
1269 return 'array of ' + elt_doc_type
1271 def visit(self
, visitor
):
1272 QAPISchemaType
.visit(self
, visitor
)
1273 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
1278 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
1281 class QAPISchemaObjectType(QAPISchemaType
):
1282 def __init__(self
, name
, info
, doc
, ifcond
,
1283 base
, local_members
, variants
, features
):
1284 # struct has local_members, optional base, and no variants
1285 # flat union has base, variants, and no local_members
1286 # simple union has local_members, variants, and no base
1287 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1288 self
.meta
= 'union' if variants
else 'struct'
1289 assert base
is None or isinstance(base
, str)
1290 for m
in local_members
:
1291 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1292 m
.set_defined_in(name
)
1293 if variants
is not None:
1294 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1295 variants
.set_defined_in(name
)
1297 assert isinstance(f
, QAPISchemaFeature
)
1298 f
.set_defined_in(name
)
1299 self
._base
_name
= base
1301 self
.local_members
= local_members
1302 self
.variants
= variants
1304 self
.features
= features
1306 def check(self
, schema
):
1307 # This calls another type T's .check() exactly when the C
1308 # struct emitted by gen_object() contains that T's C struct
1309 # (pointers don't count).
1310 if self
.members
is not None:
1311 # A previous .check() completed: nothing to do
1314 # Recursed: C struct contains itself
1315 raise QAPISemError(self
.info
,
1316 "object %s contains itself" % self
.name
)
1318 QAPISchemaType
.check(self
, schema
)
1319 assert self
._checked
and self
.members
is None
1321 seen
= OrderedDict()
1323 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
1325 if (not isinstance(self
.base
, QAPISchemaObjectType
)
1326 or self
.base
.variants
):
1329 "'base' requires a struct type, %s isn't"
1330 % self
.base
.describe())
1331 self
.base
.check(schema
)
1332 self
.base
.check_clash(self
.info
, seen
)
1333 for m
in self
.local_members
:
1335 m
.check_clash(self
.info
, seen
)
1337 self
.doc
.connect_member(m
)
1338 members
= seen
.values()
1341 self
.variants
.check(schema
, seen
)
1342 self
.variants
.check_clash(self
.info
, seen
)
1344 # Features are in a name space separate from members
1346 for f
in self
.features
:
1347 f
.check_clash(self
.info
, seen
)
1352 self
.members
= members
# mark completed
1354 # Check that the members of this type do not cause duplicate JSON members,
1355 # and update seen to track the members seen so far. Report any errors
1356 # on behalf of info, which is not necessarily self.info
1357 def check_clash(self
, info
, seen
):
1358 assert self
._checked
1359 assert not self
.variants
# not implemented
1360 for m
in self
.members
:
1361 m
.check_clash(info
, seen
)
1365 assert self
._checked
1366 if isinstance(self
._ifcond
, QAPISchemaType
):
1367 # Simple union wrapper type inherits from wrapped type;
1368 # see _make_implicit_object_type()
1369 return self
._ifcond
.ifcond
1372 def is_implicit(self
):
1373 # See QAPISchema._make_implicit_object_type(), as well as
1374 # _def_predefineds()
1375 return self
.name
.startswith('q_')
1378 assert self
.members
is not None
1379 return not self
.members
and not self
.variants
1382 assert self
.name
!= 'q_empty'
1383 return QAPISchemaType
.c_name(self
)
1386 assert not self
.is_implicit()
1387 return c_name(self
.name
) + pointer_suffix
1389 def c_unboxed_type(self
):
1390 return c_name(self
.name
)
1392 def json_type(self
):
1395 def visit(self
, visitor
):
1396 QAPISchemaType
.visit(self
, visitor
)
1397 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
1398 self
.base
, self
.local_members
, self
.variants
,
1400 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
1401 self
.members
, self
.variants
,
1405 class QAPISchemaMember(object):
1406 """ Represents object members, enum members and features """
1409 def __init__(self
, name
, info
, ifcond
=None):
1410 assert isinstance(name
, str)
1413 self
.ifcond
= ifcond
or []
1414 self
.defined_in
= None
1416 def set_defined_in(self
, name
):
1417 assert not self
.defined_in
1418 self
.defined_in
= name
1420 def check_clash(self
, info
, seen
):
1421 cname
= c_name(self
.name
)
1425 "%s collides with %s"
1426 % (self
.describe(info
), seen
[cname
].describe(info
)))
1429 def describe(self
, info
):
1431 defined_in
= self
.defined_in
1434 if defined_in
.startswith('q_obj_'):
1435 # See QAPISchema._make_implicit_object_type() - reverse the
1436 # mapping there to create a nice human-readable description
1437 defined_in
= defined_in
[6:]
1438 if defined_in
.endswith('-arg'):
1439 # Implicit type created for a command's dict 'data'
1440 assert role
== 'member'
1442 elif defined_in
.endswith('-base'):
1443 # Implicit type created for a flat union's dict 'base'
1444 role
= 'base ' + role
1446 # Implicit type created for a simple union's branch
1447 assert defined_in
.endswith('-wrapper')
1448 # Unreachable and not implemented
1450 elif defined_in
.endswith('Kind'):
1451 # See QAPISchema._make_implicit_enum_type()
1452 # Implicit enum created for simple union's branches
1453 assert role
== 'value'
1455 elif defined_in
!= info
.defn_name
:
1456 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
1457 return "%s '%s'" % (role
, self
.name
)
1460 class QAPISchemaEnumMember(QAPISchemaMember
):
1464 class QAPISchemaFeature(QAPISchemaMember
):
1468 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1469 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None):
1470 QAPISchemaMember
.__init
__(self
, name
, info
, ifcond
)
1471 assert isinstance(typ
, str)
1472 assert isinstance(optional
, bool)
1473 self
._type
_name
= typ
1475 self
.optional
= optional
1477 def check(self
, schema
):
1478 assert self
.defined_in
1479 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
1483 class QAPISchemaObjectTypeVariants(object):
1484 def __init__(self
, tag_name
, info
, tag_member
, variants
):
1485 # Flat unions pass tag_name but not tag_member.
1486 # Simple unions and alternates pass tag_member but not tag_name.
1487 # After check(), tag_member is always set, and tag_name remains
1488 # a reliable witness of being used by a flat union.
1489 assert bool(tag_member
) != bool(tag_name
)
1490 assert (isinstance(tag_name
, str) or
1491 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1493 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1494 self
._tag
_name
= tag_name
1496 self
.tag_member
= tag_member
1497 self
.variants
= variants
1499 def set_defined_in(self
, name
):
1500 for v
in self
.variants
:
1501 v
.set_defined_in(name
)
1503 def check(self
, schema
, seen
):
1504 if not self
.tag_member
: # flat union
1505 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
1507 # Pointing to the base type when not implicit would be
1508 # nice, but we don't know it here
1509 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
1512 "discriminator '%s' is not a member of %s"
1513 % (self
._tag
_name
, base
))
1515 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
1517 if not base_type
.is_implicit():
1518 base
= "base type '%s'" % self
.tag_member
.defined_in
1519 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
1522 "discriminator member '%s' of %s must be of enum type"
1523 % (self
._tag
_name
, base
))
1524 if self
.tag_member
.optional
:
1527 "discriminator member '%s' of %s must not be optional"
1528 % (self
._tag
_name
, base
))
1529 if self
.tag_member
.ifcond
:
1532 "discriminator member '%s' of %s must not be conditional"
1533 % (self
._tag
_name
, base
))
1534 else: # simple union
1535 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1536 assert not self
.tag_member
.optional
1537 assert self
.tag_member
.ifcond
== []
1538 if self
._tag
_name
: # flat union
1539 # branches that are not explicitly covered get an empty type
1540 cases
= set([v
.name
for v
in self
.variants
])
1541 for m
in self
.tag_member
.type.members
:
1542 if m
.name
not in cases
:
1543 v
= QAPISchemaObjectTypeVariant(m
.name
, self
.info
,
1544 'q_empty', m
.ifcond
)
1545 v
.set_defined_in(self
.tag_member
.defined_in
)
1546 self
.variants
.append(v
)
1547 if not self
.variants
:
1548 raise QAPISemError(self
.info
, "union has no branches")
1549 for v
in self
.variants
:
1551 # Union names must match enum values; alternate names are
1552 # checked separately. Use 'seen' to tell the two apart.
1554 if v
.name
not in self
.tag_member
.type.member_names():
1557 "branch '%s' is not a value of %s"
1558 % (v
.name
, self
.tag_member
.type.describe()))
1559 if (not isinstance(v
.type, QAPISchemaObjectType
)
1560 or v
.type.variants
):
1564 % (v
.describe(self
.info
), v
.type.describe()))
1565 v
.type.check(schema
)
1567 def check_clash(self
, info
, seen
):
1568 for v
in self
.variants
:
1569 # Reset seen map for each variant, since qapi names from one
1570 # branch do not affect another branch
1571 v
.type.check_clash(info
, dict(seen
))
1574 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1577 def __init__(self
, name
, info
, typ
, ifcond
=None):
1578 QAPISchemaObjectTypeMember
.__init
__(self
, name
, info
, typ
,
1582 class QAPISchemaAlternateType(QAPISchemaType
):
1585 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
1586 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1587 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1588 assert variants
.tag_member
1589 variants
.set_defined_in(name
)
1590 variants
.tag_member
.set_defined_in(self
.name
)
1591 self
.variants
= variants
1593 def check(self
, schema
):
1594 QAPISchemaType
.check(self
, schema
)
1595 self
.variants
.tag_member
.check(schema
)
1596 # Not calling self.variants.check_clash(), because there's nothing
1598 self
.variants
.check(schema
, {})
1599 # Alternate branch names have no relation to the tag enum values;
1600 # so we have to check for potential name collisions ourselves.
1603 for v
in self
.variants
.variants
:
1604 v
.check_clash(self
.info
, seen
)
1605 qtype
= v
.type.alternate_qtype()
1610 % (v
.describe(self
.info
), v
.type.describe()))
1611 conflicting
= set([qtype
])
1612 if qtype
== 'QTYPE_QSTRING':
1613 if isinstance(v
.type, QAPISchemaEnumType
):
1614 for m
in v
.type.members
:
1615 if m
.name
in ['on', 'off']:
1616 conflicting
.add('QTYPE_QBOOL')
1617 if re
.match(r
'[-+0-9.]', m
.name
):
1618 # lazy, could be tightened
1619 conflicting
.add('QTYPE_QNUM')
1621 conflicting
.add('QTYPE_QNUM')
1622 conflicting
.add('QTYPE_QBOOL')
1623 for qt
in conflicting
:
1624 if qt
in types_seen
:
1627 "%s can't be distinguished from '%s'"
1628 % (v
.describe(self
.info
), types_seen
[qt
]))
1629 types_seen
[qt
] = v
.name
1631 self
.doc
.connect_member(v
)
1636 return c_name(self
.name
) + pointer_suffix
1638 def json_type(self
):
1641 def visit(self
, visitor
):
1642 QAPISchemaType
.visit(self
, visitor
)
1643 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
1647 class QAPISchemaCommand(QAPISchemaEntity
):
1650 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
1651 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
1652 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1653 assert not arg_type
or isinstance(arg_type
, str)
1654 assert not ret_type
or isinstance(ret_type
, str)
1655 self
._arg
_type
_name
= arg_type
1656 self
.arg_type
= None
1657 self
._ret
_type
_name
= ret_type
1658 self
.ret_type
= None
1660 self
.success_response
= success_response
1662 self
.allow_oob
= allow_oob
1663 self
.allow_preconfig
= allow_preconfig
1665 def check(self
, schema
):
1666 QAPISchemaEntity
.check(self
, schema
)
1667 if self
._arg
_type
_name
:
1668 self
.arg_type
= schema
.resolve_type(
1669 self
._arg
_type
_name
, self
.info
, "command's 'data'")
1670 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
1673 "command's 'data' cannot take %s"
1674 % self
.arg_type
.describe())
1675 if self
.arg_type
.variants
and not self
.boxed
:
1678 "command's 'data' can take %s only with 'boxed': true"
1679 % self
.arg_type
.describe())
1680 if self
._ret
_type
_name
:
1681 self
.ret_type
= schema
.resolve_type(
1682 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
1683 if self
.name
not in returns_whitelist
:
1684 if not (isinstance(self
.ret_type
, QAPISchemaObjectType
)
1685 or (isinstance(self
.ret_type
, QAPISchemaArrayType
)
1686 and isinstance(self
.ret_type
.element_type
,
1687 QAPISchemaObjectType
))):
1690 "command's 'returns' cannot take %s"
1691 % self
.ret_type
.describe())
1693 def visit(self
, visitor
):
1694 QAPISchemaEntity
.visit(self
, visitor
)
1695 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
1696 self
.arg_type
, self
.ret_type
,
1697 self
.gen
, self
.success_response
,
1698 self
.boxed
, self
.allow_oob
,
1699 self
.allow_preconfig
)
1702 class QAPISchemaEvent(QAPISchemaEntity
):
1705 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
1706 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1707 assert not arg_type
or isinstance(arg_type
, str)
1708 self
._arg
_type
_name
= arg_type
1709 self
.arg_type
= None
1712 def check(self
, schema
):
1713 QAPISchemaEntity
.check(self
, schema
)
1714 if self
._arg
_type
_name
:
1715 self
.arg_type
= schema
.resolve_type(
1716 self
._arg
_type
_name
, self
.info
, "event's 'data'")
1717 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
1720 "event's 'data' cannot take %s"
1721 % self
.arg_type
.describe())
1722 if self
.arg_type
.variants
and not self
.boxed
:
1725 "event's 'data' can take %s only with 'boxed': true"
1726 % self
.arg_type
.describe())
1728 def visit(self
, visitor
):
1729 QAPISchemaEntity
.visit(self
, visitor
)
1730 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
1731 self
.arg_type
, self
.boxed
)
1734 class QAPISchema(object):
1735 def __init__(self
, fname
):
1737 if sys
.version_info
[0] >= 3:
1738 f
= open(fname
, 'r', encoding
='utf-8')
1740 f
= open(fname
, 'r')
1741 parser
= QAPISchemaParser(f
)
1742 exprs
= check_exprs(parser
.exprs
)
1743 self
.docs
= parser
.docs
1744 self
._entity
_list
= []
1745 self
._entity
_dict
= {}
1746 self
._predefining
= True
1747 self
._def
_predefineds
()
1748 self
._predefining
= False
1749 self
._def
_exprs
(exprs
)
1752 def _def_entity(self
, ent
):
1753 # Only the predefined types are allowed to not have info
1754 assert ent
.info
or self
._predefining
1755 self
._entity
_list
.append(ent
)
1756 if ent
.name
is None:
1758 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1759 # because they're liable to clash in generated C.
1760 other_ent
= self
._entity
_dict
.get(ent
.name
)
1763 ent
.info
, "%s is already defined" % other_ent
.describe())
1764 self
._entity
_dict
[ent
.name
] = ent
1766 def lookup_entity(self
, name
, typ
=None):
1767 ent
= self
._entity
_dict
.get(name
)
1768 if typ
and not isinstance(ent
, typ
):
1772 def lookup_type(self
, name
):
1773 return self
.lookup_entity(name
, QAPISchemaType
)
1775 def resolve_type(self
, name
, info
, what
):
1776 typ
= self
.lookup_type(name
)
1781 info
, "%s uses unknown type '%s'" % (what
, name
))
1784 def _def_include(self
, expr
, info
, doc
):
1785 include
= expr
['include']
1788 while main_info
.parent
:
1789 main_info
= main_info
.parent
1790 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
.fname
))
1791 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
1793 def _def_builtin_type(self
, name
, json_type
, c_type
):
1794 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1795 # Instantiating only the arrays that are actually used would
1796 # be nice, but we can't as long as their generated code
1797 # (qapi-builtin-types.[ch]) may be shared by some other
1799 self
._make
_array
_type
(name
, None)
1801 def _def_predefineds(self
):
1802 for t
in [('str', 'string', 'char' + pointer_suffix
),
1803 ('number', 'number', 'double'),
1804 ('int', 'int', 'int64_t'),
1805 ('int8', 'int', 'int8_t'),
1806 ('int16', 'int', 'int16_t'),
1807 ('int32', 'int', 'int32_t'),
1808 ('int64', 'int', 'int64_t'),
1809 ('uint8', 'int', 'uint8_t'),
1810 ('uint16', 'int', 'uint16_t'),
1811 ('uint32', 'int', 'uint32_t'),
1812 ('uint64', 'int', 'uint64_t'),
1813 ('size', 'int', 'uint64_t'),
1814 ('bool', 'boolean', 'bool'),
1815 ('any', 'value', 'QObject' + pointer_suffix
),
1816 ('null', 'null', 'QNull' + pointer_suffix
)]:
1817 self
._def
_builtin
_type
(*t
)
1818 self
.the_empty_object_type
= QAPISchemaObjectType(
1819 'q_empty', None, None, None, None, [], None, [])
1820 self
._def
_entity
(self
.the_empty_object_type
)
1822 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1824 qtype_values
= self
._make
_enum
_members
(
1825 [{'name': n
} for n
in qtypes
], None)
1827 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
1828 qtype_values
, 'QTYPE'))
1830 def _make_features(self
, features
, info
):
1831 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
1834 def _make_enum_members(self
, values
, info
):
1835 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
1838 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
1839 # See also QAPISchemaObjectTypeMember.describe()
1840 name
= name
+ 'Kind' # reserved by check_defn_name_str()
1841 self
._def
_entity
(QAPISchemaEnumType(
1842 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
, info
),
1846 def _make_array_type(self
, element_type
, info
):
1847 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1848 if not self
.lookup_type(name
):
1849 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1852 def _make_implicit_object_type(self
, name
, info
, doc
, ifcond
,
1856 # See also QAPISchemaObjectTypeMember.describe()
1857 name
= 'q_obj_%s-%s' % (name
, role
)
1858 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1860 # The implicit object type has multiple users. This can
1861 # happen only for simple unions' implicit wrapper types.
1862 # Its ifcond should be the disjunction of its user's
1863 # ifconds. Not implemented. Instead, we always pass the
1864 # wrapped type's ifcond, which is trivially the same for all
1865 # users. It's also necessary for the wrapper to compile.
1866 # But it's not tight: the disjunction need not imply it. We
1867 # may end up compiling useless wrapper types.
1868 # TODO kill simple unions or implement the disjunction
1869 assert (ifcond
or []) == typ
._ifcond
# pylint: disable=protected-access
1871 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
,
1872 None, members
, None, []))
1875 def _def_enum_type(self
, expr
, info
, doc
):
1878 prefix
= expr
.get('prefix')
1879 ifcond
= expr
.get('if')
1880 self
._def
_entity
(QAPISchemaEnumType(
1881 name
, info
, doc
, ifcond
,
1882 self
._make
_enum
_members
(data
, info
), prefix
))
1884 def _make_member(self
, name
, typ
, ifcond
, info
):
1886 if name
.startswith('*'):
1889 if isinstance(typ
, list):
1890 assert len(typ
) == 1
1891 typ
= self
._make
_array
_type
(typ
[0], info
)
1892 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
)
1894 def _make_members(self
, data
, info
):
1895 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
1896 for (key
, value
) in data
.items()]
1898 def _def_struct_type(self
, expr
, info
, doc
):
1899 name
= expr
['struct']
1900 base
= expr
.get('base')
1902 ifcond
= expr
.get('if')
1903 features
= expr
.get('features', [])
1904 self
._def
_entity
(QAPISchemaObjectType(
1905 name
, info
, doc
, ifcond
, base
,
1906 self
._make
_members
(data
, info
),
1908 self
._make
_features
(features
, info
)))
1910 def _make_variant(self
, case
, typ
, ifcond
, info
):
1911 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1913 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1914 if isinstance(typ
, list):
1915 assert len(typ
) == 1
1916 typ
= self
._make
_array
_type
(typ
[0], info
)
1917 typ
= self
._make
_implicit
_object
_type
(
1918 typ
, info
, None, self
.lookup_type(typ
),
1919 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
1920 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1922 def _def_union_type(self
, expr
, info
, doc
):
1923 name
= expr
['union']
1925 base
= expr
.get('base')
1926 ifcond
= expr
.get('if')
1927 tag_name
= expr
.get('discriminator')
1929 if isinstance(base
, dict):
1930 base
= self
._make
_implicit
_object
_type
(
1931 name
, info
, doc
, ifcond
,
1932 'base', self
._make
_members
(base
, info
))
1934 variants
= [self
._make
_variant
(key
, value
['type'],
1935 value
.get('if'), info
)
1936 for (key
, value
) in data
.items()]
1939 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1940 value
.get('if'), info
)
1941 for (key
, value
) in data
.items()]
1942 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1943 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1944 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1945 members
= [tag_member
]
1947 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
1948 QAPISchemaObjectTypeVariants(
1949 tag_name
, info
, tag_member
, variants
),
1952 def _def_alternate_type(self
, expr
, info
, doc
):
1953 name
= expr
['alternate']
1955 ifcond
= expr
.get('if')
1956 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1958 for (key
, value
) in data
.items()]
1959 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1961 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
1962 QAPISchemaObjectTypeVariants(
1963 None, info
, tag_member
, variants
)))
1965 def _def_command(self
, expr
, info
, doc
):
1966 name
= expr
['command']
1967 data
= expr
.get('data')
1968 rets
= expr
.get('returns')
1969 gen
= expr
.get('gen', True)
1970 success_response
= expr
.get('success-response', True)
1971 boxed
= expr
.get('boxed', False)
1972 allow_oob
= expr
.get('allow-oob', False)
1973 allow_preconfig
= expr
.get('allow-preconfig', False)
1974 ifcond
= expr
.get('if')
1975 if isinstance(data
, OrderedDict
):
1976 data
= self
._make
_implicit
_object
_type
(
1977 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1978 if isinstance(rets
, list):
1979 assert len(rets
) == 1
1980 rets
= self
._make
_array
_type
(rets
[0], info
)
1981 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
1982 gen
, success_response
,
1983 boxed
, allow_oob
, allow_preconfig
))
1985 def _def_event(self
, expr
, info
, doc
):
1986 name
= expr
['event']
1987 data
= expr
.get('data')
1988 boxed
= expr
.get('boxed', False)
1989 ifcond
= expr
.get('if')
1990 if isinstance(data
, OrderedDict
):
1991 data
= self
._make
_implicit
_object
_type
(
1992 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1993 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
1995 def _def_exprs(self
, exprs
):
1996 for expr_elem
in exprs
:
1997 expr
= expr_elem
['expr']
1998 info
= expr_elem
['info']
1999 doc
= expr_elem
.get('doc')
2001 self
._def
_enum
_type
(expr
, info
, doc
)
2002 elif 'struct' in expr
:
2003 self
._def
_struct
_type
(expr
, info
, doc
)
2004 elif 'union' in expr
:
2005 self
._def
_union
_type
(expr
, info
, doc
)
2006 elif 'alternate' in expr
:
2007 self
._def
_alternate
_type
(expr
, info
, doc
)
2008 elif 'command' in expr
:
2009 self
._def
_command
(expr
, info
, doc
)
2010 elif 'event' in expr
:
2011 self
._def
_event
(expr
, info
, doc
)
2012 elif 'include' in expr
:
2013 self
._def
_include
(expr
, info
, doc
)
2018 for ent
in self
._entity
_list
:
2021 def visit(self
, visitor
):
2022 visitor
.visit_begin(self
)
2024 visitor
.visit_module(module
)
2025 for entity
in self
._entity
_list
:
2026 if visitor
.visit_needed(entity
):
2027 if entity
.module
!= module
:
2028 module
= entity
.module
2029 visitor
.visit_module(module
)
2030 entity
.visit(visitor
)
2035 # Code generation helpers
2038 def camel_case(name
):
2042 if ch
in ['_', '-']:
2045 new_name
+= ch
.upper()
2048 new_name
+= ch
.lower()
2052 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2053 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2054 # ENUM24_Name -> ENUM24_NAME
2055 def camel_to_upper(value
):
2056 c_fun_str
= c_name(value
, False)
2061 length
= len(c_fun_str
)
2062 for i
in range(length
):
2064 # When c is upper and no '_' appears before, do more checks
2065 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
2066 if i
< length
- 1 and c_fun_str
[i
+ 1].islower():
2068 elif c_fun_str
[i
- 1].isdigit():
2071 return new_name
.lstrip('_').upper()
2074 def c_enum_const(type_name
, const_name
, prefix
=None):
2075 if prefix
is not None:
2077 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
2080 if hasattr(str, 'maketrans'):
2081 c_name_trans
= str.maketrans('.-', '__')
2083 c_name_trans
= string
.maketrans('.-', '__')
2086 # Map @name to a valid C identifier.
2087 # If @protect, avoid returning certain ticklish identifiers (like
2088 # C keywords) by prepending 'q_'.
2090 # Used for converting 'name' from a 'name':'type' qapi definition
2091 # into a generated struct member, as well as converting type names
2092 # into substrings of a generated C function name.
2093 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2094 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2095 def c_name(name
, protect
=True):
2096 # ANSI X3J11/88-090, 3.1.1
2097 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
2098 'default', 'do', 'double', 'else', 'enum', 'extern',
2099 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2100 'return', 'short', 'signed', 'sizeof', 'static',
2101 'struct', 'switch', 'typedef', 'union', 'unsigned',
2102 'void', 'volatile', 'while'])
2103 # ISO/IEC 9899:1999, 6.4.1
2104 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2105 # ISO/IEC 9899:2011, 6.4.1
2106 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2107 '_Noreturn', '_Static_assert', '_Thread_local'])
2108 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2110 gcc_words
= set(['asm', 'typeof'])
2111 # C++ ISO/IEC 14882:2003 2.11
2112 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
2113 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2114 'namespace', 'new', 'operator', 'private', 'protected',
2115 'public', 'reinterpret_cast', 'static_cast', 'template',
2116 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2117 'using', 'virtual', 'wchar_t',
2118 # alternative representations
2119 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2120 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2121 # namespace pollution:
2122 polluted_words
= set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2123 name
= name
.translate(c_name_trans
)
2124 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
2125 | cpp_words | polluted_words
):
2130 eatspace
= '\033EATSPACE.'
2131 pointer_suffix
= ' *' + eatspace
2134 def genindent(count
):
2136 for _
in range(count
):
2144 def push_indent(indent_amount
=4):
2146 indent_level
+= indent_amount
2149 def pop_indent(indent_amount
=4):
2151 indent_level
-= indent_amount
2154 # Generate @code with @kwds interpolated.
2155 # Obey indent_level, and strip eatspace.
2156 def cgen(code
, **kwds
):
2159 indent
= genindent(indent_level
)
2160 # re.subn() lacks flags support before Python 2.7, use re.compile()
2161 raw
= re
.subn(re
.compile(r
'^(?!(#|$))', re
.MULTILINE
),
2164 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
2167 def mcgen(code
, **kwds
):
2170 return cgen(code
, **kwds
)
2173 def c_fname(filename
):
2174 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
)
2177 def guardstart(name
):
2183 name
=c_fname(name
).upper())
2189 #endif /* %(name)s */
2191 name
=c_fname(name
).upper())
2203 def gen_endif(ifcond
):
2205 for ifc
in reversed(ifcond
):
2207 #endif /* %(cond)s */
2212 def _wrap_ifcond(ifcond
, before
, after
):
2214 return after
# suppress empty #if ... #endif
2216 assert after
.startswith(before
)
2218 added
= after
[len(before
):]
2219 if added
[0] == '\n':
2222 out
+= gen_if(ifcond
)
2224 out
+= gen_endif(ifcond
)
2228 def gen_enum_lookup(name
, members
, prefix
=None):
2231 const QEnumLookup %(c_name)s_lookup = {
2232 .array = (const char *const[]) {
2234 c_name
=c_name(name
))
2236 ret
+= gen_if(m
.ifcond
)
2237 index
= c_enum_const(name
, m
.name
, prefix
)
2239 [%(index)s] = "%(name)s",
2241 index
=index
, name
=m
.name
)
2242 ret
+= gen_endif(m
.ifcond
)
2246 .size = %(max_index)s
2249 max_index
=c_enum_const(name
, '_MAX', prefix
))
2253 def gen_enum(name
, members
, prefix
=None):
2254 # append automatically generated _MAX value
2255 enum_members
= members
+ [QAPISchemaEnumMember('_MAX', None)]
2259 typedef enum %(c_name)s {
2261 c_name
=c_name(name
))
2263 for m
in enum_members
:
2264 ret
+= gen_if(m
.ifcond
)
2268 c_enum
=c_enum_const(name
, m
.name
, prefix
))
2269 ret
+= gen_endif(m
.ifcond
)
2274 c_name
=c_name(name
))
2278 #define %(c_name)s_str(val) \\
2279 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2281 extern const QEnumLookup %(c_name)s_lookup;
2283 c_name
=c_name(name
))
2287 def build_params(arg_type
, boxed
, extra
=None):
2292 ret
+= '%s arg' % arg_type
.c_param_type()
2295 assert not arg_type
.variants
2296 for memb
in arg_type
.members
:
2300 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
2301 ret
+= '%s %s' % (memb
.type.c_param_type(),
2305 return ret
if ret
else 'void'
2309 # Accumulate and write output
2312 class QAPIGen(object):
2314 def __init__(self
, fname
):
2319 def preamble_add(self
, text
):
2320 self
._preamble
+= text
2322 def add(self
, text
):
2325 def get_content(self
):
2326 return self
._top
() + self
._preamble
+ self
._body
+ self
._bottom
()
2334 def write(self
, output_dir
):
2335 pathname
= os
.path
.join(output_dir
, self
.fname
)
2336 dir = os
.path
.dirname(pathname
)
2340 except os
.error
as e
:
2341 if e
.errno
!= errno
.EEXIST
:
2343 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2344 if sys
.version_info
[0] >= 3:
2345 f
= open(fd
, 'r+', encoding
='utf-8')
2347 f
= os
.fdopen(fd
, 'r+')
2348 text
= self
.get_content()
2349 oldtext
= f
.read(len(text
) + 1)
2358 def ifcontext(ifcond
, *args
):
2359 """A 'with' statement context manager to wrap with start_if()/end_if()
2361 *args: any number of QAPIGenCCode
2365 with ifcontext(ifcond, self._genh, self._genc):
2366 modify self._genh and self._genc ...
2368 Is equivalent to calling::
2370 self._genh.start_if(ifcond)
2371 self._genc.start_if(ifcond)
2372 modify self._genh and self._genc ...
2377 arg
.start_if(ifcond
)
2383 class QAPIGenCCode(QAPIGen
):
2385 def __init__(self
, fname
):
2386 QAPIGen
.__init
__(self
, fname
)
2387 self
._start
_if
= None
2389 def start_if(self
, ifcond
):
2390 assert self
._start
_if
is None
2391 self
._start
_if
= (ifcond
, self
._body
, self
._preamble
)
2394 assert self
._start
_if
2396 self
._start
_if
= None
2398 def _wrap_ifcond(self
):
2399 self
._body
= _wrap_ifcond(self
._start
_if
[0],
2400 self
._start
_if
[1], self
._body
)
2401 self
._preamble
= _wrap_ifcond(self
._start
_if
[0],
2402 self
._start
_if
[2], self
._preamble
)
2404 def get_content(self
):
2405 assert self
._start
_if
is None
2406 return QAPIGen
.get_content(self
)
2409 class QAPIGenC(QAPIGenCCode
):
2411 def __init__(self
, fname
, blurb
, pydoc
):
2412 QAPIGenCCode
.__init
__(self
, fname
)
2414 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2419 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2426 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2427 * See the COPYING.LIB file in the top-level directory.
2431 blurb
=self
._blurb
, copyright
=self
._copyright
)
2436 /* Dummy declaration to prevent empty .o file */
2437 char qapi_dummy_%(name)s;
2439 name
=c_fname(self
.fname
))
2442 class QAPIGenH(QAPIGenC
):
2445 return QAPIGenC
._top
(self
) + guardstart(self
.fname
)
2448 return guardend(self
.fname
)
2451 class QAPIGenDoc(QAPIGen
):
2454 return (QAPIGen
._top
(self
)
2455 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2458 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2460 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2461 self
._prefix
= prefix
2463 self
._genc
= QAPIGenC(self
._prefix
+ self
._what
+ '.c',
2465 self
._genh
= QAPIGenH(self
._prefix
+ self
._what
+ '.h',
2468 def write(self
, output_dir
):
2469 self
._genc
.write(output_dir
)
2470 self
._genh
.write(output_dir
)
2473 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2475 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2476 self
._prefix
= prefix
2483 self
._main
_module
= None
2486 def _is_user_module(name
):
2487 return name
and not name
.startswith('./')
2490 def _is_builtin_module(name
):
2493 def _module_dirname(self
, what
, name
):
2494 if self
._is
_user
_module
(name
):
2495 return os
.path
.dirname(name
)
2498 def _module_basename(self
, what
, name
):
2499 ret
= '' if self
._is
_builtin
_module
(name
) else self
._prefix
2500 if self
._is
_user
_module
(name
):
2501 basename
= os
.path
.basename(name
)
2503 if name
!= self
._main
_module
:
2504 ret
+= '-' + os
.path
.splitext(basename
)[0]
2506 name
= name
[2:] if name
else 'builtin'
2507 ret
+= re
.sub(r
'-', '-' + name
+ '-', what
)
2510 def _module_filename(self
, what
, name
):
2511 return os
.path
.join(self
._module
_dirname
(what
, name
),
2512 self
._module
_basename
(what
, name
))
2514 def _add_module(self
, name
, blurb
):
2515 basename
= self
._module
_filename
(self
._what
, name
)
2516 genc
= QAPIGenC(basename
+ '.c', blurb
, self
._pydoc
)
2517 genh
= QAPIGenH(basename
+ '.h', blurb
, self
._pydoc
)
2518 self
._module
[name
] = (genc
, genh
)
2519 self
._set
_module
(name
)
2521 def _add_user_module(self
, name
, blurb
):
2522 assert self
._is
_user
_module
(name
)
2523 if self
._main
_module
is None:
2524 self
._main
_module
= name
2525 self
._add
_module
(name
, blurb
)
2527 def _add_system_module(self
, name
, blurb
):
2528 self
._add
_module
(name
and './' + name
, blurb
)
2530 def _set_module(self
, name
):
2531 self
._genc
, self
._genh
= self
._module
[name
]
2533 def write(self
, output_dir
, opt_builtins
=False):
2534 for name
in self
._module
:
2535 if self
._is
_builtin
_module
(name
) and not opt_builtins
:
2537 (genc
, genh
) = self
._module
[name
]
2538 genc
.write(output_dir
)
2539 genh
.write(output_dir
)
2541 def _begin_user_module(self
, name
):
2544 def visit_module(self
, name
):
2545 if name
in self
._module
:
2546 self
._set
_module
(name
)
2547 elif self
._is
_builtin
_module
(name
):
2548 # The built-in module has not been created. No code may
2553 self
._add
_user
_module
(name
, self
._blurb
)
2554 self
._begin
_user
_module
(name
)
2556 def visit_include(self
, name
, info
):
2557 relname
= os
.path
.relpath(self
._module
_filename
(self
._what
, name
),
2558 os
.path
.dirname(self
._genh
.fname
))
2559 self
._genh
.preamble_add(mcgen('''
2560 #include "%(relname)s.h"