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 if self
.fname
is None:
59 if self
.line
is not None:
60 ret
+= ':%d' % self
.line
65 return "%s: In %s '%s':\n" % (self
.fname
,
66 self
.defn_meta
, self
.defn_name
)
69 def include_path(self
):
73 ret
= 'In file included from %s:\n' % parent
.loc() + ret
74 parent
= parent
.parent
78 return self
.include_path() + self
.in_defn() + self
.loc()
81 class QAPIError(Exception):
82 def __init__(self
, info
, col
, msg
):
83 Exception.__init
__(self
)
90 if self
.col
is not None:
91 assert self
.info
.line
is not None
92 loc
+= ':%s' % self
.col
93 return loc
+ ': ' + self
.msg
96 class QAPIParseError(QAPIError
):
97 def __init__(self
, parser
, msg
):
99 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
101 col
= (col
+ 7) % 8 + 1
104 QAPIError
.__init
__(self
, parser
.info
, col
, msg
)
107 class QAPISemError(QAPIError
):
108 def __init__(self
, info
, msg
):
109 QAPIError
.__init
__(self
, info
, None, msg
)
112 class QAPIDoc(object):
114 A documentation comment block, either definition or free-form
116 Definition documentation blocks consist of
118 * a body section: one line naming the definition, followed by an
119 overview (any number of lines)
121 * argument sections: a description of each argument (for commands
122 and events) or member (for structs, unions and alternates)
124 * features sections: a description of each feature flag
126 * additional (non-argument) sections, possibly tagged
128 Free-form documentation blocks consist only of a body section.
131 class Section(object):
132 def __init__(self
, name
=None):
133 # optional section name (argument/member or section name)
135 # the list of lines for this section
138 def append(self
, line
):
139 self
.text
+= line
.rstrip() + '\n'
141 class ArgSection(Section
):
142 def __init__(self
, name
):
143 QAPIDoc
.Section
.__init
__(self
, name
)
146 def connect(self
, member
):
149 def __init__(self
, parser
, info
):
150 # self._parser is used to report errors with QAPIParseError. The
151 # resulting error position depends on the state of the parser.
152 # It happens to be the beginning of the comment. More or less
153 # servicable, but action at a distance.
154 self
._parser
= parser
157 self
.body
= QAPIDoc
.Section()
158 # dict mapping parameter name to ArgSection
159 self
.args
= OrderedDict()
160 self
.features
= OrderedDict()
163 # the current section
164 self
._section
= self
.body
165 self
._append
_line
= self
._append
_body
_line
167 def has_section(self
, name
):
168 """Return True if we have a section with this name."""
169 for i
in self
.sections
:
174 def append(self
, line
):
176 Parse a comment line and add it to the documentation.
178 The way that the line is dealt with depends on which part of
179 the documentation we're parsing right now:
180 * The body section: ._append_line is ._append_body_line
181 * An argument section: ._append_line is ._append_args_line
182 * A features section: ._append_line is ._append_features_line
183 * An additional section: ._append_line is ._append_various_line
187 self
._append
_freeform
(line
)
191 raise QAPIParseError(self
._parser
, "missing space after #")
193 self
._append
_line
(line
)
195 def end_comment(self
):
199 def _is_section_tag(name
):
200 return name
in ('Returns:', 'Since:',
201 # those are often singular or plural
203 'Example:', 'Examples:',
206 def _append_body_line(self
, line
):
208 Process a line of documentation text in the body section.
210 If this a symbol line and it is the section's first line, this
211 is a definition documentation block for that symbol.
213 If it's a definition documentation block, another symbol line
214 begins the argument section for the argument named by it, and
215 a section tag begins an additional section. Start that
216 section and append the line to it.
218 Else, append the line to the current section.
220 name
= line
.split(' ', 1)[0]
221 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
222 # recognized, and get silently treated as ordinary text
223 if not self
.symbol
and not self
.body
.text
and line
.startswith('@'):
224 if not line
.endswith(':'):
225 raise QAPIParseError(self
._parser
, "line should end with ':'")
226 self
.symbol
= line
[1:-1]
227 # FIXME invalid names other than the empty string aren't flagged
229 raise QAPIParseError(self
._parser
, "invalid name")
231 # This is a definition documentation block
232 if name
.startswith('@') and name
.endswith(':'):
233 self
._append
_line
= self
._append
_args
_line
234 self
._append
_args
_line
(line
)
235 elif line
== 'Features:':
236 self
._append
_line
= self
._append
_features
_line
237 elif self
._is
_section
_tag
(name
):
238 self
._append
_line
= self
._append
_various
_line
239 self
._append
_various
_line
(line
)
241 self
._append
_freeform
(line
.strip())
243 # This is a free-form documentation block
244 self
._append
_freeform
(line
.strip())
246 def _append_args_line(self
, line
):
248 Process a line of documentation text in an argument section.
250 A symbol line begins the next argument section, a section tag
251 section or a non-indented line after a blank line begins an
252 additional section. Start that section and append the line to
255 Else, append the line to the current section.
258 name
= line
.split(' ', 1)[0]
260 if name
.startswith('@') and name
.endswith(':'):
261 line
= line
[len(name
)+1:]
262 self
._start
_args
_section
(name
[1:-1])
263 elif self
._is
_section
_tag
(name
):
264 self
._append
_line
= self
._append
_various
_line
265 self
._append
_various
_line
(line
)
267 elif (self
._section
.text
.endswith('\n\n')
268 and line
and not line
[0].isspace()):
269 if line
== 'Features:':
270 self
._append
_line
= self
._append
_features
_line
272 self
._start
_section
()
273 self
._append
_line
= self
._append
_various
_line
274 self
._append
_various
_line
(line
)
277 self
._append
_freeform
(line
.strip())
279 def _append_features_line(self
, line
):
280 name
= line
.split(' ', 1)[0]
282 if name
.startswith('@') and name
.endswith(':'):
283 line
= line
[len(name
)+1:]
284 self
._start
_features
_section
(name
[1:-1])
285 elif self
._is
_section
_tag
(name
):
286 self
._append
_line
= self
._append
_various
_line
287 self
._append
_various
_line
(line
)
289 elif (self
._section
.text
.endswith('\n\n')
290 and line
and not line
[0].isspace()):
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_various_line(self
, line
):
300 Process a line of documentation text in an additional section.
302 A symbol line is an error.
304 A section tag begins an additional section. Start that
305 section and append the line to it.
307 Else, append the line to the current section.
309 name
= line
.split(' ', 1)[0]
311 if name
.startswith('@') and name
.endswith(':'):
312 raise QAPIParseError(self
._parser
,
313 "'%s' can't follow '%s' section"
314 % (name
, self
.sections
[0].name
))
315 elif self
._is
_section
_tag
(name
):
316 line
= line
[len(name
)+1:]
317 self
._start
_section
(name
[:-1])
319 if (not self
._section
.name
or
320 not self
._section
.name
.startswith('Example')):
323 self
._append
_freeform
(line
)
325 def _start_symbol_section(self
, symbols_dict
, name
):
326 # FIXME invalid names other than the empty string aren't flagged
328 raise QAPIParseError(self
._parser
, "invalid parameter name")
329 if name
in symbols_dict
:
330 raise QAPIParseError(self
._parser
,
331 "'%s' parameter name duplicated" % name
)
332 assert not self
.sections
334 self
._section
= QAPIDoc
.ArgSection(name
)
335 symbols_dict
[name
] = self
._section
337 def _start_args_section(self
, name
):
338 self
._start
_symbol
_section
(self
.args
, name
)
340 def _start_features_section(self
, name
):
341 self
._start
_symbol
_section
(self
.features
, name
)
343 def _start_section(self
, name
=None):
344 if name
in ('Returns', 'Since') and self
.has_section(name
):
345 raise QAPIParseError(self
._parser
,
346 "duplicated '%s' section" % name
)
348 self
._section
= QAPIDoc
.Section(name
)
349 self
.sections
.append(self
._section
)
351 def _end_section(self
):
353 text
= self
._section
.text
= self
._section
.text
.strip()
354 if self
._section
.name
and (not text
or text
.isspace()):
355 raise QAPIParseError(
357 "empty doc section '%s'" % self
._section
.name
)
360 def _append_freeform(self
, line
):
361 match
= re
.match(r
'(@\S+:)', line
)
363 raise QAPIParseError(self
._parser
,
364 "'%s' not allowed in free-form documentation"
366 self
._section
.append(line
)
368 def connect_member(self
, member
):
369 if member
.name
not in self
.args
:
370 # Undocumented TODO outlaw
371 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
372 self
.args
[member
.name
].connect(member
)
374 def check_expr(self
, expr
):
375 if self
.has_section('Returns') and 'command' not in expr
:
376 raise QAPISemError(self
.info
,
377 "'Returns:' is only valid for commands")
380 bogus
= [name
for name
, section
in self
.args
.items()
381 if not section
.member
]
385 "the following documented members are not in "
386 "the declaration: %s" % ", ".join(bogus
))
389 class QAPISchemaParser(object):
391 def __init__(self
, fname
, previously_included
=[], incl_info
=None):
392 previously_included
.append(os
.path
.abspath(fname
))
395 if sys
.version_info
[0] >= 3:
396 fp
= open(fname
, 'r', encoding
='utf-8')
398 fp
= open(fname
, 'r')
401 raise QAPISemError(incl_info
or QAPISourceInfo(None, None, None),
402 "can't read %s file '%s': %s"
403 % ("include" if incl_info
else "schema",
407 if self
.src
== '' or self
.src
[-1] != '\n':
410 self
.info
= QAPISourceInfo(fname
, 1, incl_info
)
417 while self
.tok
is not None:
420 self
.reject_expr_doc(cur_doc
)
421 cur_doc
= self
.get_doc(info
)
422 self
.docs
.append(cur_doc
)
425 expr
= self
.get_expr(False)
426 if 'include' in expr
:
427 self
.reject_expr_doc(cur_doc
)
429 raise QAPISemError(info
, "invalid 'include' directive")
430 include
= expr
['include']
431 if not isinstance(include
, str):
432 raise QAPISemError(info
,
433 "value of 'include' must be a string")
434 incl_fname
= os
.path
.join(os
.path
.dirname(fname
),
436 self
.exprs
.append({'expr': {'include': incl_fname
},
438 exprs_include
= self
._include
(include
, info
, incl_fname
,
441 self
.exprs
.extend(exprs_include
.exprs
)
442 self
.docs
.extend(exprs_include
.docs
)
443 elif "pragma" in expr
:
444 self
.reject_expr_doc(cur_doc
)
446 raise QAPISemError(info
, "invalid 'pragma' directive")
447 pragma
= expr
['pragma']
448 if not isinstance(pragma
, dict):
450 info
, "value of 'pragma' must be an object")
451 for name
, value
in pragma
.items():
452 self
._pragma
(name
, value
, info
)
454 expr_elem
= {'expr': expr
,
457 if not cur_doc
.symbol
:
459 cur_doc
.info
, "definition documentation required")
460 expr_elem
['doc'] = cur_doc
461 self
.exprs
.append(expr_elem
)
463 self
.reject_expr_doc(cur_doc
)
466 def reject_expr_doc(doc
):
467 if doc
and doc
.symbol
:
470 "documentation for '%s' is not followed by the definition"
473 def _include(self
, include
, info
, incl_fname
, previously_included
):
474 incl_abs_fname
= os
.path
.abspath(incl_fname
)
475 # catch inclusion cycle
478 if incl_abs_fname
== os
.path
.abspath(inf
.fname
):
479 raise QAPISemError(info
, "inclusion loop for %s" % include
)
482 # skip multiple include of the same file
483 if incl_abs_fname
in previously_included
:
486 return QAPISchemaParser(incl_fname
, previously_included
, info
)
488 def _pragma(self
, name
, value
, info
):
489 global doc_required
, returns_whitelist
, name_case_whitelist
490 if name
== 'doc-required':
491 if not isinstance(value
, bool):
492 raise QAPISemError(info
,
493 "pragma 'doc-required' must be boolean")
495 elif name
== 'returns-whitelist':
496 if (not isinstance(value
, list)
497 or any([not isinstance(elt
, str) for elt
in value
])):
500 "pragma returns-whitelist must be a list of strings")
501 returns_whitelist
= value
502 elif name
== 'name-case-whitelist':
503 if (not isinstance(value
, list)
504 or any([not isinstance(elt
, str) for elt
in value
])):
507 "pragma name-case-whitelist must be a list of strings")
508 name_case_whitelist
= value
510 raise QAPISemError(info
, "unknown pragma '%s'" % name
)
512 def accept(self
, skip_comment
=True):
514 self
.tok
= self
.src
[self
.cursor
]
515 self
.pos
= self
.cursor
520 if self
.src
[self
.cursor
] == '#':
521 # Start of doc comment
523 self
.cursor
= self
.src
.find('\n', self
.cursor
)
525 self
.val
= self
.src
[self
.pos
:self
.cursor
]
527 elif self
.tok
in '{}:,[]':
529 elif self
.tok
== "'":
530 # Note: we accept only printable ASCII
534 ch
= self
.src
[self
.cursor
]
537 raise QAPIParseError(self
, "missing terminating \"'\"")
539 # Note: we recognize only \\ because we have
540 # no use for funny characters in strings
542 raise QAPIParseError(self
,
543 "unknown escape \\%s" % ch
)
551 if ord(ch
) < 32 or ord(ch
) >= 127:
552 raise QAPIParseError(
553 self
, "funny character in string")
555 elif self
.src
.startswith('true', self
.pos
):
559 elif self
.src
.startswith('false', self
.pos
):
563 elif self
.tok
== '\n':
564 if self
.cursor
== len(self
.src
):
567 self
.info
= self
.info
.next_line()
568 self
.line_pos
= self
.cursor
569 elif not self
.tok
.isspace():
570 # Show up to next structural, whitespace or quote
572 match
= re
.match('[^[\\]{}:,\\s\'"]+',
573 self
.src
[self
.cursor
-1:])
574 raise QAPIParseError(self
, "stray '%s'" % match
.group(0))
576 def get_members(self
):
582 raise QAPIParseError(self
, "expected string or '}'")
587 raise QAPIParseError(self
, "expected ':'")
590 raise QAPIParseError(self
, "duplicate key '%s'" % key
)
591 expr
[key
] = self
.get_expr(True)
596 raise QAPIParseError(self
, "expected ',' or '}'")
599 raise QAPIParseError(self
, "expected string")
601 def get_values(self
):
606 if self
.tok
not in "{['tfn":
607 raise QAPIParseError(
608 self
, "expected '{', '[', ']', string, boolean or 'null'")
610 expr
.append(self
.get_expr(True))
615 raise QAPIParseError(self
, "expected ',' or ']'")
618 def get_expr(self
, nested
):
619 if self
.tok
!= '{' and not nested
:
620 raise QAPIParseError(self
, "expected '{'")
623 expr
= self
.get_members()
624 elif self
.tok
== '[':
626 expr
= self
.get_values()
627 elif self
.tok
in "'tfn":
631 raise QAPIParseError(
632 self
, "expected '{', '[', string, boolean or 'null'")
635 def get_doc(self
, info
):
637 raise QAPIParseError(
638 self
, "junk after '##' at start of documentation comment")
640 doc
= QAPIDoc(self
, info
)
642 while self
.tok
== '#':
643 if self
.val
.startswith('##'):
646 raise QAPIParseError(
648 "junk after '##' at end of documentation comment")
656 raise QAPIParseError(self
, "documentation comment must end with '##'")
660 # Check (context-free) schema expression structure
663 # Names must be letters, numbers, -, and _. They must start with letter,
664 # except for downstream extensions which must start with __RFQDN_.
665 # Dots are only valid in the downstream extension prefix.
666 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
667 '[a-zA-Z][a-zA-Z0-9_-]*$')
670 def check_name_is_str(name
, info
, source
):
671 if not isinstance(name
, str):
672 raise QAPISemError(info
, "%s requires a string name" % source
)
675 def check_name_str(name
, info
, source
,
676 allow_optional
=False, enum_member
=False,
681 if allow_optional
and name
.startswith('*'):
682 membername
= name
[1:]
683 # Enum members can start with a digit, because the generated C
684 # code always prefixes it with the enum name
685 if enum_member
and membername
[0].isdigit():
686 membername
= 'D' + membername
687 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
688 # and 'q_obj_*' implicit type names.
689 if not valid_name
.match(membername
) or \
690 c_name(membername
, False).startswith('q_'):
691 raise QAPISemError(info
, "%s has an invalid name" % source
)
692 if not permit_upper
and name
.lower() != name
:
694 info
, "%s uses uppercase in name" % source
)
695 assert not membername
.startswith('*')
698 def check_defn_name_str(name
, info
, meta
):
699 check_name_str(name
, info
, meta
, permit_upper
=True)
700 if name
.endswith('Kind') or name
.endswith('List'):
702 info
, "%s name should not end in '%s'" % (meta
, name
[-4:]))
705 def check_if(expr
, info
, source
):
707 def check_if_str(ifcond
, info
):
708 if not isinstance(ifcond
, str):
711 "'if' condition of %s must be a string or a list of strings"
713 if ifcond
.strip() == '':
716 "'if' condition '%s' of %s makes no sense"
719 ifcond
= expr
.get('if')
722 if isinstance(ifcond
, list):
725 info
, "'if' condition [] of %s is useless" % source
)
727 check_if_str(elt
, info
)
729 check_if_str(ifcond
, info
)
732 def check_type(value
, info
, source
,
733 allow_array
=False, allow_dict
=False):
738 if isinstance(value
, list):
740 raise QAPISemError(info
, "%s cannot be an array" % source
)
741 if len(value
) != 1 or not isinstance(value
[0], str):
742 raise QAPISemError(info
,
743 "%s: array type must contain single type name" %
748 if isinstance(value
, str):
754 raise QAPISemError(info
, "%s should be a type name" % source
)
756 if not isinstance(value
, OrderedDict
):
757 raise QAPISemError(info
,
758 "%s should be an object or type name" % source
)
760 permit_upper
= allow_dict
in name_case_whitelist
762 # value is a dictionary, check that each member is okay
763 for (key
, arg
) in value
.items():
764 key_source
= "%s member '%s'" % (source
, key
)
765 check_name_str(key
, info
, key_source
,
766 allow_optional
=True, permit_upper
=permit_upper
)
767 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
768 raise QAPISemError(info
, "%s uses reserved name" % key_source
)
769 check_keys(arg
, info
, key_source
, ['type'], ['if'])
770 check_if(arg
, info
, key_source
)
772 check_type(arg
['type'], info
, key_source
, allow_array
=True)
775 def check_command(expr
, info
):
776 args
= expr
.get('data')
777 rets
= expr
.get('returns')
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
)
783 check_type(rets
, info
, "'returns'", allow_array
=True)
786 def check_event(expr
, info
):
787 args
= expr
.get('data')
788 boxed
= expr
.get('boxed', False)
790 if boxed
and args
is None:
791 raise QAPISemError(info
, "'boxed': true requires 'data'")
792 check_type(args
, info
, "'data'", allow_dict
=not boxed
)
795 def check_union(expr
, info
):
797 base
= expr
.get('base')
798 discriminator
= expr
.get('discriminator')
799 members
= expr
['data']
801 if discriminator
is None: # simple union
803 raise QAPISemError(info
, "'base' requires 'discriminator'")
805 check_type(base
, info
, "'base'", allow_dict
=name
)
807 raise QAPISemError(info
, "'discriminator' requires 'base'")
808 check_name_is_str(discriminator
, info
, "'discriminator'")
810 for (key
, value
) in members
.items():
811 source
= "'data' member '%s'" % key
812 check_name_str(key
, info
, source
)
813 check_keys(value
, info
, source
, ['type'], ['if'])
814 check_if(value
, info
, source
)
816 check_type(value
['type'], info
, source
, allow_array
=not base
)
819 def check_alternate(expr
, info
):
820 members
= expr
['data']
822 if len(members
) == 0:
823 raise QAPISemError(info
, "'data' must not be empty")
824 for (key
, value
) in members
.items():
825 source
= "'data' member '%s'" % key
826 check_name_str(key
, info
, source
)
827 check_keys(value
, info
, source
, ['type'], ['if'])
828 check_if(value
, info
, source
)
830 check_type(value
['type'], info
, source
)
833 def check_enum(expr
, info
):
835 members
= expr
['data']
836 prefix
= expr
.get('prefix')
838 if not isinstance(members
, list):
839 raise QAPISemError(info
, "'data' must be an array")
840 if prefix
is not None and not isinstance(prefix
, str):
841 raise QAPISemError(info
, "'prefix' must be a string")
843 permit_upper
= name
in name_case_whitelist
845 for member
in members
:
846 source
= "'data' member"
847 check_keys(member
, info
, source
, ['name'], ['if'])
848 check_name_is_str(member
['name'], info
, source
)
849 source
= "%s '%s'" % (source
, member
['name'])
850 check_name_str(member
['name'], info
, source
,
851 enum_member
=True, permit_upper
=permit_upper
)
852 check_if(member
, info
, source
)
856 def check_struct(expr
, info
):
857 name
= expr
['struct']
858 members
= expr
['data']
859 features
= expr
.get('features')
861 check_type(members
, info
, "'data'", allow_dict
=name
)
862 check_type(expr
.get('base'), info
, "'base'")
865 if not isinstance(features
, list):
866 raise QAPISemError(info
, "'features' must be an array")
868 source
= "'features' member"
869 assert isinstance(f
, dict)
870 check_keys(f
, info
, source
, ['name'], ['if'])
871 check_name_is_str(f
['name'], info
, source
)
872 source
= "%s '%s'" % (source
, f
['name'])
873 check_name_str(f
['name'], info
, source
)
874 check_if(f
, info
, source
)
878 def check_keys(value
, info
, source
, required
, optional
):
881 return ', '.join("'" + e
+ "'" for e
in sorted(elems
))
883 missing
= set(required
) - set(value
)
888 % (source
, 's' if len(missing
) > 1 else '',
890 allowed
= set(required
+ optional
)
891 unknown
= set(value
) - allowed
895 "%s has unknown key%s %s\nValid keys are %s."
896 % (source
, 's' if len(unknown
) > 1 else '',
897 pprint(unknown
), pprint(allowed
)))
900 def check_flags(expr
, info
):
901 for key
in ['gen', 'success-response']:
902 if key
in expr
and expr
[key
] is not False:
904 info
, "flag '%s' may only use false value" % key
)
905 for key
in ['boxed', 'allow-oob', 'allow-preconfig']:
906 if key
in expr
and expr
[key
] is not True:
908 info
, "flag '%s' may only use true value" % key
)
911 def normalize_enum(expr
):
912 if isinstance(expr
['data'], list):
913 expr
['data'] = [m
if isinstance(m
, dict) else {'name': m
}
914 for m
in expr
['data']]
917 def normalize_members(members
):
918 if isinstance(members
, OrderedDict
):
919 for key
, arg
in members
.items():
920 if isinstance(arg
, dict):
922 members
[key
] = {'type': arg
}
925 def normalize_features(features
):
926 if isinstance(features
, list):
927 features
[:] = [f
if isinstance(f
, dict) else {'name': f
}
931 def normalize_if(expr
):
932 ifcond
= expr
.get('if')
933 if isinstance(ifcond
, str):
934 expr
['if'] = [ifcond
]
937 def check_exprs(exprs
):
938 for expr_elem
in exprs
:
939 expr
= expr_elem
['expr']
940 info
= expr_elem
['info']
941 doc
= expr_elem
.get('doc')
943 if 'include' in expr
:
948 elif 'union' in expr
:
950 elif 'alternate' in expr
:
952 elif 'struct' in expr
:
954 elif 'command' in expr
:
956 elif 'event' in expr
:
959 raise QAPISemError(info
, "expression is missing metatype")
962 check_name_is_str(name
, info
, "'%s'" % meta
)
963 info
.set_defn(meta
, name
)
964 check_defn_name_str(name
, info
, meta
)
967 if doc
.symbol
!= name
:
969 info
, "documentation comment is for '%s'" % doc
.symbol
)
972 raise QAPISemError(info
,
973 "documentation comment required")
976 check_keys(expr
, info
, meta
,
977 ['enum', 'data'], ['if', 'prefix'])
979 check_enum(expr
, info
)
980 elif meta
== 'union':
981 check_keys(expr
, info
, meta
,
983 ['base', 'discriminator', 'if'])
984 normalize_members(expr
.get('base'))
985 normalize_members(expr
['data'])
986 check_union(expr
, info
)
987 elif meta
== 'alternate':
988 check_keys(expr
, info
, meta
,
989 ['alternate', 'data'], ['if'])
990 normalize_members(expr
['data'])
991 check_alternate(expr
, info
)
992 elif meta
== 'struct':
993 check_keys(expr
, info
, meta
,
994 ['struct', 'data'], ['base', 'if', 'features'])
995 normalize_members(expr
['data'])
996 normalize_features(expr
.get('features'))
997 check_struct(expr
, info
)
998 elif meta
== 'command':
999 check_keys(expr
, info
, meta
,
1001 ['data', 'returns', 'boxed', 'if',
1002 'gen', 'success-response', 'allow-oob',
1004 normalize_members(expr
.get('data'))
1005 check_command(expr
, info
)
1006 elif meta
== 'event':
1007 check_keys(expr
, info
, meta
,
1008 ['event'], ['data', 'boxed', 'if'])
1009 normalize_members(expr
.get('data'))
1010 check_event(expr
, info
)
1012 assert False, 'unexpected meta type'
1015 check_if(expr
, info
, meta
)
1016 check_flags(expr
, info
)
1022 # Schema compiler frontend
1023 # TODO catching name collisions in generated code would be nice
1026 class QAPISchemaEntity(object):
1029 def __init__(self
, name
, info
, doc
, ifcond
=None):
1030 assert name
is None or isinstance(name
, str)
1033 # For explicitly defined entities, info points to the (explicit)
1034 # definition. For builtins (and their arrays), info is None.
1035 # For implicitly defined entities, info points to a place that
1036 # triggered the implicit definition (there may be more than one
1040 self
._ifcond
= ifcond
or []
1041 self
._checked
= False
1044 return c_name(self
.name
)
1046 def check(self
, schema
):
1047 assert not self
._checked
1049 self
._module
= os
.path
.relpath(self
.info
.fname
,
1050 os
.path
.dirname(schema
.fname
))
1051 self
._checked
= True
1055 assert self
._checked
1060 assert self
._checked
1063 def is_implicit(self
):
1064 return not self
.info
1066 def visit(self
, visitor
):
1067 assert self
._checked
1071 return "%s '%s'" % (self
.meta
, self
.name
)
1074 class QAPISchemaVisitor(object):
1075 def visit_begin(self
, schema
):
1078 def visit_end(self
):
1081 def visit_module(self
, fname
):
1084 def visit_needed(self
, entity
):
1085 # Default to visiting everything
1088 def visit_include(self
, fname
, info
):
1091 def visit_builtin_type(self
, name
, info
, json_type
):
1094 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
1097 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
1100 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
,
1104 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
1108 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
1111 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
1112 success_response
, boxed
, allow_oob
, allow_preconfig
):
1115 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
1119 class QAPISchemaInclude(QAPISchemaEntity
):
1121 def __init__(self
, fname
, info
):
1122 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1125 def visit(self
, visitor
):
1126 QAPISchemaEntity
.visit(self
, visitor
)
1127 visitor
.visit_include(self
.fname
, self
.info
)
1130 class QAPISchemaType(QAPISchemaEntity
):
1131 # Return the C type for common use.
1132 # For the types we commonly box, this is a pointer type.
1136 # Return the C type to be used in a parameter list.
1137 def c_param_type(self
):
1138 return self
.c_type()
1140 # Return the C type to be used where we suppress boxing.
1141 def c_unboxed_type(self
):
1142 return self
.c_type()
1144 def json_type(self
):
1147 def alternate_qtype(self
):
1149 'null': 'QTYPE_QNULL',
1150 'string': 'QTYPE_QSTRING',
1151 'number': 'QTYPE_QNUM',
1152 'int': 'QTYPE_QNUM',
1153 'boolean': 'QTYPE_QBOOL',
1154 'object': 'QTYPE_QDICT'
1156 return json2qtype
.get(self
.json_type())
1159 if self
.is_implicit():
1165 return "%s type '%s'" % (self
.meta
, self
.name
)
1168 class QAPISchemaBuiltinType(QAPISchemaType
):
1171 def __init__(self
, name
, json_type
, c_type
):
1172 QAPISchemaType
.__init
__(self
, name
, None, None)
1173 assert not c_type
or isinstance(c_type
, str)
1174 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1176 self
._json
_type
_name
= json_type
1177 self
._c
_type
_name
= c_type
1183 return self
._c
_type
_name
1185 def c_param_type(self
):
1186 if self
.name
== 'str':
1187 return 'const ' + self
._c
_type
_name
1188 return self
._c
_type
_name
1190 def json_type(self
):
1191 return self
._json
_type
_name
1194 return self
.json_type()
1196 def visit(self
, visitor
):
1197 QAPISchemaType
.visit(self
, visitor
)
1198 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1201 class QAPISchemaEnumType(QAPISchemaType
):
1204 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
1205 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1207 assert isinstance(m
, QAPISchemaEnumMember
)
1208 m
.set_defined_in(name
)
1209 assert prefix
is None or isinstance(prefix
, str)
1210 self
.members
= members
1211 self
.prefix
= prefix
1213 def check(self
, schema
):
1214 QAPISchemaType
.check(self
, schema
)
1216 for m
in self
.members
:
1217 m
.check_clash(self
.info
, seen
)
1219 self
.doc
.connect_member(m
)
1221 def is_implicit(self
):
1222 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1223 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1226 return c_name(self
.name
)
1228 def member_names(self
):
1229 return [m
.name
for m
in self
.members
]
1231 def json_type(self
):
1234 def visit(self
, visitor
):
1235 QAPISchemaType
.visit(self
, visitor
)
1236 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
1237 self
.members
, self
.prefix
)
1240 class QAPISchemaArrayType(QAPISchemaType
):
1243 def __init__(self
, name
, info
, element_type
):
1244 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
1245 assert isinstance(element_type
, str)
1246 self
._element
_type
_name
= element_type
1247 self
.element_type
= None
1249 def check(self
, schema
):
1250 QAPISchemaType
.check(self
, schema
)
1251 self
.element_type
= schema
.resolve_type(
1252 self
._element
_type
_name
, self
.info
,
1253 self
.info
and self
.info
.defn_meta
)
1254 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
1258 assert self
._checked
1259 return self
.element_type
.ifcond
1263 assert self
._checked
1264 return self
.element_type
.module
1266 def is_implicit(self
):
1270 return c_name(self
.name
) + pointer_suffix
1272 def json_type(self
):
1276 elt_doc_type
= self
.element_type
.doc_type()
1277 if not elt_doc_type
:
1279 return 'array of ' + elt_doc_type
1281 def visit(self
, visitor
):
1282 QAPISchemaType
.visit(self
, visitor
)
1283 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
1288 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
1291 class QAPISchemaObjectType(QAPISchemaType
):
1292 def __init__(self
, name
, info
, doc
, ifcond
,
1293 base
, local_members
, variants
, features
):
1294 # struct has local_members, optional base, and no variants
1295 # flat union has base, variants, and no local_members
1296 # simple union has local_members, variants, and no base
1297 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1298 self
.meta
= 'union' if variants
else 'struct'
1299 assert base
is None or isinstance(base
, str)
1300 for m
in local_members
:
1301 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1302 m
.set_defined_in(name
)
1303 if variants
is not None:
1304 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1305 variants
.set_defined_in(name
)
1307 assert isinstance(f
, QAPISchemaFeature
)
1308 f
.set_defined_in(name
)
1309 self
._base
_name
= base
1311 self
.local_members
= local_members
1312 self
.variants
= variants
1314 self
.features
= features
1316 def check(self
, schema
):
1317 # This calls another type T's .check() exactly when the C
1318 # struct emitted by gen_object() contains that T's C struct
1319 # (pointers don't count).
1320 if self
.members
is not None:
1321 # A previous .check() completed: nothing to do
1324 # Recursed: C struct contains itself
1325 raise QAPISemError(self
.info
,
1326 "object %s contains itself" % self
.name
)
1328 QAPISchemaType
.check(self
, schema
)
1329 assert self
._checked
and self
.members
is None
1331 seen
= OrderedDict()
1333 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
1335 if (not isinstance(self
.base
, QAPISchemaObjectType
)
1336 or self
.base
.variants
):
1339 "'base' requires a struct type, %s isn't"
1340 % self
.base
.describe())
1341 self
.base
.check(schema
)
1342 self
.base
.check_clash(self
.info
, seen
)
1343 for m
in self
.local_members
:
1345 m
.check_clash(self
.info
, seen
)
1347 self
.doc
.connect_member(m
)
1348 members
= seen
.values()
1351 self
.variants
.check(schema
, seen
)
1352 self
.variants
.check_clash(self
.info
, seen
)
1354 # Features are in a name space separate from members
1356 for f
in self
.features
:
1357 f
.check_clash(self
.info
, seen
)
1362 self
.members
= members
# mark completed
1364 # Check that the members of this type do not cause duplicate JSON members,
1365 # and update seen to track the members seen so far. Report any errors
1366 # on behalf of info, which is not necessarily self.info
1367 def check_clash(self
, info
, seen
):
1368 assert self
._checked
1369 assert not self
.variants
# not implemented
1370 for m
in self
.members
:
1371 m
.check_clash(info
, seen
)
1375 assert self
._checked
1376 if isinstance(self
._ifcond
, QAPISchemaType
):
1377 # Simple union wrapper type inherits from wrapped type;
1378 # see _make_implicit_object_type()
1379 return self
._ifcond
.ifcond
1382 def is_implicit(self
):
1383 # See QAPISchema._make_implicit_object_type(), as well as
1384 # _def_predefineds()
1385 return self
.name
.startswith('q_')
1388 assert self
.members
is not None
1389 return not self
.members
and not self
.variants
1392 assert self
.name
!= 'q_empty'
1393 return QAPISchemaType
.c_name(self
)
1396 assert not self
.is_implicit()
1397 return c_name(self
.name
) + pointer_suffix
1399 def c_unboxed_type(self
):
1400 return c_name(self
.name
)
1402 def json_type(self
):
1405 def visit(self
, visitor
):
1406 QAPISchemaType
.visit(self
, visitor
)
1407 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
1408 self
.base
, self
.local_members
, self
.variants
,
1410 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
1411 self
.members
, self
.variants
,
1415 class QAPISchemaMember(object):
1416 """ Represents object members, enum members and features """
1419 def __init__(self
, name
, info
, ifcond
=None):
1420 assert isinstance(name
, str)
1423 self
.ifcond
= ifcond
or []
1424 self
.defined_in
= None
1426 def set_defined_in(self
, name
):
1427 assert not self
.defined_in
1428 self
.defined_in
= name
1430 def check_clash(self
, info
, seen
):
1431 cname
= c_name(self
.name
)
1435 "%s collides with %s"
1436 % (self
.describe(info
), seen
[cname
].describe(info
)))
1439 def describe(self
, info
):
1441 defined_in
= self
.defined_in
1444 if defined_in
.startswith('q_obj_'):
1445 # See QAPISchema._make_implicit_object_type() - reverse the
1446 # mapping there to create a nice human-readable description
1447 defined_in
= defined_in
[6:]
1448 if defined_in
.endswith('-arg'):
1449 # Implicit type created for a command's dict 'data'
1450 assert role
== 'member'
1452 elif defined_in
.endswith('-base'):
1453 # Implicit type created for a flat union's dict 'base'
1454 role
= 'base ' + role
1456 # Implicit type created for a simple union's branch
1457 assert defined_in
.endswith('-wrapper')
1458 # Unreachable and not implemented
1460 elif defined_in
.endswith('Kind'):
1461 # See QAPISchema._make_implicit_enum_type()
1462 # Implicit enum created for simple union's branches
1463 assert role
== 'value'
1465 elif defined_in
!= info
.defn_name
:
1466 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
1467 return "%s '%s'" % (role
, self
.name
)
1470 class QAPISchemaEnumMember(QAPISchemaMember
):
1474 class QAPISchemaFeature(QAPISchemaMember
):
1478 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1479 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None):
1480 QAPISchemaMember
.__init
__(self
, name
, info
, ifcond
)
1481 assert isinstance(typ
, str)
1482 assert isinstance(optional
, bool)
1483 self
._type
_name
= typ
1485 self
.optional
= optional
1487 def check(self
, schema
):
1488 assert self
.defined_in
1489 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
1493 class QAPISchemaObjectTypeVariants(object):
1494 def __init__(self
, tag_name
, info
, tag_member
, variants
):
1495 # Flat unions pass tag_name but not tag_member.
1496 # Simple unions and alternates pass tag_member but not tag_name.
1497 # After check(), tag_member is always set, and tag_name remains
1498 # a reliable witness of being used by a flat union.
1499 assert bool(tag_member
) != bool(tag_name
)
1500 assert (isinstance(tag_name
, str) or
1501 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1503 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1504 self
._tag
_name
= tag_name
1506 self
.tag_member
= tag_member
1507 self
.variants
= variants
1509 def set_defined_in(self
, name
):
1510 for v
in self
.variants
:
1511 v
.set_defined_in(name
)
1513 def check(self
, schema
, seen
):
1514 if not self
.tag_member
: # flat union
1515 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
1517 # Pointing to the base type when not implicit would be
1518 # nice, but we don't know it here
1519 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
1522 "discriminator '%s' is not a member of %s"
1523 % (self
._tag
_name
, base
))
1525 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
1527 if not base_type
.is_implicit():
1528 base
= "base type '%s'" % self
.tag_member
.defined_in
1529 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
1532 "discriminator member '%s' of %s must be of enum type"
1533 % (self
._tag
_name
, base
))
1534 if self
.tag_member
.optional
:
1537 "discriminator member '%s' of %s must not be optional"
1538 % (self
._tag
_name
, base
))
1539 if self
.tag_member
.ifcond
:
1542 "discriminator member '%s' of %s must not be conditional"
1543 % (self
._tag
_name
, base
))
1544 else: # simple union
1545 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1546 assert not self
.tag_member
.optional
1547 assert self
.tag_member
.ifcond
== []
1548 if self
._tag
_name
: # flat union
1549 # branches that are not explicitly covered get an empty type
1550 cases
= set([v
.name
for v
in self
.variants
])
1551 for m
in self
.tag_member
.type.members
:
1552 if m
.name
not in cases
:
1553 v
= QAPISchemaObjectTypeVariant(m
.name
, self
.info
,
1554 'q_empty', m
.ifcond
)
1555 v
.set_defined_in(self
.tag_member
.defined_in
)
1556 self
.variants
.append(v
)
1557 if not self
.variants
:
1558 raise QAPISemError(self
.info
, "union has no branches")
1559 for v
in self
.variants
:
1561 # Union names must match enum values; alternate names are
1562 # checked separately. Use 'seen' to tell the two apart.
1564 if v
.name
not in self
.tag_member
.type.member_names():
1567 "branch '%s' is not a value of %s"
1568 % (v
.name
, self
.tag_member
.type.describe()))
1569 if (not isinstance(v
.type, QAPISchemaObjectType
)
1570 or v
.type.variants
):
1574 % (v
.describe(self
.info
), v
.type.describe()))
1575 v
.type.check(schema
)
1577 def check_clash(self
, info
, seen
):
1578 for v
in self
.variants
:
1579 # Reset seen map for each variant, since qapi names from one
1580 # branch do not affect another branch
1581 v
.type.check_clash(info
, dict(seen
))
1584 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1587 def __init__(self
, name
, info
, typ
, ifcond
=None):
1588 QAPISchemaObjectTypeMember
.__init
__(self
, name
, info
, typ
,
1592 class QAPISchemaAlternateType(QAPISchemaType
):
1595 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
1596 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1597 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1598 assert variants
.tag_member
1599 variants
.set_defined_in(name
)
1600 variants
.tag_member
.set_defined_in(self
.name
)
1601 self
.variants
= variants
1603 def check(self
, schema
):
1604 QAPISchemaType
.check(self
, schema
)
1605 self
.variants
.tag_member
.check(schema
)
1606 # Not calling self.variants.check_clash(), because there's nothing
1608 self
.variants
.check(schema
, {})
1609 # Alternate branch names have no relation to the tag enum values;
1610 # so we have to check for potential name collisions ourselves.
1613 for v
in self
.variants
.variants
:
1614 v
.check_clash(self
.info
, seen
)
1615 qtype
= v
.type.alternate_qtype()
1620 % (v
.describe(self
.info
), v
.type.describe()))
1621 conflicting
= set([qtype
])
1622 if qtype
== 'QTYPE_QSTRING':
1623 if isinstance(v
.type, QAPISchemaEnumType
):
1624 for m
in v
.type.members
:
1625 if m
.name
in ['on', 'off']:
1626 conflicting
.add('QTYPE_QBOOL')
1627 if re
.match(r
'[-+0-9.]', m
.name
):
1628 # lazy, could be tightened
1629 conflicting
.add('QTYPE_QNUM')
1631 conflicting
.add('QTYPE_QNUM')
1632 conflicting
.add('QTYPE_QBOOL')
1633 for qt
in conflicting
:
1634 if qt
in types_seen
:
1637 "%s can't be distinguished from '%s'"
1638 % (v
.describe(self
.info
), types_seen
[qt
]))
1639 types_seen
[qt
] = v
.name
1641 self
.doc
.connect_member(v
)
1646 return c_name(self
.name
) + pointer_suffix
1648 def json_type(self
):
1651 def visit(self
, visitor
):
1652 QAPISchemaType
.visit(self
, visitor
)
1653 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
1657 class QAPISchemaCommand(QAPISchemaEntity
):
1660 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
1661 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
1662 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1663 assert not arg_type
or isinstance(arg_type
, str)
1664 assert not ret_type
or isinstance(ret_type
, str)
1665 self
._arg
_type
_name
= arg_type
1666 self
.arg_type
= None
1667 self
._ret
_type
_name
= ret_type
1668 self
.ret_type
= None
1670 self
.success_response
= success_response
1672 self
.allow_oob
= allow_oob
1673 self
.allow_preconfig
= allow_preconfig
1675 def check(self
, schema
):
1676 QAPISchemaEntity
.check(self
, schema
)
1677 if self
._arg
_type
_name
:
1678 self
.arg_type
= schema
.resolve_type(
1679 self
._arg
_type
_name
, self
.info
, "command's 'data'")
1680 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
1683 "command's 'data' cannot take %s"
1684 % self
.arg_type
.describe())
1685 if self
.arg_type
.variants
and not self
.boxed
:
1688 "command's 'data' can take %s only with 'boxed': true"
1689 % self
.arg_type
.describe())
1690 if self
._ret
_type
_name
:
1691 self
.ret_type
= schema
.resolve_type(
1692 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
1693 if self
.name
not in returns_whitelist
:
1694 if not (isinstance(self
.ret_type
, QAPISchemaObjectType
)
1695 or (isinstance(self
.ret_type
, QAPISchemaArrayType
)
1696 and isinstance(self
.ret_type
.element_type
,
1697 QAPISchemaObjectType
))):
1700 "command's 'returns' cannot take %s"
1701 % self
.ret_type
.describe())
1703 def visit(self
, visitor
):
1704 QAPISchemaEntity
.visit(self
, visitor
)
1705 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
1706 self
.arg_type
, self
.ret_type
,
1707 self
.gen
, self
.success_response
,
1708 self
.boxed
, self
.allow_oob
,
1709 self
.allow_preconfig
)
1712 class QAPISchemaEvent(QAPISchemaEntity
):
1715 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
1716 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1717 assert not arg_type
or isinstance(arg_type
, str)
1718 self
._arg
_type
_name
= arg_type
1719 self
.arg_type
= None
1722 def check(self
, schema
):
1723 QAPISchemaEntity
.check(self
, schema
)
1724 if self
._arg
_type
_name
:
1725 self
.arg_type
= schema
.resolve_type(
1726 self
._arg
_type
_name
, self
.info
, "event's 'data'")
1727 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
1730 "event's 'data' cannot take %s"
1731 % self
.arg_type
.describe())
1732 if self
.arg_type
.variants
and not self
.boxed
:
1735 "event's 'data' can take %s only with 'boxed': true"
1736 % self
.arg_type
.describe())
1738 def visit(self
, visitor
):
1739 QAPISchemaEntity
.visit(self
, visitor
)
1740 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
1741 self
.arg_type
, self
.boxed
)
1744 class QAPISchema(object):
1745 def __init__(self
, fname
):
1747 parser
= QAPISchemaParser(fname
)
1748 exprs
= check_exprs(parser
.exprs
)
1749 self
.docs
= parser
.docs
1750 self
._entity
_list
= []
1751 self
._entity
_dict
= {}
1752 self
._predefining
= True
1753 self
._def
_predefineds
()
1754 self
._predefining
= False
1755 self
._def
_exprs
(exprs
)
1758 def _def_entity(self
, ent
):
1759 # Only the predefined types are allowed to not have info
1760 assert ent
.info
or self
._predefining
1761 self
._entity
_list
.append(ent
)
1762 if ent
.name
is None:
1764 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1765 # because they're liable to clash in generated C.
1766 other_ent
= self
._entity
_dict
.get(ent
.name
)
1769 where
= QAPIError(other_ent
.info
, None, "previous definition")
1772 "'%s' is already defined\n%s" % (ent
.name
, where
))
1774 ent
.info
, "%s is already defined" % other_ent
.describe())
1775 self
._entity
_dict
[ent
.name
] = ent
1777 def lookup_entity(self
, name
, typ
=None):
1778 ent
= self
._entity
_dict
.get(name
)
1779 if typ
and not isinstance(ent
, typ
):
1783 def lookup_type(self
, name
):
1784 return self
.lookup_entity(name
, QAPISchemaType
)
1786 def resolve_type(self
, name
, info
, what
):
1787 typ
= self
.lookup_type(name
)
1792 info
, "%s uses unknown type '%s'" % (what
, name
))
1795 def _def_include(self
, expr
, info
, doc
):
1796 include
= expr
['include']
1799 while main_info
.parent
:
1800 main_info
= main_info
.parent
1801 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
.fname
))
1802 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
1804 def _def_builtin_type(self
, name
, json_type
, c_type
):
1805 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1806 # Instantiating only the arrays that are actually used would
1807 # be nice, but we can't as long as their generated code
1808 # (qapi-builtin-types.[ch]) may be shared by some other
1810 self
._make
_array
_type
(name
, None)
1812 def _def_predefineds(self
):
1813 for t
in [('str', 'string', 'char' + pointer_suffix
),
1814 ('number', 'number', 'double'),
1815 ('int', 'int', 'int64_t'),
1816 ('int8', 'int', 'int8_t'),
1817 ('int16', 'int', 'int16_t'),
1818 ('int32', 'int', 'int32_t'),
1819 ('int64', 'int', 'int64_t'),
1820 ('uint8', 'int', 'uint8_t'),
1821 ('uint16', 'int', 'uint16_t'),
1822 ('uint32', 'int', 'uint32_t'),
1823 ('uint64', 'int', 'uint64_t'),
1824 ('size', 'int', 'uint64_t'),
1825 ('bool', 'boolean', 'bool'),
1826 ('any', 'value', 'QObject' + pointer_suffix
),
1827 ('null', 'null', 'QNull' + pointer_suffix
)]:
1828 self
._def
_builtin
_type
(*t
)
1829 self
.the_empty_object_type
= QAPISchemaObjectType(
1830 'q_empty', None, None, None, None, [], None, [])
1831 self
._def
_entity
(self
.the_empty_object_type
)
1833 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1835 qtype_values
= self
._make
_enum
_members
(
1836 [{'name': n
} for n
in qtypes
], None)
1838 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
1839 qtype_values
, 'QTYPE'))
1841 def _make_features(self
, features
, info
):
1842 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
1845 def _make_enum_members(self
, values
, info
):
1846 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
1849 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
1850 # See also QAPISchemaObjectTypeMember.describe()
1851 name
= name
+ 'Kind' # reserved by check_defn_name_str()
1852 self
._def
_entity
(QAPISchemaEnumType(
1853 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
, info
),
1857 def _make_array_type(self
, element_type
, info
):
1858 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1859 if not self
.lookup_type(name
):
1860 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1863 def _make_implicit_object_type(self
, name
, info
, doc
, ifcond
,
1867 # See also QAPISchemaObjectTypeMember.describe()
1868 name
= 'q_obj_%s-%s' % (name
, role
)
1869 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1871 # The implicit object type has multiple users. This can
1872 # happen only for simple unions' implicit wrapper types.
1873 # Its ifcond should be the disjunction of its user's
1874 # ifconds. Not implemented. Instead, we always pass the
1875 # wrapped type's ifcond, which is trivially the same for all
1876 # users. It's also necessary for the wrapper to compile.
1877 # But it's not tight: the disjunction need not imply it. We
1878 # may end up compiling useless wrapper types.
1879 # TODO kill simple unions or implement the disjunction
1880 assert (ifcond
or []) == typ
._ifcond
# pylint: disable=protected-access
1882 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
,
1883 None, members
, None, []))
1886 def _def_enum_type(self
, expr
, info
, doc
):
1889 prefix
= expr
.get('prefix')
1890 ifcond
= expr
.get('if')
1891 self
._def
_entity
(QAPISchemaEnumType(
1892 name
, info
, doc
, ifcond
,
1893 self
._make
_enum
_members
(data
, info
), prefix
))
1895 def _make_member(self
, name
, typ
, ifcond
, info
):
1897 if name
.startswith('*'):
1900 if isinstance(typ
, list):
1901 assert len(typ
) == 1
1902 typ
= self
._make
_array
_type
(typ
[0], info
)
1903 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
)
1905 def _make_members(self
, data
, info
):
1906 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
1907 for (key
, value
) in data
.items()]
1909 def _def_struct_type(self
, expr
, info
, doc
):
1910 name
= expr
['struct']
1911 base
= expr
.get('base')
1913 ifcond
= expr
.get('if')
1914 features
= expr
.get('features', [])
1915 self
._def
_entity
(QAPISchemaObjectType(
1916 name
, info
, doc
, ifcond
, base
,
1917 self
._make
_members
(data
, info
),
1919 self
._make
_features
(features
, info
)))
1921 def _make_variant(self
, case
, typ
, ifcond
, info
):
1922 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1924 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1925 if isinstance(typ
, list):
1926 assert len(typ
) == 1
1927 typ
= self
._make
_array
_type
(typ
[0], info
)
1928 typ
= self
._make
_implicit
_object
_type
(
1929 typ
, info
, None, self
.lookup_type(typ
),
1930 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
1931 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1933 def _def_union_type(self
, expr
, info
, doc
):
1934 name
= expr
['union']
1936 base
= expr
.get('base')
1937 ifcond
= expr
.get('if')
1938 tag_name
= expr
.get('discriminator')
1940 if isinstance(base
, dict):
1941 base
= self
._make
_implicit
_object
_type
(
1942 name
, info
, doc
, ifcond
,
1943 'base', self
._make
_members
(base
, info
))
1945 variants
= [self
._make
_variant
(key
, value
['type'],
1946 value
.get('if'), info
)
1947 for (key
, value
) in data
.items()]
1950 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1951 value
.get('if'), info
)
1952 for (key
, value
) in data
.items()]
1953 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1954 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1955 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1956 members
= [tag_member
]
1958 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
1959 QAPISchemaObjectTypeVariants(
1960 tag_name
, info
, tag_member
, variants
),
1963 def _def_alternate_type(self
, expr
, info
, doc
):
1964 name
= expr
['alternate']
1966 ifcond
= expr
.get('if')
1967 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1969 for (key
, value
) in data
.items()]
1970 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1972 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
1973 QAPISchemaObjectTypeVariants(
1974 None, info
, tag_member
, variants
)))
1976 def _def_command(self
, expr
, info
, doc
):
1977 name
= expr
['command']
1978 data
= expr
.get('data')
1979 rets
= expr
.get('returns')
1980 gen
= expr
.get('gen', True)
1981 success_response
= expr
.get('success-response', True)
1982 boxed
= expr
.get('boxed', False)
1983 allow_oob
= expr
.get('allow-oob', False)
1984 allow_preconfig
= expr
.get('allow-preconfig', False)
1985 ifcond
= expr
.get('if')
1986 if isinstance(data
, OrderedDict
):
1987 data
= self
._make
_implicit
_object
_type
(
1988 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1989 if isinstance(rets
, list):
1990 assert len(rets
) == 1
1991 rets
= self
._make
_array
_type
(rets
[0], info
)
1992 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
1993 gen
, success_response
,
1994 boxed
, allow_oob
, allow_preconfig
))
1996 def _def_event(self
, expr
, info
, doc
):
1997 name
= expr
['event']
1998 data
= expr
.get('data')
1999 boxed
= expr
.get('boxed', False)
2000 ifcond
= expr
.get('if')
2001 if isinstance(data
, OrderedDict
):
2002 data
= self
._make
_implicit
_object
_type
(
2003 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
2004 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
2006 def _def_exprs(self
, exprs
):
2007 for expr_elem
in exprs
:
2008 expr
= expr_elem
['expr']
2009 info
= expr_elem
['info']
2010 doc
= expr_elem
.get('doc')
2012 self
._def
_enum
_type
(expr
, info
, doc
)
2013 elif 'struct' in expr
:
2014 self
._def
_struct
_type
(expr
, info
, doc
)
2015 elif 'union' in expr
:
2016 self
._def
_union
_type
(expr
, info
, doc
)
2017 elif 'alternate' in expr
:
2018 self
._def
_alternate
_type
(expr
, info
, doc
)
2019 elif 'command' in expr
:
2020 self
._def
_command
(expr
, info
, doc
)
2021 elif 'event' in expr
:
2022 self
._def
_event
(expr
, info
, doc
)
2023 elif 'include' in expr
:
2024 self
._def
_include
(expr
, info
, doc
)
2029 for ent
in self
._entity
_list
:
2032 def visit(self
, visitor
):
2033 visitor
.visit_begin(self
)
2035 visitor
.visit_module(module
)
2036 for entity
in self
._entity
_list
:
2037 if visitor
.visit_needed(entity
):
2038 if entity
.module
!= module
:
2039 module
= entity
.module
2040 visitor
.visit_module(module
)
2041 entity
.visit(visitor
)
2046 # Code generation helpers
2049 def camel_case(name
):
2053 if ch
in ['_', '-']:
2056 new_name
+= ch
.upper()
2059 new_name
+= ch
.lower()
2063 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2064 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2065 # ENUM24_Name -> ENUM24_NAME
2066 def camel_to_upper(value
):
2067 c_fun_str
= c_name(value
, False)
2072 length
= len(c_fun_str
)
2073 for i
in range(length
):
2075 # When c is upper and no '_' appears before, do more checks
2076 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
2077 if i
< length
- 1 and c_fun_str
[i
+ 1].islower():
2079 elif c_fun_str
[i
- 1].isdigit():
2082 return new_name
.lstrip('_').upper()
2085 def c_enum_const(type_name
, const_name
, prefix
=None):
2086 if prefix
is not None:
2088 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
2091 if hasattr(str, 'maketrans'):
2092 c_name_trans
= str.maketrans('.-', '__')
2094 c_name_trans
= string
.maketrans('.-', '__')
2097 # Map @name to a valid C identifier.
2098 # If @protect, avoid returning certain ticklish identifiers (like
2099 # C keywords) by prepending 'q_'.
2101 # Used for converting 'name' from a 'name':'type' qapi definition
2102 # into a generated struct member, as well as converting type names
2103 # into substrings of a generated C function name.
2104 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2105 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2106 def c_name(name
, protect
=True):
2107 # ANSI X3J11/88-090, 3.1.1
2108 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
2109 'default', 'do', 'double', 'else', 'enum', 'extern',
2110 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2111 'return', 'short', 'signed', 'sizeof', 'static',
2112 'struct', 'switch', 'typedef', 'union', 'unsigned',
2113 'void', 'volatile', 'while'])
2114 # ISO/IEC 9899:1999, 6.4.1
2115 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2116 # ISO/IEC 9899:2011, 6.4.1
2117 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2118 '_Noreturn', '_Static_assert', '_Thread_local'])
2119 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2121 gcc_words
= set(['asm', 'typeof'])
2122 # C++ ISO/IEC 14882:2003 2.11
2123 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
2124 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2125 'namespace', 'new', 'operator', 'private', 'protected',
2126 'public', 'reinterpret_cast', 'static_cast', 'template',
2127 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2128 'using', 'virtual', 'wchar_t',
2129 # alternative representations
2130 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2131 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2132 # namespace pollution:
2133 polluted_words
= set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2134 name
= name
.translate(c_name_trans
)
2135 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
2136 | cpp_words | polluted_words
):
2141 eatspace
= '\033EATSPACE.'
2142 pointer_suffix
= ' *' + eatspace
2145 def genindent(count
):
2147 for _
in range(count
):
2155 def push_indent(indent_amount
=4):
2157 indent_level
+= indent_amount
2160 def pop_indent(indent_amount
=4):
2162 indent_level
-= indent_amount
2165 # Generate @code with @kwds interpolated.
2166 # Obey indent_level, and strip eatspace.
2167 def cgen(code
, **kwds
):
2170 indent
= genindent(indent_level
)
2171 # re.subn() lacks flags support before Python 2.7, use re.compile()
2172 raw
= re
.subn(re
.compile(r
'^(?!(#|$))', re
.MULTILINE
),
2175 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
2178 def mcgen(code
, **kwds
):
2181 return cgen(code
, **kwds
)
2184 def c_fname(filename
):
2185 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
)
2188 def guardstart(name
):
2194 name
=c_fname(name
).upper())
2200 #endif /* %(name)s */
2202 name
=c_fname(name
).upper())
2214 def gen_endif(ifcond
):
2216 for ifc
in reversed(ifcond
):
2218 #endif /* %(cond)s */
2223 def _wrap_ifcond(ifcond
, before
, after
):
2225 return after
# suppress empty #if ... #endif
2227 assert after
.startswith(before
)
2229 added
= after
[len(before
):]
2230 if added
[0] == '\n':
2233 out
+= gen_if(ifcond
)
2235 out
+= gen_endif(ifcond
)
2239 def gen_enum_lookup(name
, members
, prefix
=None):
2242 const QEnumLookup %(c_name)s_lookup = {
2243 .array = (const char *const[]) {
2245 c_name
=c_name(name
))
2247 ret
+= gen_if(m
.ifcond
)
2248 index
= c_enum_const(name
, m
.name
, prefix
)
2250 [%(index)s] = "%(name)s",
2252 index
=index
, name
=m
.name
)
2253 ret
+= gen_endif(m
.ifcond
)
2257 .size = %(max_index)s
2260 max_index
=c_enum_const(name
, '_MAX', prefix
))
2264 def gen_enum(name
, members
, prefix
=None):
2265 # append automatically generated _MAX value
2266 enum_members
= members
+ [QAPISchemaEnumMember('_MAX', None)]
2270 typedef enum %(c_name)s {
2272 c_name
=c_name(name
))
2274 for m
in enum_members
:
2275 ret
+= gen_if(m
.ifcond
)
2279 c_enum
=c_enum_const(name
, m
.name
, prefix
))
2280 ret
+= gen_endif(m
.ifcond
)
2285 c_name
=c_name(name
))
2289 #define %(c_name)s_str(val) \\
2290 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2292 extern const QEnumLookup %(c_name)s_lookup;
2294 c_name
=c_name(name
))
2298 def build_params(arg_type
, boxed
, extra
=None):
2303 ret
+= '%s arg' % arg_type
.c_param_type()
2306 assert not arg_type
.variants
2307 for memb
in arg_type
.members
:
2311 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
2312 ret
+= '%s %s' % (memb
.type.c_param_type(),
2316 return ret
if ret
else 'void'
2320 # Accumulate and write output
2323 class QAPIGen(object):
2325 def __init__(self
, fname
):
2330 def preamble_add(self
, text
):
2331 self
._preamble
+= text
2333 def add(self
, text
):
2336 def get_content(self
):
2337 return self
._top
() + self
._preamble
+ self
._body
+ self
._bottom
()
2345 def write(self
, output_dir
):
2346 pathname
= os
.path
.join(output_dir
, self
.fname
)
2347 dir = os
.path
.dirname(pathname
)
2351 except os
.error
as e
:
2352 if e
.errno
!= errno
.EEXIST
:
2354 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2355 if sys
.version_info
[0] >= 3:
2356 f
= open(fd
, 'r+', encoding
='utf-8')
2358 f
= os
.fdopen(fd
, 'r+')
2359 text
= self
.get_content()
2360 oldtext
= f
.read(len(text
) + 1)
2369 def ifcontext(ifcond
, *args
):
2370 """A 'with' statement context manager to wrap with start_if()/end_if()
2372 *args: any number of QAPIGenCCode
2376 with ifcontext(ifcond, self._genh, self._genc):
2377 modify self._genh and self._genc ...
2379 Is equivalent to calling::
2381 self._genh.start_if(ifcond)
2382 self._genc.start_if(ifcond)
2383 modify self._genh and self._genc ...
2388 arg
.start_if(ifcond
)
2394 class QAPIGenCCode(QAPIGen
):
2396 def __init__(self
, fname
):
2397 QAPIGen
.__init
__(self
, fname
)
2398 self
._start
_if
= None
2400 def start_if(self
, ifcond
):
2401 assert self
._start
_if
is None
2402 self
._start
_if
= (ifcond
, self
._body
, self
._preamble
)
2405 assert self
._start
_if
2407 self
._start
_if
= None
2409 def _wrap_ifcond(self
):
2410 self
._body
= _wrap_ifcond(self
._start
_if
[0],
2411 self
._start
_if
[1], self
._body
)
2412 self
._preamble
= _wrap_ifcond(self
._start
_if
[0],
2413 self
._start
_if
[2], self
._preamble
)
2415 def get_content(self
):
2416 assert self
._start
_if
is None
2417 return QAPIGen
.get_content(self
)
2420 class QAPIGenC(QAPIGenCCode
):
2422 def __init__(self
, fname
, blurb
, pydoc
):
2423 QAPIGenCCode
.__init
__(self
, fname
)
2425 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2430 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2437 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2438 * See the COPYING.LIB file in the top-level directory.
2442 blurb
=self
._blurb
, copyright
=self
._copyright
)
2447 /* Dummy declaration to prevent empty .o file */
2448 char qapi_dummy_%(name)s;
2450 name
=c_fname(self
.fname
))
2453 class QAPIGenH(QAPIGenC
):
2456 return QAPIGenC
._top
(self
) + guardstart(self
.fname
)
2459 return guardend(self
.fname
)
2462 class QAPIGenDoc(QAPIGen
):
2465 return (QAPIGen
._top
(self
)
2466 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2469 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2471 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2472 self
._prefix
= prefix
2474 self
._genc
= QAPIGenC(self
._prefix
+ self
._what
+ '.c',
2476 self
._genh
= QAPIGenH(self
._prefix
+ self
._what
+ '.h',
2479 def write(self
, output_dir
):
2480 self
._genc
.write(output_dir
)
2481 self
._genh
.write(output_dir
)
2484 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2486 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2487 self
._prefix
= prefix
2494 self
._main
_module
= None
2497 def _is_user_module(name
):
2498 return name
and not name
.startswith('./')
2501 def _is_builtin_module(name
):
2504 def _module_dirname(self
, what
, name
):
2505 if self
._is
_user
_module
(name
):
2506 return os
.path
.dirname(name
)
2509 def _module_basename(self
, what
, name
):
2510 ret
= '' if self
._is
_builtin
_module
(name
) else self
._prefix
2511 if self
._is
_user
_module
(name
):
2512 basename
= os
.path
.basename(name
)
2514 if name
!= self
._main
_module
:
2515 ret
+= '-' + os
.path
.splitext(basename
)[0]
2517 name
= name
[2:] if name
else 'builtin'
2518 ret
+= re
.sub(r
'-', '-' + name
+ '-', what
)
2521 def _module_filename(self
, what
, name
):
2522 return os
.path
.join(self
._module
_dirname
(what
, name
),
2523 self
._module
_basename
(what
, name
))
2525 def _add_module(self
, name
, blurb
):
2526 basename
= self
._module
_filename
(self
._what
, name
)
2527 genc
= QAPIGenC(basename
+ '.c', blurb
, self
._pydoc
)
2528 genh
= QAPIGenH(basename
+ '.h', blurb
, self
._pydoc
)
2529 self
._module
[name
] = (genc
, genh
)
2530 self
._set
_module
(name
)
2532 def _add_user_module(self
, name
, blurb
):
2533 assert self
._is
_user
_module
(name
)
2534 if self
._main
_module
is None:
2535 self
._main
_module
= name
2536 self
._add
_module
(name
, blurb
)
2538 def _add_system_module(self
, name
, blurb
):
2539 self
._add
_module
(name
and './' + name
, blurb
)
2541 def _set_module(self
, name
):
2542 self
._genc
, self
._genh
= self
._module
[name
]
2544 def write(self
, output_dir
, opt_builtins
=False):
2545 for name
in self
._module
:
2546 if self
._is
_builtin
_module
(name
) and not opt_builtins
:
2548 (genc
, genh
) = self
._module
[name
]
2549 genc
.write(output_dir
)
2550 genh
.write(output_dir
)
2552 def _begin_user_module(self
, name
):
2555 def visit_module(self
, name
):
2556 if name
in self
._module
:
2557 self
._set
_module
(name
)
2558 elif self
._is
_builtin
_module
(name
):
2559 # The built-in module has not been created. No code may
2564 self
._add
_user
_module
(name
, self
._blurb
)
2565 self
._begin
_user
_module
(name
)
2567 def visit_include(self
, name
, info
):
2568 relname
= os
.path
.relpath(self
._module
_filename
(self
._what
, name
),
2569 os
.path
.dirname(self
._genh
.fname
))
2570 self
._genh
.preamble_add(mcgen('''
2571 #include "%(relname)s.h"