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(name
, info
, source
,
661 allow_optional
=False, enum_member
=False, permit_upper
=False):
662 check_name_is_str(name
, info
, source
)
663 check_name_str(name
, info
, source
,
664 allow_optional
, enum_member
, permit_upper
)
667 def check_name_is_str(name
, info
, source
):
668 if not isinstance(name
, str):
669 raise QAPISemError(info
, "%s requires a string name" % source
)
672 def check_name_str(name
, info
, source
,
673 allow_optional
=False, enum_member
=False,
678 if allow_optional
and name
.startswith('*'):
679 membername
= name
[1:]
680 # Enum members can start with a digit, because the generated C
681 # code always prefixes it with the enum name
682 if enum_member
and membername
[0].isdigit():
683 membername
= 'D' + membername
684 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
685 # and 'q_obj_*' implicit type names.
686 if not valid_name
.match(membername
) or \
687 c_name(membername
, False).startswith('q_'):
688 raise QAPISemError(info
, "%s uses invalid name '%s'" % (source
, name
))
689 if not permit_upper
and name
.lower() != name
:
691 info
, "%s uses uppercase in name '%s'" % (source
, name
))
692 assert not membername
.startswith('*')
695 def check_defn_name_str(name
, info
, meta
):
696 check_name_str(name
, info
, meta
, permit_upper
=True)
697 if name
.endswith('Kind') or name
.endswith('List'):
699 info
, "%s '%s' should not end in '%s'" % (meta
, name
, name
[-4:]))
702 def check_if(expr
, info
):
704 def check_if_str(ifcond
, info
):
705 if not isinstance(ifcond
, str):
707 info
, "'if' condition must be a string or a list of strings")
708 if ifcond
.strip() == '':
709 raise QAPISemError(info
, "'if' condition '%s' makes no sense"
712 ifcond
= expr
.get('if')
715 if isinstance(ifcond
, list):
717 raise QAPISemError(info
, "'if' condition [] is useless")
719 check_if_str(elt
, info
)
721 check_if_str(ifcond
, info
)
724 def check_type(value
, info
, source
,
725 allow_array
=False, allow_dict
=False):
730 if isinstance(value
, list):
732 raise QAPISemError(info
, "%s cannot be an array" % source
)
733 if len(value
) != 1 or not isinstance(value
[0], str):
734 raise QAPISemError(info
,
735 "%s: array type must contain single type name" %
740 if isinstance(value
, str):
746 raise QAPISemError(info
, "%s should be a type name" % source
)
748 if not isinstance(value
, OrderedDict
):
749 raise QAPISemError(info
,
750 "%s should be an object or type name" % source
)
752 permit_upper
= allow_dict
in name_case_whitelist
754 # value is a dictionary, check that each member is okay
755 for (key
, arg
) in value
.items():
756 check_name_str(key
, info
, "member of %s" % source
,
757 allow_optional
=True, permit_upper
=permit_upper
)
758 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
760 info
, "member of %s uses reserved name '%s'" % (source
, key
))
761 check_known_keys(arg
, info
, "member '%s' of %s" % (key
, source
),
765 check_type(arg
['type'], info
, "member '%s' of %s" % (key
, source
),
769 def check_command(expr
, info
):
770 name
= expr
['command']
771 args
= expr
.get('data')
772 boxed
= expr
.get('boxed', False)
774 if boxed
and args
is None:
775 raise QAPISemError(info
, "'boxed': true requires 'data'")
776 check_type(args
, info
, "'data' for command '%s'" % name
,
777 allow_dict
=not boxed
)
778 check_type(expr
.get('returns'), info
,
779 "'returns' for command '%s'" % name
,
783 def check_event(expr
, info
):
785 args
= expr
.get('data')
786 boxed
= expr
.get('boxed', False)
788 if boxed
and args
is None:
789 raise QAPISemError(info
, "'boxed': true requires 'data'")
790 check_type(args
, info
, "'data' for event '%s'" % name
,
791 allow_dict
=not boxed
)
794 def check_union(expr
, info
):
796 base
= expr
.get('base')
797 discriminator
= expr
.get('discriminator')
798 members
= expr
['data']
800 if discriminator
is None: # simple union
803 info
, "simple union '%s' must not have a base" % name
)
805 check_type(base
, info
, "'base' for union '%s'" % name
,
809 info
, "flat union '%s' must have a base" % name
)
810 check_name_is_str(discriminator
, info
,
811 "discriminator of flat union '%s'" % name
)
813 for (key
, value
) in members
.items():
814 check_name_str(key
, info
, "member of union '%s'" % name
)
815 check_known_keys(value
, info
,
816 "member '%s' of union '%s'" % (key
, name
),
818 check_if(value
, info
)
820 check_type(value
['type'], info
,
821 "member '%s' of union '%s'" % (key
, name
),
822 allow_array
=not base
)
825 def check_alternate(expr
, info
):
826 name
= expr
['alternate']
827 members
= expr
['data']
829 if len(members
) == 0:
830 raise QAPISemError(info
,
831 "alternate '%s' cannot have empty 'data'" % name
)
832 for (key
, value
) in members
.items():
833 check_name_str(key
, info
, "member of alternate '%s'" % name
)
834 check_known_keys(value
, info
,
835 "member '%s' of alternate '%s'" % (key
, name
),
837 check_if(value
, info
)
839 check_type(value
['type'], info
,
840 "member '%s' of alternate '%s'" % (key
, name
))
843 def check_enum(expr
, info
):
845 members
= expr
['data']
846 prefix
= expr
.get('prefix')
848 if not isinstance(members
, list):
849 raise QAPISemError(info
,
850 "enum '%s' requires an array for 'data'" % name
)
851 if prefix
is not None and not isinstance(prefix
, str):
852 raise QAPISemError(info
,
853 "enum '%s' requires a string for 'prefix'" % name
)
855 permit_upper
= name
in name_case_whitelist
857 for member
in members
:
858 check_known_keys(member
, info
, "member of enum '%s'" % name
,
860 check_if(member
, info
)
862 check_name(member
['name'], info
, "member of enum '%s'" % name
,
863 enum_member
=True, permit_upper
=permit_upper
)
866 def check_struct(expr
, info
):
867 name
= expr
['struct']
868 members
= expr
['data']
869 features
= expr
.get('features')
871 check_type(members
, info
, "'data' for struct '%s'" % name
,
873 check_type(expr
.get('base'), info
, "'base' for struct '%s'" % name
)
876 if not isinstance(features
, list):
878 info
, "struct '%s' requires an array for 'features'" % name
)
880 assert isinstance(f
, dict)
881 check_known_keys(f
, info
, "feature of struct %s" % name
,
886 check_name(f
['name'], info
, "feature of struct %s" % name
)
889 def check_known_keys(value
, info
, source
, required
, optional
):
892 return ', '.join("'" + e
+ "'" for e
in sorted(elems
))
894 missing
= set(required
) - set(value
)
898 "key%s %s %s missing from %s"
899 % ('s' if len(missing
) > 1 else '', pprint(missing
),
900 'are' if len(missing
) > 1 else 'is', source
))
901 allowed
= set(required
+ optional
)
902 unknown
= set(value
) - allowed
906 "unknown key%s %s in %s\nValid keys are %s."
907 % ('s' if len(unknown
) > 1 else '', pprint(unknown
),
908 source
, pprint(allowed
)))
911 def check_keys(expr
, info
, meta
, required
, optional
=[]):
913 required
= required
+ [meta
]
914 source
= "%s '%s'" % (meta
, name
)
915 check_known_keys(expr
, info
, source
, required
, optional
)
918 def check_flags(expr
, info
):
919 for key
in ['gen', 'success-response']:
920 if key
in expr
and expr
[key
] is not False:
922 info
, "flag '%s' may only use false value" % key
)
923 for key
in ['boxed', 'allow-oob', 'allow-preconfig']:
924 if key
in expr
and expr
[key
] is not True:
926 info
, "flag '%s' may only use true value" % key
)
929 def normalize_enum(expr
):
930 if isinstance(expr
['data'], list):
931 expr
['data'] = [m
if isinstance(m
, dict) else {'name': m
}
932 for m
in expr
['data']]
935 def normalize_members(members
):
936 if isinstance(members
, OrderedDict
):
937 for key
, arg
in members
.items():
938 if isinstance(arg
, dict):
940 members
[key
] = {'type': arg
}
943 def normalize_features(features
):
944 if isinstance(features
, list):
945 features
[:] = [f
if isinstance(f
, dict) else {'name': f
}
949 def normalize_if(expr
):
950 ifcond
= expr
.get('if')
951 if isinstance(ifcond
, str):
952 expr
['if'] = [ifcond
]
955 def check_exprs(exprs
):
956 for expr_elem
in exprs
:
957 expr
= expr_elem
['expr']
958 info
= expr_elem
['info']
959 doc
= expr_elem
.get('doc')
961 if 'include' in expr
:
964 if not doc
and doc_required
:
965 raise QAPISemError(info
,
966 "definition missing documentation comment")
970 elif 'union' in expr
:
972 elif 'alternate' in expr
:
974 elif 'struct' in expr
:
976 elif 'command' in expr
:
978 elif 'event' in expr
:
981 raise QAPISemError(info
, "expression is missing metatype")
984 check_name_is_str(name
, info
, "'%s'" % meta
)
985 info
.set_defn(meta
, name
)
986 check_defn_name_str(name
, info
, meta
)
988 if doc
and doc
.symbol
!= name
:
991 "definition of '%s' follows documentation for '%s'"
992 % (name
, doc
.symbol
))
995 check_keys(expr
, info
, 'enum', ['data'], ['if', 'prefix'])
997 check_enum(expr
, info
)
998 elif meta
== 'union':
999 check_keys(expr
, info
, 'union', ['data'],
1000 ['base', 'discriminator', 'if'])
1001 normalize_members(expr
.get('base'))
1002 normalize_members(expr
['data'])
1003 check_union(expr
, info
)
1004 elif meta
== 'alternate':
1005 check_keys(expr
, info
, 'alternate', ['data'], ['if'])
1006 normalize_members(expr
['data'])
1007 check_alternate(expr
, info
)
1008 elif meta
== 'struct':
1009 check_keys(expr
, info
, 'struct', ['data'],
1010 ['base', 'if', 'features'])
1011 normalize_members(expr
['data'])
1012 normalize_features(expr
.get('features'))
1013 check_struct(expr
, info
)
1014 elif meta
== 'command':
1015 check_keys(expr
, info
, 'command', [],
1016 ['data', 'returns', 'gen', 'success-response',
1017 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
1018 normalize_members(expr
.get('data'))
1019 check_command(expr
, info
)
1020 elif meta
== 'event':
1021 check_keys(expr
, info
, 'event', [], ['data', 'boxed', 'if'])
1022 normalize_members(expr
.get('data'))
1023 check_event(expr
, info
)
1025 assert False, 'unexpected meta type'
1028 check_if(expr
, info
)
1029 check_flags(expr
, info
)
1032 doc
.check_expr(expr
)
1038 # Schema compiler frontend
1039 # TODO catching name collisions in generated code would be nice
1042 class QAPISchemaEntity(object):
1045 def __init__(self
, name
, info
, doc
, ifcond
=None):
1046 assert name
is None or isinstance(name
, str)
1049 # For explicitly defined entities, info points to the (explicit)
1050 # definition. For builtins (and their arrays), info is None.
1051 # For implicitly defined entities, info points to a place that
1052 # triggered the implicit definition (there may be more than one
1056 self
._ifcond
= ifcond
or []
1057 self
._checked
= False
1060 return c_name(self
.name
)
1062 def check(self
, schema
):
1063 assert not self
._checked
1065 self
._module
= os
.path
.relpath(self
.info
.fname
,
1066 os
.path
.dirname(schema
.fname
))
1067 self
._checked
= True
1071 assert self
._checked
1076 assert self
._checked
1079 def is_implicit(self
):
1080 return not self
.info
1082 def visit(self
, visitor
):
1083 assert self
._checked
1087 return "%s '%s'" % (self
.meta
, self
.name
)
1090 class QAPISchemaVisitor(object):
1091 def visit_begin(self
, schema
):
1094 def visit_end(self
):
1097 def visit_module(self
, fname
):
1100 def visit_needed(self
, entity
):
1101 # Default to visiting everything
1104 def visit_include(self
, fname
, info
):
1107 def visit_builtin_type(self
, name
, info
, json_type
):
1110 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
1113 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
1116 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
,
1120 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
1124 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
1127 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
1128 success_response
, boxed
, allow_oob
, allow_preconfig
):
1131 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
1135 class QAPISchemaInclude(QAPISchemaEntity
):
1137 def __init__(self
, fname
, info
):
1138 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1141 def visit(self
, visitor
):
1142 QAPISchemaEntity
.visit(self
, visitor
)
1143 visitor
.visit_include(self
.fname
, self
.info
)
1146 class QAPISchemaType(QAPISchemaEntity
):
1147 # Return the C type for common use.
1148 # For the types we commonly box, this is a pointer type.
1152 # Return the C type to be used in a parameter list.
1153 def c_param_type(self
):
1154 return self
.c_type()
1156 # Return the C type to be used where we suppress boxing.
1157 def c_unboxed_type(self
):
1158 return self
.c_type()
1160 def json_type(self
):
1163 def alternate_qtype(self
):
1165 'null': 'QTYPE_QNULL',
1166 'string': 'QTYPE_QSTRING',
1167 'number': 'QTYPE_QNUM',
1168 'int': 'QTYPE_QNUM',
1169 'boolean': 'QTYPE_QBOOL',
1170 'object': 'QTYPE_QDICT'
1172 return json2qtype
.get(self
.json_type())
1175 if self
.is_implicit():
1181 return "%s type '%s'" % (self
.meta
, self
.name
)
1184 class QAPISchemaBuiltinType(QAPISchemaType
):
1187 def __init__(self
, name
, json_type
, c_type
):
1188 QAPISchemaType
.__init
__(self
, name
, None, None)
1189 assert not c_type
or isinstance(c_type
, str)
1190 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1192 self
._json
_type
_name
= json_type
1193 self
._c
_type
_name
= c_type
1199 return self
._c
_type
_name
1201 def c_param_type(self
):
1202 if self
.name
== 'str':
1203 return 'const ' + self
._c
_type
_name
1204 return self
._c
_type
_name
1206 def json_type(self
):
1207 return self
._json
_type
_name
1210 return self
.json_type()
1212 def visit(self
, visitor
):
1213 QAPISchemaType
.visit(self
, visitor
)
1214 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1217 class QAPISchemaEnumType(QAPISchemaType
):
1220 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
1221 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1223 assert isinstance(m
, QAPISchemaEnumMember
)
1224 m
.set_defined_in(name
)
1225 assert prefix
is None or isinstance(prefix
, str)
1226 self
.members
= members
1227 self
.prefix
= prefix
1229 def check(self
, schema
):
1230 QAPISchemaType
.check(self
, schema
)
1232 for m
in self
.members
:
1233 m
.check_clash(self
.info
, seen
)
1235 self
.doc
.connect_member(m
)
1237 def is_implicit(self
):
1238 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1239 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1242 return c_name(self
.name
)
1244 def member_names(self
):
1245 return [m
.name
for m
in self
.members
]
1247 def json_type(self
):
1250 def visit(self
, visitor
):
1251 QAPISchemaType
.visit(self
, visitor
)
1252 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
1253 self
.members
, self
.prefix
)
1256 class QAPISchemaArrayType(QAPISchemaType
):
1259 def __init__(self
, name
, info
, element_type
):
1260 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
1261 assert isinstance(element_type
, str)
1262 self
._element
_type
_name
= element_type
1263 self
.element_type
= None
1265 def check(self
, schema
):
1266 QAPISchemaType
.check(self
, schema
)
1267 self
.element_type
= schema
.resolve_type(
1268 self
._element
_type
_name
, self
.info
,
1269 self
.info
and self
.info
.defn_meta
)
1270 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
1274 assert self
._checked
1275 return self
.element_type
.ifcond
1279 assert self
._checked
1280 return self
.element_type
.module
1282 def is_implicit(self
):
1286 return c_name(self
.name
) + pointer_suffix
1288 def json_type(self
):
1292 elt_doc_type
= self
.element_type
.doc_type()
1293 if not elt_doc_type
:
1295 return 'array of ' + elt_doc_type
1297 def visit(self
, visitor
):
1298 QAPISchemaType
.visit(self
, visitor
)
1299 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
1304 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
1307 class QAPISchemaObjectType(QAPISchemaType
):
1308 def __init__(self
, name
, info
, doc
, ifcond
,
1309 base
, local_members
, variants
, features
):
1310 # struct has local_members, optional base, and no variants
1311 # flat union has base, variants, and no local_members
1312 # simple union has local_members, variants, and no base
1313 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1314 self
.meta
= 'union' if variants
else 'struct'
1315 assert base
is None or isinstance(base
, str)
1316 for m
in local_members
:
1317 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1318 m
.set_defined_in(name
)
1319 if variants
is not None:
1320 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1321 variants
.set_defined_in(name
)
1323 assert isinstance(f
, QAPISchemaFeature
)
1324 f
.set_defined_in(name
)
1325 self
._base
_name
= base
1327 self
.local_members
= local_members
1328 self
.variants
= variants
1330 self
.features
= features
1332 def check(self
, schema
):
1333 # This calls another type T's .check() exactly when the C
1334 # struct emitted by gen_object() contains that T's C struct
1335 # (pointers don't count).
1336 if self
.members
is not None:
1337 # A previous .check() completed: nothing to do
1340 # Recursed: C struct contains itself
1341 raise QAPISemError(self
.info
,
1342 "object %s contains itself" % self
.name
)
1344 QAPISchemaType
.check(self
, schema
)
1345 assert self
._checked
and self
.members
is None
1347 seen
= OrderedDict()
1349 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
1351 if (not isinstance(self
.base
, QAPISchemaObjectType
)
1352 or self
.base
.variants
):
1355 "'base' requires a struct type, %s isn't"
1356 % self
.base
.describe())
1357 self
.base
.check(schema
)
1358 self
.base
.check_clash(self
.info
, seen
)
1359 for m
in self
.local_members
:
1361 m
.check_clash(self
.info
, seen
)
1363 self
.doc
.connect_member(m
)
1364 members
= seen
.values()
1367 self
.variants
.check(schema
, seen
)
1368 self
.variants
.check_clash(self
.info
, seen
)
1370 # Features are in a name space separate from members
1372 for f
in self
.features
:
1373 f
.check_clash(self
.info
, seen
)
1378 self
.members
= members
# mark completed
1380 # Check that the members of this type do not cause duplicate JSON members,
1381 # and update seen to track the members seen so far. Report any errors
1382 # on behalf of info, which is not necessarily self.info
1383 def check_clash(self
, info
, seen
):
1384 assert self
._checked
1385 assert not self
.variants
# not implemented
1386 for m
in self
.members
:
1387 m
.check_clash(info
, seen
)
1391 assert self
._checked
1392 if isinstance(self
._ifcond
, QAPISchemaType
):
1393 # Simple union wrapper type inherits from wrapped type;
1394 # see _make_implicit_object_type()
1395 return self
._ifcond
.ifcond
1398 def is_implicit(self
):
1399 # See QAPISchema._make_implicit_object_type(), as well as
1400 # _def_predefineds()
1401 return self
.name
.startswith('q_')
1404 assert self
.members
is not None
1405 return not self
.members
and not self
.variants
1408 assert self
.name
!= 'q_empty'
1409 return QAPISchemaType
.c_name(self
)
1412 assert not self
.is_implicit()
1413 return c_name(self
.name
) + pointer_suffix
1415 def c_unboxed_type(self
):
1416 return c_name(self
.name
)
1418 def json_type(self
):
1421 def visit(self
, visitor
):
1422 QAPISchemaType
.visit(self
, visitor
)
1423 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
1424 self
.base
, self
.local_members
, self
.variants
,
1426 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
1427 self
.members
, self
.variants
,
1431 class QAPISchemaMember(object):
1432 """ Represents object members, enum members and features """
1435 def __init__(self
, name
, info
, ifcond
=None):
1436 assert isinstance(name
, str)
1439 self
.ifcond
= ifcond
or []
1440 self
.defined_in
= None
1442 def set_defined_in(self
, name
):
1443 assert not self
.defined_in
1444 self
.defined_in
= name
1446 def check_clash(self
, info
, seen
):
1447 cname
= c_name(self
.name
)
1451 "%s collides with %s"
1452 % (self
.describe(info
), seen
[cname
].describe(info
)))
1455 def describe(self
, info
):
1457 defined_in
= self
.defined_in
1460 if defined_in
.startswith('q_obj_'):
1461 # See QAPISchema._make_implicit_object_type() - reverse the
1462 # mapping there to create a nice human-readable description
1463 defined_in
= defined_in
[6:]
1464 if defined_in
.endswith('-arg'):
1465 # Implicit type created for a command's dict 'data'
1466 assert role
== 'member'
1468 elif defined_in
.endswith('-base'):
1469 # Implicit type created for a flat union's dict 'base'
1470 role
= 'base ' + role
1472 # Implicit type created for a simple union's branch
1473 assert defined_in
.endswith('-wrapper')
1474 # Unreachable and not implemented
1476 elif defined_in
.endswith('Kind'):
1477 # See QAPISchema._make_implicit_enum_type()
1478 # Implicit enum created for simple union's branches
1479 assert role
== 'value'
1481 elif defined_in
!= info
.defn_name
:
1482 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
1483 return "%s '%s'" % (role
, self
.name
)
1486 class QAPISchemaEnumMember(QAPISchemaMember
):
1490 class QAPISchemaFeature(QAPISchemaMember
):
1494 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1495 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None):
1496 QAPISchemaMember
.__init
__(self
, name
, info
, ifcond
)
1497 assert isinstance(typ
, str)
1498 assert isinstance(optional
, bool)
1499 self
._type
_name
= typ
1501 self
.optional
= optional
1503 def check(self
, schema
):
1504 assert self
.defined_in
1505 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
1509 class QAPISchemaObjectTypeVariants(object):
1510 def __init__(self
, tag_name
, info
, tag_member
, variants
):
1511 # Flat unions pass tag_name but not tag_member.
1512 # Simple unions and alternates pass tag_member but not tag_name.
1513 # After check(), tag_member is always set, and tag_name remains
1514 # a reliable witness of being used by a flat union.
1515 assert bool(tag_member
) != bool(tag_name
)
1516 assert (isinstance(tag_name
, str) or
1517 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1519 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1520 self
._tag
_name
= tag_name
1522 self
.tag_member
= tag_member
1523 self
.variants
= variants
1525 def set_defined_in(self
, name
):
1526 for v
in self
.variants
:
1527 v
.set_defined_in(name
)
1529 def check(self
, schema
, seen
):
1530 if not self
.tag_member
: # flat union
1531 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
1533 # Pointing to the base type when not implicit would be
1534 # nice, but we don't know it here
1535 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
1538 "discriminator '%s' is not a member of %s"
1539 % (self
._tag
_name
, base
))
1541 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
1543 if not base_type
.is_implicit():
1544 base
= "base type '%s'" % self
.tag_member
.defined_in
1545 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
1548 "discriminator member '%s' of %s must be of enum type"
1549 % (self
._tag
_name
, base
))
1550 if self
.tag_member
.optional
:
1553 "discriminator member '%s' of %s must not be optional"
1554 % (self
._tag
_name
, base
))
1555 if self
.tag_member
.ifcond
:
1558 "discriminator member '%s' of %s must not be conditional"
1559 % (self
._tag
_name
, base
))
1560 else: # simple union
1561 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1562 assert not self
.tag_member
.optional
1563 assert self
.tag_member
.ifcond
== []
1564 if self
._tag
_name
: # flat union
1565 # branches that are not explicitly covered get an empty type
1566 cases
= set([v
.name
for v
in self
.variants
])
1567 for m
in self
.tag_member
.type.members
:
1568 if m
.name
not in cases
:
1569 v
= QAPISchemaObjectTypeVariant(m
.name
, self
.info
,
1570 'q_empty', m
.ifcond
)
1571 v
.set_defined_in(self
.tag_member
.defined_in
)
1572 self
.variants
.append(v
)
1573 if not self
.variants
:
1574 raise QAPISemError(self
.info
, "union has no branches")
1575 for v
in self
.variants
:
1577 # Union names must match enum values; alternate names are
1578 # checked separately. Use 'seen' to tell the two apart.
1580 if v
.name
not in self
.tag_member
.type.member_names():
1583 "branch '%s' is not a value of %s"
1584 % (v
.name
, self
.tag_member
.type.describe()))
1585 if (not isinstance(v
.type, QAPISchemaObjectType
)
1586 or v
.type.variants
):
1590 % (v
.describe(self
.info
), v
.type.describe()))
1591 v
.type.check(schema
)
1593 def check_clash(self
, info
, seen
):
1594 for v
in self
.variants
:
1595 # Reset seen map for each variant, since qapi names from one
1596 # branch do not affect another branch
1597 v
.type.check_clash(info
, dict(seen
))
1600 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1603 def __init__(self
, name
, info
, typ
, ifcond
=None):
1604 QAPISchemaObjectTypeMember
.__init
__(self
, name
, info
, typ
,
1608 class QAPISchemaAlternateType(QAPISchemaType
):
1611 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
1612 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1613 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1614 assert variants
.tag_member
1615 variants
.set_defined_in(name
)
1616 variants
.tag_member
.set_defined_in(self
.name
)
1617 self
.variants
= variants
1619 def check(self
, schema
):
1620 QAPISchemaType
.check(self
, schema
)
1621 self
.variants
.tag_member
.check(schema
)
1622 # Not calling self.variants.check_clash(), because there's nothing
1624 self
.variants
.check(schema
, {})
1625 # Alternate branch names have no relation to the tag enum values;
1626 # so we have to check for potential name collisions ourselves.
1629 for v
in self
.variants
.variants
:
1630 v
.check_clash(self
.info
, seen
)
1631 qtype
= v
.type.alternate_qtype()
1636 % (v
.describe(self
.info
), v
.type.describe()))
1637 conflicting
= set([qtype
])
1638 if qtype
== 'QTYPE_QSTRING':
1639 if isinstance(v
.type, QAPISchemaEnumType
):
1640 for m
in v
.type.members
:
1641 if m
.name
in ['on', 'off']:
1642 conflicting
.add('QTYPE_QBOOL')
1643 if re
.match(r
'[-+0-9.]', m
.name
):
1644 # lazy, could be tightened
1645 conflicting
.add('QTYPE_QNUM')
1647 conflicting
.add('QTYPE_QNUM')
1648 conflicting
.add('QTYPE_QBOOL')
1649 for qt
in conflicting
:
1650 if qt
in types_seen
:
1653 "%s can't be distinguished from '%s'"
1654 % (v
.describe(self
.info
), types_seen
[qt
]))
1655 types_seen
[qt
] = v
.name
1657 self
.doc
.connect_member(v
)
1662 return c_name(self
.name
) + pointer_suffix
1664 def json_type(self
):
1667 def visit(self
, visitor
):
1668 QAPISchemaType
.visit(self
, visitor
)
1669 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
1673 class QAPISchemaCommand(QAPISchemaEntity
):
1676 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
1677 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
1678 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1679 assert not arg_type
or isinstance(arg_type
, str)
1680 assert not ret_type
or isinstance(ret_type
, str)
1681 self
._arg
_type
_name
= arg_type
1682 self
.arg_type
= None
1683 self
._ret
_type
_name
= ret_type
1684 self
.ret_type
= None
1686 self
.success_response
= success_response
1688 self
.allow_oob
= allow_oob
1689 self
.allow_preconfig
= allow_preconfig
1691 def check(self
, schema
):
1692 QAPISchemaEntity
.check(self
, schema
)
1693 if self
._arg
_type
_name
:
1694 self
.arg_type
= schema
.resolve_type(
1695 self
._arg
_type
_name
, self
.info
, "command's 'data'")
1696 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
1699 "command's 'data' cannot take %s"
1700 % self
.arg_type
.describe())
1701 if self
.arg_type
.variants
and not self
.boxed
:
1704 "command's 'data' can take %s only with 'boxed': true"
1705 % self
.arg_type
.describe())
1706 if self
._ret
_type
_name
:
1707 self
.ret_type
= schema
.resolve_type(
1708 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
1709 if self
.name
not in returns_whitelist
:
1710 if not (isinstance(self
.ret_type
, QAPISchemaObjectType
)
1711 or (isinstance(self
.ret_type
, QAPISchemaArrayType
)
1712 and isinstance(self
.ret_type
.element_type
,
1713 QAPISchemaObjectType
))):
1716 "command's 'returns' cannot take %s"
1717 % self
.ret_type
.describe())
1719 def visit(self
, visitor
):
1720 QAPISchemaEntity
.visit(self
, visitor
)
1721 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
1722 self
.arg_type
, self
.ret_type
,
1723 self
.gen
, self
.success_response
,
1724 self
.boxed
, self
.allow_oob
,
1725 self
.allow_preconfig
)
1728 class QAPISchemaEvent(QAPISchemaEntity
):
1731 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
1732 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1733 assert not arg_type
or isinstance(arg_type
, str)
1734 self
._arg
_type
_name
= arg_type
1735 self
.arg_type
= None
1738 def check(self
, schema
):
1739 QAPISchemaEntity
.check(self
, schema
)
1740 if self
._arg
_type
_name
:
1741 self
.arg_type
= schema
.resolve_type(
1742 self
._arg
_type
_name
, self
.info
, "event's 'data'")
1743 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
1746 "event's 'data' cannot take %s"
1747 % self
.arg_type
.describe())
1748 if self
.arg_type
.variants
and not self
.boxed
:
1751 "event's 'data' can take %s only with 'boxed': true"
1752 % self
.arg_type
.describe())
1754 def visit(self
, visitor
):
1755 QAPISchemaEntity
.visit(self
, visitor
)
1756 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
1757 self
.arg_type
, self
.boxed
)
1760 class QAPISchema(object):
1761 def __init__(self
, fname
):
1763 if sys
.version_info
[0] >= 3:
1764 f
= open(fname
, 'r', encoding
='utf-8')
1766 f
= open(fname
, 'r')
1767 parser
= QAPISchemaParser(f
)
1768 exprs
= check_exprs(parser
.exprs
)
1769 self
.docs
= parser
.docs
1770 self
._entity
_list
= []
1771 self
._entity
_dict
= {}
1772 self
._predefining
= True
1773 self
._def
_predefineds
()
1774 self
._predefining
= False
1775 self
._def
_exprs
(exprs
)
1778 def _def_entity(self
, ent
):
1779 # Only the predefined types are allowed to not have info
1780 assert ent
.info
or self
._predefining
1781 self
._entity
_list
.append(ent
)
1782 if ent
.name
is None:
1784 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1785 # because they're liable to clash in generated C.
1786 other_ent
= self
._entity
_dict
.get(ent
.name
)
1789 ent
.info
, "%s is already defined" % other_ent
.describe())
1790 self
._entity
_dict
[ent
.name
] = ent
1792 def lookup_entity(self
, name
, typ
=None):
1793 ent
= self
._entity
_dict
.get(name
)
1794 if typ
and not isinstance(ent
, typ
):
1798 def lookup_type(self
, name
):
1799 return self
.lookup_entity(name
, QAPISchemaType
)
1801 def resolve_type(self
, name
, info
, what
):
1802 typ
= self
.lookup_type(name
)
1807 info
, "%s uses unknown type '%s'" % (what
, name
))
1810 def _def_include(self
, expr
, info
, doc
):
1811 include
= expr
['include']
1814 while main_info
.parent
:
1815 main_info
= main_info
.parent
1816 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
.fname
))
1817 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
1819 def _def_builtin_type(self
, name
, json_type
, c_type
):
1820 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1821 # Instantiating only the arrays that are actually used would
1822 # be nice, but we can't as long as their generated code
1823 # (qapi-builtin-types.[ch]) may be shared by some other
1825 self
._make
_array
_type
(name
, None)
1827 def _def_predefineds(self
):
1828 for t
in [('str', 'string', 'char' + pointer_suffix
),
1829 ('number', 'number', 'double'),
1830 ('int', 'int', 'int64_t'),
1831 ('int8', 'int', 'int8_t'),
1832 ('int16', 'int', 'int16_t'),
1833 ('int32', 'int', 'int32_t'),
1834 ('int64', 'int', 'int64_t'),
1835 ('uint8', 'int', 'uint8_t'),
1836 ('uint16', 'int', 'uint16_t'),
1837 ('uint32', 'int', 'uint32_t'),
1838 ('uint64', 'int', 'uint64_t'),
1839 ('size', 'int', 'uint64_t'),
1840 ('bool', 'boolean', 'bool'),
1841 ('any', 'value', 'QObject' + pointer_suffix
),
1842 ('null', 'null', 'QNull' + pointer_suffix
)]:
1843 self
._def
_builtin
_type
(*t
)
1844 self
.the_empty_object_type
= QAPISchemaObjectType(
1845 'q_empty', None, None, None, None, [], None, [])
1846 self
._def
_entity
(self
.the_empty_object_type
)
1848 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1850 qtype_values
= self
._make
_enum
_members
(
1851 [{'name': n
} for n
in qtypes
], None)
1853 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
1854 qtype_values
, 'QTYPE'))
1856 def _make_features(self
, features
, info
):
1857 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
1860 def _make_enum_members(self
, values
, info
):
1861 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
1864 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
1865 # See also QAPISchemaObjectTypeMember.describe()
1866 name
= name
+ 'Kind' # reserved by check_defn_name_str()
1867 self
._def
_entity
(QAPISchemaEnumType(
1868 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
, info
),
1872 def _make_array_type(self
, element_type
, info
):
1873 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1874 if not self
.lookup_type(name
):
1875 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1878 def _make_implicit_object_type(self
, name
, info
, doc
, ifcond
,
1882 # See also QAPISchemaObjectTypeMember.describe()
1883 name
= 'q_obj_%s-%s' % (name
, role
)
1884 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1886 # The implicit object type has multiple users. This can
1887 # happen only for simple unions' implicit wrapper types.
1888 # Its ifcond should be the disjunction of its user's
1889 # ifconds. Not implemented. Instead, we always pass the
1890 # wrapped type's ifcond, which is trivially the same for all
1891 # users. It's also necessary for the wrapper to compile.
1892 # But it's not tight: the disjunction need not imply it. We
1893 # may end up compiling useless wrapper types.
1894 # TODO kill simple unions or implement the disjunction
1895 assert (ifcond
or []) == typ
._ifcond
# pylint: disable=protected-access
1897 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
,
1898 None, members
, None, []))
1901 def _def_enum_type(self
, expr
, info
, doc
):
1904 prefix
= expr
.get('prefix')
1905 ifcond
= expr
.get('if')
1906 self
._def
_entity
(QAPISchemaEnumType(
1907 name
, info
, doc
, ifcond
,
1908 self
._make
_enum
_members
(data
, info
), prefix
))
1910 def _make_member(self
, name
, typ
, ifcond
, info
):
1912 if name
.startswith('*'):
1915 if isinstance(typ
, list):
1916 assert len(typ
) == 1
1917 typ
= self
._make
_array
_type
(typ
[0], info
)
1918 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
)
1920 def _make_members(self
, data
, info
):
1921 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
1922 for (key
, value
) in data
.items()]
1924 def _def_struct_type(self
, expr
, info
, doc
):
1925 name
= expr
['struct']
1926 base
= expr
.get('base')
1928 ifcond
= expr
.get('if')
1929 features
= expr
.get('features', [])
1930 self
._def
_entity
(QAPISchemaObjectType(
1931 name
, info
, doc
, ifcond
, base
,
1932 self
._make
_members
(data
, info
),
1934 self
._make
_features
(features
, info
)))
1936 def _make_variant(self
, case
, typ
, ifcond
, info
):
1937 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1939 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1940 if isinstance(typ
, list):
1941 assert len(typ
) == 1
1942 typ
= self
._make
_array
_type
(typ
[0], info
)
1943 typ
= self
._make
_implicit
_object
_type
(
1944 typ
, info
, None, self
.lookup_type(typ
),
1945 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
1946 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1948 def _def_union_type(self
, expr
, info
, doc
):
1949 name
= expr
['union']
1951 base
= expr
.get('base')
1952 ifcond
= expr
.get('if')
1953 tag_name
= expr
.get('discriminator')
1955 if isinstance(base
, dict):
1956 base
= self
._make
_implicit
_object
_type
(
1957 name
, info
, doc
, ifcond
,
1958 'base', self
._make
_members
(base
, info
))
1960 variants
= [self
._make
_variant
(key
, value
['type'],
1961 value
.get('if'), info
)
1962 for (key
, value
) in data
.items()]
1965 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1966 value
.get('if'), info
)
1967 for (key
, value
) in data
.items()]
1968 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1969 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1970 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1971 members
= [tag_member
]
1973 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
1974 QAPISchemaObjectTypeVariants(
1975 tag_name
, info
, tag_member
, variants
),
1978 def _def_alternate_type(self
, expr
, info
, doc
):
1979 name
= expr
['alternate']
1981 ifcond
= expr
.get('if')
1982 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1984 for (key
, value
) in data
.items()]
1985 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1987 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
1988 QAPISchemaObjectTypeVariants(
1989 None, info
, tag_member
, variants
)))
1991 def _def_command(self
, expr
, info
, doc
):
1992 name
= expr
['command']
1993 data
= expr
.get('data')
1994 rets
= expr
.get('returns')
1995 gen
= expr
.get('gen', True)
1996 success_response
= expr
.get('success-response', True)
1997 boxed
= expr
.get('boxed', False)
1998 allow_oob
= expr
.get('allow-oob', False)
1999 allow_preconfig
= expr
.get('allow-preconfig', 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 if isinstance(rets
, list):
2005 assert len(rets
) == 1
2006 rets
= self
._make
_array
_type
(rets
[0], info
)
2007 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
2008 gen
, success_response
,
2009 boxed
, allow_oob
, allow_preconfig
))
2011 def _def_event(self
, expr
, info
, doc
):
2012 name
= expr
['event']
2013 data
= expr
.get('data')
2014 boxed
= expr
.get('boxed', False)
2015 ifcond
= expr
.get('if')
2016 if isinstance(data
, OrderedDict
):
2017 data
= self
._make
_implicit
_object
_type
(
2018 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
2019 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
2021 def _def_exprs(self
, exprs
):
2022 for expr_elem
in exprs
:
2023 expr
= expr_elem
['expr']
2024 info
= expr_elem
['info']
2025 doc
= expr_elem
.get('doc')
2027 self
._def
_enum
_type
(expr
, info
, doc
)
2028 elif 'struct' in expr
:
2029 self
._def
_struct
_type
(expr
, info
, doc
)
2030 elif 'union' in expr
:
2031 self
._def
_union
_type
(expr
, info
, doc
)
2032 elif 'alternate' in expr
:
2033 self
._def
_alternate
_type
(expr
, info
, doc
)
2034 elif 'command' in expr
:
2035 self
._def
_command
(expr
, info
, doc
)
2036 elif 'event' in expr
:
2037 self
._def
_event
(expr
, info
, doc
)
2038 elif 'include' in expr
:
2039 self
._def
_include
(expr
, info
, doc
)
2044 for ent
in self
._entity
_list
:
2047 def visit(self
, visitor
):
2048 visitor
.visit_begin(self
)
2050 visitor
.visit_module(module
)
2051 for entity
in self
._entity
_list
:
2052 if visitor
.visit_needed(entity
):
2053 if entity
.module
!= module
:
2054 module
= entity
.module
2055 visitor
.visit_module(module
)
2056 entity
.visit(visitor
)
2061 # Code generation helpers
2064 def camel_case(name
):
2068 if ch
in ['_', '-']:
2071 new_name
+= ch
.upper()
2074 new_name
+= ch
.lower()
2078 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2079 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2080 # ENUM24_Name -> ENUM24_NAME
2081 def camel_to_upper(value
):
2082 c_fun_str
= c_name(value
, False)
2087 length
= len(c_fun_str
)
2088 for i
in range(length
):
2090 # When c is upper and no '_' appears before, do more checks
2091 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
2092 if i
< length
- 1 and c_fun_str
[i
+ 1].islower():
2094 elif c_fun_str
[i
- 1].isdigit():
2097 return new_name
.lstrip('_').upper()
2100 def c_enum_const(type_name
, const_name
, prefix
=None):
2101 if prefix
is not None:
2103 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
2106 if hasattr(str, 'maketrans'):
2107 c_name_trans
= str.maketrans('.-', '__')
2109 c_name_trans
= string
.maketrans('.-', '__')
2112 # Map @name to a valid C identifier.
2113 # If @protect, avoid returning certain ticklish identifiers (like
2114 # C keywords) by prepending 'q_'.
2116 # Used for converting 'name' from a 'name':'type' qapi definition
2117 # into a generated struct member, as well as converting type names
2118 # into substrings of a generated C function name.
2119 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2120 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2121 def c_name(name
, protect
=True):
2122 # ANSI X3J11/88-090, 3.1.1
2123 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
2124 'default', 'do', 'double', 'else', 'enum', 'extern',
2125 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2126 'return', 'short', 'signed', 'sizeof', 'static',
2127 'struct', 'switch', 'typedef', 'union', 'unsigned',
2128 'void', 'volatile', 'while'])
2129 # ISO/IEC 9899:1999, 6.4.1
2130 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2131 # ISO/IEC 9899:2011, 6.4.1
2132 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2133 '_Noreturn', '_Static_assert', '_Thread_local'])
2134 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2136 gcc_words
= set(['asm', 'typeof'])
2137 # C++ ISO/IEC 14882:2003 2.11
2138 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
2139 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2140 'namespace', 'new', 'operator', 'private', 'protected',
2141 'public', 'reinterpret_cast', 'static_cast', 'template',
2142 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2143 'using', 'virtual', 'wchar_t',
2144 # alternative representations
2145 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2146 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2147 # namespace pollution:
2148 polluted_words
= set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2149 name
= name
.translate(c_name_trans
)
2150 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
2151 | cpp_words | polluted_words
):
2156 eatspace
= '\033EATSPACE.'
2157 pointer_suffix
= ' *' + eatspace
2160 def genindent(count
):
2162 for _
in range(count
):
2170 def push_indent(indent_amount
=4):
2172 indent_level
+= indent_amount
2175 def pop_indent(indent_amount
=4):
2177 indent_level
-= indent_amount
2180 # Generate @code with @kwds interpolated.
2181 # Obey indent_level, and strip eatspace.
2182 def cgen(code
, **kwds
):
2185 indent
= genindent(indent_level
)
2186 # re.subn() lacks flags support before Python 2.7, use re.compile()
2187 raw
= re
.subn(re
.compile(r
'^(?!(#|$))', re
.MULTILINE
),
2190 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
2193 def mcgen(code
, **kwds
):
2196 return cgen(code
, **kwds
)
2199 def c_fname(filename
):
2200 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
)
2203 def guardstart(name
):
2209 name
=c_fname(name
).upper())
2215 #endif /* %(name)s */
2217 name
=c_fname(name
).upper())
2229 def gen_endif(ifcond
):
2231 for ifc
in reversed(ifcond
):
2233 #endif /* %(cond)s */
2238 def _wrap_ifcond(ifcond
, before
, after
):
2240 return after
# suppress empty #if ... #endif
2242 assert after
.startswith(before
)
2244 added
= after
[len(before
):]
2245 if added
[0] == '\n':
2248 out
+= gen_if(ifcond
)
2250 out
+= gen_endif(ifcond
)
2254 def gen_enum_lookup(name
, members
, prefix
=None):
2257 const QEnumLookup %(c_name)s_lookup = {
2258 .array = (const char *const[]) {
2260 c_name
=c_name(name
))
2262 ret
+= gen_if(m
.ifcond
)
2263 index
= c_enum_const(name
, m
.name
, prefix
)
2265 [%(index)s] = "%(name)s",
2267 index
=index
, name
=m
.name
)
2268 ret
+= gen_endif(m
.ifcond
)
2272 .size = %(max_index)s
2275 max_index
=c_enum_const(name
, '_MAX', prefix
))
2279 def gen_enum(name
, members
, prefix
=None):
2280 # append automatically generated _MAX value
2281 enum_members
= members
+ [QAPISchemaEnumMember('_MAX', None)]
2285 typedef enum %(c_name)s {
2287 c_name
=c_name(name
))
2289 for m
in enum_members
:
2290 ret
+= gen_if(m
.ifcond
)
2294 c_enum
=c_enum_const(name
, m
.name
, prefix
))
2295 ret
+= gen_endif(m
.ifcond
)
2300 c_name
=c_name(name
))
2304 #define %(c_name)s_str(val) \\
2305 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2307 extern const QEnumLookup %(c_name)s_lookup;
2309 c_name
=c_name(name
))
2313 def build_params(arg_type
, boxed
, extra
=None):
2318 ret
+= '%s arg' % arg_type
.c_param_type()
2321 assert not arg_type
.variants
2322 for memb
in arg_type
.members
:
2326 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
2327 ret
+= '%s %s' % (memb
.type.c_param_type(),
2331 return ret
if ret
else 'void'
2335 # Accumulate and write output
2338 class QAPIGen(object):
2340 def __init__(self
, fname
):
2345 def preamble_add(self
, text
):
2346 self
._preamble
+= text
2348 def add(self
, text
):
2351 def get_content(self
):
2352 return self
._top
() + self
._preamble
+ self
._body
+ self
._bottom
()
2360 def write(self
, output_dir
):
2361 pathname
= os
.path
.join(output_dir
, self
.fname
)
2362 dir = os
.path
.dirname(pathname
)
2366 except os
.error
as e
:
2367 if e
.errno
!= errno
.EEXIST
:
2369 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2370 if sys
.version_info
[0] >= 3:
2371 f
= open(fd
, 'r+', encoding
='utf-8')
2373 f
= os
.fdopen(fd
, 'r+')
2374 text
= self
.get_content()
2375 oldtext
= f
.read(len(text
) + 1)
2384 def ifcontext(ifcond
, *args
):
2385 """A 'with' statement context manager to wrap with start_if()/end_if()
2387 *args: any number of QAPIGenCCode
2391 with ifcontext(ifcond, self._genh, self._genc):
2392 modify self._genh and self._genc ...
2394 Is equivalent to calling::
2396 self._genh.start_if(ifcond)
2397 self._genc.start_if(ifcond)
2398 modify self._genh and self._genc ...
2403 arg
.start_if(ifcond
)
2409 class QAPIGenCCode(QAPIGen
):
2411 def __init__(self
, fname
):
2412 QAPIGen
.__init
__(self
, fname
)
2413 self
._start
_if
= None
2415 def start_if(self
, ifcond
):
2416 assert self
._start
_if
is None
2417 self
._start
_if
= (ifcond
, self
._body
, self
._preamble
)
2420 assert self
._start
_if
2422 self
._start
_if
= None
2424 def _wrap_ifcond(self
):
2425 self
._body
= _wrap_ifcond(self
._start
_if
[0],
2426 self
._start
_if
[1], self
._body
)
2427 self
._preamble
= _wrap_ifcond(self
._start
_if
[0],
2428 self
._start
_if
[2], self
._preamble
)
2430 def get_content(self
):
2431 assert self
._start
_if
is None
2432 return QAPIGen
.get_content(self
)
2435 class QAPIGenC(QAPIGenCCode
):
2437 def __init__(self
, fname
, blurb
, pydoc
):
2438 QAPIGenCCode
.__init
__(self
, fname
)
2440 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2445 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2452 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2453 * See the COPYING.LIB file in the top-level directory.
2457 blurb
=self
._blurb
, copyright
=self
._copyright
)
2462 /* Dummy declaration to prevent empty .o file */
2463 char qapi_dummy_%(name)s;
2465 name
=c_fname(self
.fname
))
2468 class QAPIGenH(QAPIGenC
):
2471 return QAPIGenC
._top
(self
) + guardstart(self
.fname
)
2474 return guardend(self
.fname
)
2477 class QAPIGenDoc(QAPIGen
):
2480 return (QAPIGen
._top
(self
)
2481 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2484 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2486 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2487 self
._prefix
= prefix
2489 self
._genc
= QAPIGenC(self
._prefix
+ self
._what
+ '.c',
2491 self
._genh
= QAPIGenH(self
._prefix
+ self
._what
+ '.h',
2494 def write(self
, output_dir
):
2495 self
._genc
.write(output_dir
)
2496 self
._genh
.write(output_dir
)
2499 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2501 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2502 self
._prefix
= prefix
2509 self
._main
_module
= None
2512 def _is_user_module(name
):
2513 return name
and not name
.startswith('./')
2516 def _is_builtin_module(name
):
2519 def _module_dirname(self
, what
, name
):
2520 if self
._is
_user
_module
(name
):
2521 return os
.path
.dirname(name
)
2524 def _module_basename(self
, what
, name
):
2525 ret
= '' if self
._is
_builtin
_module
(name
) else self
._prefix
2526 if self
._is
_user
_module
(name
):
2527 basename
= os
.path
.basename(name
)
2529 if name
!= self
._main
_module
:
2530 ret
+= '-' + os
.path
.splitext(basename
)[0]
2532 name
= name
[2:] if name
else 'builtin'
2533 ret
+= re
.sub(r
'-', '-' + name
+ '-', what
)
2536 def _module_filename(self
, what
, name
):
2537 return os
.path
.join(self
._module
_dirname
(what
, name
),
2538 self
._module
_basename
(what
, name
))
2540 def _add_module(self
, name
, blurb
):
2541 basename
= self
._module
_filename
(self
._what
, name
)
2542 genc
= QAPIGenC(basename
+ '.c', blurb
, self
._pydoc
)
2543 genh
= QAPIGenH(basename
+ '.h', blurb
, self
._pydoc
)
2544 self
._module
[name
] = (genc
, genh
)
2545 self
._set
_module
(name
)
2547 def _add_user_module(self
, name
, blurb
):
2548 assert self
._is
_user
_module
(name
)
2549 if self
._main
_module
is None:
2550 self
._main
_module
= name
2551 self
._add
_module
(name
, blurb
)
2553 def _add_system_module(self
, name
, blurb
):
2554 self
._add
_module
(name
and './' + name
, blurb
)
2556 def _set_module(self
, name
):
2557 self
._genc
, self
._genh
= self
._module
[name
]
2559 def write(self
, output_dir
, opt_builtins
=False):
2560 for name
in self
._module
:
2561 if self
._is
_builtin
_module
(name
) and not opt_builtins
:
2563 (genc
, genh
) = self
._module
[name
]
2564 genc
.write(output_dir
)
2565 genh
.write(output_dir
)
2567 def _begin_user_module(self
, name
):
2570 def visit_module(self
, name
):
2571 if name
in self
._module
:
2572 self
._set
_module
(name
)
2573 elif self
._is
_builtin
_module
(name
):
2574 # The built-in module has not been created. No code may
2579 self
._add
_user
_module
(name
, self
._blurb
)
2580 self
._begin
_user
_module
(name
)
2582 def visit_include(self
, name
, info
):
2583 relname
= os
.path
.relpath(self
._module
_filename
(self
._what
, name
),
2584 os
.path
.dirname(self
._genh
.fname
))
2585 self
._genh
.preamble_add(mcgen('''
2586 #include "%(relname)s.h"