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
26 # Parsing the schema into expressions
30 class QAPISchemaPragma(object):
32 # Are documentation comments required?
33 self
.doc_required
= False
34 # Whitelist of commands allowed to return a non-dictionary
35 self
.returns_whitelist
= []
36 # Whitelist of entities allowed to violate case conventions
37 self
.name_case_whitelist
= []
40 class QAPISourceInfo(object):
41 def __init__(self
, fname
, line
, parent
):
45 self
.pragma
= parent
.pragma
if parent
else QAPISchemaPragma()
49 def set_defn(self
, meta
, name
):
54 info
= copy
.copy(self
)
59 if self
.fname
is None:
62 if self
.line
is not None:
63 ret
+= ':%d' % self
.line
68 return "%s: In %s '%s':\n" % (self
.fname
,
69 self
.defn_meta
, self
.defn_name
)
72 def include_path(self
):
76 ret
= 'In file included from %s:\n' % parent
.loc() + ret
77 parent
= parent
.parent
81 return self
.include_path() + self
.in_defn() + self
.loc()
84 class QAPIError(Exception):
85 def __init__(self
, info
, col
, msg
):
86 Exception.__init
__(self
)
93 if self
.col
is not None:
94 assert self
.info
.line
is not None
95 loc
+= ':%s' % self
.col
96 return loc
+ ': ' + self
.msg
99 class QAPIParseError(QAPIError
):
100 def __init__(self
, parser
, msg
):
102 for ch
in parser
.src
[parser
.line_pos
:parser
.pos
]:
104 col
= (col
+ 7) % 8 + 1
107 QAPIError
.__init
__(self
, parser
.info
, col
, msg
)
110 class QAPISemError(QAPIError
):
111 def __init__(self
, info
, msg
):
112 QAPIError
.__init
__(self
, info
, None, msg
)
115 class QAPIDoc(object):
117 A documentation comment block, either definition or free-form
119 Definition documentation blocks consist of
121 * a body section: one line naming the definition, followed by an
122 overview (any number of lines)
124 * argument sections: a description of each argument (for commands
125 and events) or member (for structs, unions and alternates)
127 * features sections: a description of each feature flag
129 * additional (non-argument) sections, possibly tagged
131 Free-form documentation blocks consist only of a body section.
134 class Section(object):
135 def __init__(self
, name
=None):
136 # optional section name (argument/member or section name)
138 # the list of lines for this section
141 def append(self
, line
):
142 self
.text
+= line
.rstrip() + '\n'
144 class ArgSection(Section
):
145 def __init__(self
, name
):
146 QAPIDoc
.Section
.__init
__(self
, name
)
149 def connect(self
, member
):
152 def __init__(self
, parser
, info
):
153 # self._parser is used to report errors with QAPIParseError. The
154 # resulting error position depends on the state of the parser.
155 # It happens to be the beginning of the comment. More or less
156 # servicable, but action at a distance.
157 self
._parser
= parser
160 self
.body
= QAPIDoc
.Section()
161 # dict mapping parameter name to ArgSection
162 self
.args
= OrderedDict()
163 self
.features
= OrderedDict()
166 # the current section
167 self
._section
= self
.body
168 self
._append
_line
= self
._append
_body
_line
170 def has_section(self
, name
):
171 """Return True if we have a section with this name."""
172 for i
in self
.sections
:
177 def append(self
, line
):
179 Parse a comment line and add it to the documentation.
181 The way that the line is dealt with depends on which part of
182 the documentation we're parsing right now:
183 * The body section: ._append_line is ._append_body_line
184 * An argument section: ._append_line is ._append_args_line
185 * A features section: ._append_line is ._append_features_line
186 * An additional section: ._append_line is ._append_various_line
190 self
._append
_freeform
(line
)
194 raise QAPIParseError(self
._parser
, "missing space after #")
196 self
._append
_line
(line
)
198 def end_comment(self
):
202 def _is_section_tag(name
):
203 return name
in ('Returns:', 'Since:',
204 # those are often singular or plural
206 'Example:', 'Examples:',
209 def _append_body_line(self
, line
):
211 Process a line of documentation text in the body section.
213 If this a symbol line and it is the section's first line, this
214 is a definition documentation block for that symbol.
216 If it's a definition documentation block, another symbol line
217 begins the argument section for the argument named by it, and
218 a section tag begins an additional section. Start that
219 section and append the line to it.
221 Else, append the line to the current section.
223 name
= line
.split(' ', 1)[0]
224 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
225 # recognized, and get silently treated as ordinary text
226 if not self
.symbol
and not self
.body
.text
and line
.startswith('@'):
227 if not line
.endswith(':'):
228 raise QAPIParseError(self
._parser
, "line should end with ':'")
229 self
.symbol
= line
[1:-1]
230 # FIXME invalid names other than the empty string aren't flagged
232 raise QAPIParseError(self
._parser
, "invalid name")
234 # This is a definition documentation block
235 if name
.startswith('@') and name
.endswith(':'):
236 self
._append
_line
= self
._append
_args
_line
237 self
._append
_args
_line
(line
)
238 elif line
== 'Features:':
239 self
._append
_line
= self
._append
_features
_line
240 elif self
._is
_section
_tag
(name
):
241 self
._append
_line
= self
._append
_various
_line
242 self
._append
_various
_line
(line
)
244 self
._append
_freeform
(line
.strip())
246 # This is a free-form documentation block
247 self
._append
_freeform
(line
.strip())
249 def _append_args_line(self
, line
):
251 Process a line of documentation text in an argument section.
253 A symbol line begins the next argument section, a section tag
254 section or a non-indented line after a blank line begins an
255 additional section. Start that section and append the line to
258 Else, append the line to the current section.
261 name
= line
.split(' ', 1)[0]
263 if name
.startswith('@') and name
.endswith(':'):
264 line
= line
[len(name
)+1:]
265 self
._start
_args
_section
(name
[1:-1])
266 elif self
._is
_section
_tag
(name
):
267 self
._append
_line
= self
._append
_various
_line
268 self
._append
_various
_line
(line
)
270 elif (self
._section
.text
.endswith('\n\n')
271 and line
and not line
[0].isspace()):
272 if line
== 'Features:':
273 self
._append
_line
= self
._append
_features
_line
275 self
._start
_section
()
276 self
._append
_line
= self
._append
_various
_line
277 self
._append
_various
_line
(line
)
280 self
._append
_freeform
(line
.strip())
282 def _append_features_line(self
, line
):
283 name
= line
.split(' ', 1)[0]
285 if name
.startswith('@') and name
.endswith(':'):
286 line
= line
[len(name
)+1:]
287 self
._start
_features
_section
(name
[1:-1])
288 elif self
._is
_section
_tag
(name
):
289 self
._append
_line
= self
._append
_various
_line
290 self
._append
_various
_line
(line
)
292 elif (self
._section
.text
.endswith('\n\n')
293 and line
and not line
[0].isspace()):
294 self
._start
_section
()
295 self
._append
_line
= self
._append
_various
_line
296 self
._append
_various
_line
(line
)
299 self
._append
_freeform
(line
.strip())
301 def _append_various_line(self
, line
):
303 Process a line of documentation text in an additional section.
305 A symbol line is an error.
307 A section tag begins an additional section. Start that
308 section and append the line to it.
310 Else, append the line to the current section.
312 name
= line
.split(' ', 1)[0]
314 if name
.startswith('@') and name
.endswith(':'):
315 raise QAPIParseError(self
._parser
,
316 "'%s' can't follow '%s' section"
317 % (name
, self
.sections
[0].name
))
318 elif self
._is
_section
_tag
(name
):
319 line
= line
[len(name
)+1:]
320 self
._start
_section
(name
[:-1])
322 if (not self
._section
.name
or
323 not self
._section
.name
.startswith('Example')):
326 self
._append
_freeform
(line
)
328 def _start_symbol_section(self
, symbols_dict
, name
):
329 # FIXME invalid names other than the empty string aren't flagged
331 raise QAPIParseError(self
._parser
, "invalid parameter name")
332 if name
in symbols_dict
:
333 raise QAPIParseError(self
._parser
,
334 "'%s' parameter name duplicated" % name
)
335 assert not self
.sections
337 self
._section
= QAPIDoc
.ArgSection(name
)
338 symbols_dict
[name
] = self
._section
340 def _start_args_section(self
, name
):
341 self
._start
_symbol
_section
(self
.args
, name
)
343 def _start_features_section(self
, name
):
344 self
._start
_symbol
_section
(self
.features
, name
)
346 def _start_section(self
, name
=None):
347 if name
in ('Returns', 'Since') and self
.has_section(name
):
348 raise QAPIParseError(self
._parser
,
349 "duplicated '%s' section" % name
)
351 self
._section
= QAPIDoc
.Section(name
)
352 self
.sections
.append(self
._section
)
354 def _end_section(self
):
356 text
= self
._section
.text
= self
._section
.text
.strip()
357 if self
._section
.name
and (not text
or text
.isspace()):
358 raise QAPIParseError(
360 "empty doc section '%s'" % self
._section
.name
)
363 def _append_freeform(self
, line
):
364 match
= re
.match(r
'(@\S+:)', line
)
366 raise QAPIParseError(self
._parser
,
367 "'%s' not allowed in free-form documentation"
369 self
._section
.append(line
)
371 def connect_member(self
, member
):
372 if member
.name
not in self
.args
:
373 # Undocumented TODO outlaw
374 self
.args
[member
.name
] = QAPIDoc
.ArgSection(member
.name
)
375 self
.args
[member
.name
].connect(member
)
377 def check_expr(self
, expr
):
378 if self
.has_section('Returns') and 'command' not in expr
:
379 raise QAPISemError(self
.info
,
380 "'Returns:' is only valid for commands")
383 bogus
= [name
for name
, section
in self
.args
.items()
384 if not section
.member
]
388 "the following documented members are not in "
389 "the declaration: %s" % ", ".join(bogus
))
392 class QAPISchemaParser(object):
394 def __init__(self
, fname
, previously_included
=None, incl_info
=None):
395 previously_included
= previously_included
or set()
396 previously_included
.add(os
.path
.abspath(fname
))
399 if sys
.version_info
[0] >= 3:
400 fp
= open(fname
, 'r', encoding
='utf-8')
402 fp
= open(fname
, 'r')
405 raise QAPISemError(incl_info
or QAPISourceInfo(None, None, None),
406 "can't read %s file '%s': %s"
407 % ("include" if incl_info
else "schema",
411 if self
.src
== '' or self
.src
[-1] != '\n':
414 self
.info
= QAPISourceInfo(fname
, 1, incl_info
)
421 while self
.tok
is not None:
424 self
.reject_expr_doc(cur_doc
)
425 cur_doc
= self
.get_doc(info
)
426 self
.docs
.append(cur_doc
)
429 expr
= self
.get_expr(False)
430 if 'include' in expr
:
431 self
.reject_expr_doc(cur_doc
)
433 raise QAPISemError(info
, "invalid 'include' directive")
434 include
= expr
['include']
435 if not isinstance(include
, str):
436 raise QAPISemError(info
,
437 "value of 'include' must be a string")
438 incl_fname
= os
.path
.join(os
.path
.dirname(fname
),
440 self
.exprs
.append({'expr': {'include': incl_fname
},
442 exprs_include
= self
._include
(include
, info
, incl_fname
,
445 self
.exprs
.extend(exprs_include
.exprs
)
446 self
.docs
.extend(exprs_include
.docs
)
447 elif "pragma" in expr
:
448 self
.reject_expr_doc(cur_doc
)
450 raise QAPISemError(info
, "invalid 'pragma' directive")
451 pragma
= expr
['pragma']
452 if not isinstance(pragma
, dict):
454 info
, "value of 'pragma' must be an object")
455 for name
, value
in pragma
.items():
456 self
._pragma
(name
, value
, info
)
458 expr_elem
= {'expr': expr
,
461 if not cur_doc
.symbol
:
463 cur_doc
.info
, "definition documentation required")
464 expr_elem
['doc'] = cur_doc
465 self
.exprs
.append(expr_elem
)
467 self
.reject_expr_doc(cur_doc
)
470 def reject_expr_doc(doc
):
471 if doc
and doc
.symbol
:
474 "documentation for '%s' is not followed by the definition"
477 def _include(self
, include
, info
, incl_fname
, previously_included
):
478 incl_abs_fname
= os
.path
.abspath(incl_fname
)
479 # catch inclusion cycle
482 if incl_abs_fname
== os
.path
.abspath(inf
.fname
):
483 raise QAPISemError(info
, "inclusion loop for %s" % include
)
486 # skip multiple include of the same file
487 if incl_abs_fname
in previously_included
:
490 return QAPISchemaParser(incl_fname
, previously_included
, info
)
492 def _pragma(self
, name
, value
, info
):
493 if name
== 'doc-required':
494 if not isinstance(value
, bool):
495 raise QAPISemError(info
,
496 "pragma 'doc-required' must be boolean")
497 info
.pragma
.doc_required
= value
498 elif name
== 'returns-whitelist':
499 if (not isinstance(value
, list)
500 or any([not isinstance(elt
, str) for elt
in value
])):
503 "pragma returns-whitelist must be a list of strings")
504 info
.pragma
.returns_whitelist
= value
505 elif name
== 'name-case-whitelist':
506 if (not isinstance(value
, list)
507 or any([not isinstance(elt
, str) for elt
in value
])):
510 "pragma name-case-whitelist must be a list of strings")
511 info
.pragma
.name_case_whitelist
= value
513 raise QAPISemError(info
, "unknown pragma '%s'" % name
)
515 def accept(self
, skip_comment
=True):
517 self
.tok
= self
.src
[self
.cursor
]
518 self
.pos
= self
.cursor
523 if self
.src
[self
.cursor
] == '#':
524 # Start of doc comment
526 self
.cursor
= self
.src
.find('\n', self
.cursor
)
528 self
.val
= self
.src
[self
.pos
:self
.cursor
]
530 elif self
.tok
in '{}:,[]':
532 elif self
.tok
== "'":
533 # Note: we accept only printable ASCII
537 ch
= self
.src
[self
.cursor
]
540 raise QAPIParseError(self
, "missing terminating \"'\"")
542 # Note: we recognize only \\ because we have
543 # no use for funny characters in strings
545 raise QAPIParseError(self
,
546 "unknown escape \\%s" % ch
)
554 if ord(ch
) < 32 or ord(ch
) >= 127:
555 raise QAPIParseError(
556 self
, "funny character in string")
558 elif self
.src
.startswith('true', self
.pos
):
562 elif self
.src
.startswith('false', self
.pos
):
566 elif self
.tok
== '\n':
567 if self
.cursor
== len(self
.src
):
570 self
.info
= self
.info
.next_line()
571 self
.line_pos
= self
.cursor
572 elif not self
.tok
.isspace():
573 # Show up to next structural, whitespace or quote
575 match
= re
.match('[^[\\]{}:,\\s\'"]+',
576 self
.src
[self
.cursor
-1:])
577 raise QAPIParseError(self
, "stray '%s'" % match
.group(0))
579 def get_members(self
):
585 raise QAPIParseError(self
, "expected string or '}'")
590 raise QAPIParseError(self
, "expected ':'")
593 raise QAPIParseError(self
, "duplicate key '%s'" % key
)
594 expr
[key
] = self
.get_expr(True)
599 raise QAPIParseError(self
, "expected ',' or '}'")
602 raise QAPIParseError(self
, "expected string")
604 def get_values(self
):
609 if self
.tok
not in "{['tfn":
610 raise QAPIParseError(
611 self
, "expected '{', '[', ']', string, boolean or 'null'")
613 expr
.append(self
.get_expr(True))
618 raise QAPIParseError(self
, "expected ',' or ']'")
621 def get_expr(self
, nested
):
622 if self
.tok
!= '{' and not nested
:
623 raise QAPIParseError(self
, "expected '{'")
626 expr
= self
.get_members()
627 elif self
.tok
== '[':
629 expr
= self
.get_values()
630 elif self
.tok
in "'tfn":
634 raise QAPIParseError(
635 self
, "expected '{', '[', string, boolean or 'null'")
638 def get_doc(self
, info
):
640 raise QAPIParseError(
641 self
, "junk after '##' at start of documentation comment")
643 doc
= QAPIDoc(self
, info
)
645 while self
.tok
== '#':
646 if self
.val
.startswith('##'):
649 raise QAPIParseError(
651 "junk after '##' at end of documentation comment")
659 raise QAPIParseError(self
, "documentation comment must end with '##'")
663 # Check (context-free) schema expression structure
666 # Names must be letters, numbers, -, and _. They must start with letter,
667 # except for downstream extensions which must start with __RFQDN_.
668 # Dots are only valid in the downstream extension prefix.
669 valid_name
= re
.compile(r
'^(__[a-zA-Z0-9.-]+_)?'
670 '[a-zA-Z][a-zA-Z0-9_-]*$')
673 def check_name_is_str(name
, info
, source
):
674 if not isinstance(name
, str):
675 raise QAPISemError(info
, "%s requires a string name" % source
)
678 def check_name_str(name
, info
, source
,
679 allow_optional
=False, enum_member
=False,
684 if allow_optional
and name
.startswith('*'):
685 membername
= name
[1:]
686 # Enum members can start with a digit, because the generated C
687 # code always prefixes it with the enum name
688 if enum_member
and membername
[0].isdigit():
689 membername
= 'D' + membername
690 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
691 # and 'q_obj_*' implicit type names.
692 if not valid_name
.match(membername
) or \
693 c_name(membername
, False).startswith('q_'):
694 raise QAPISemError(info
, "%s has an invalid name" % source
)
695 if not permit_upper
and name
.lower() != name
:
697 info
, "%s uses uppercase in name" % source
)
698 assert not membername
.startswith('*')
701 def check_defn_name_str(name
, info
, meta
):
702 check_name_str(name
, info
, meta
, permit_upper
=True)
703 if name
.endswith('Kind') or name
.endswith('List'):
705 info
, "%s name should not end in '%s'" % (meta
, name
[-4:]))
708 def check_if(expr
, info
, source
):
710 def check_if_str(ifcond
, info
):
711 if not isinstance(ifcond
, str):
714 "'if' condition of %s must be a string or a list of strings"
716 if ifcond
.strip() == '':
719 "'if' condition '%s' of %s makes no sense"
722 ifcond
= expr
.get('if')
725 if isinstance(ifcond
, list):
728 info
, "'if' condition [] of %s is useless" % source
)
730 check_if_str(elt
, info
)
732 check_if_str(ifcond
, info
)
735 def check_type(value
, info
, source
,
736 allow_array
=False, allow_dict
=False):
741 if isinstance(value
, list):
743 raise QAPISemError(info
, "%s cannot be an array" % source
)
744 if len(value
) != 1 or not isinstance(value
[0], str):
745 raise QAPISemError(info
,
746 "%s: array type must contain single type name" %
751 if isinstance(value
, str):
757 raise QAPISemError(info
, "%s should be a type name" % source
)
759 if not isinstance(value
, OrderedDict
):
760 raise QAPISemError(info
,
761 "%s should be an object or type name" % source
)
763 permit_upper
= allow_dict
in info
.pragma
.name_case_whitelist
765 # value is a dictionary, check that each member is okay
766 for (key
, arg
) in value
.items():
767 key_source
= "%s member '%s'" % (source
, key
)
768 check_name_str(key
, info
, key_source
,
769 allow_optional
=True, permit_upper
=permit_upper
)
770 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
771 raise QAPISemError(info
, "%s uses reserved name" % key_source
)
772 check_keys(arg
, info
, key_source
, ['type'], ['if'])
773 check_if(arg
, info
, key_source
)
775 check_type(arg
['type'], info
, key_source
, allow_array
=True)
778 def check_command(expr
, info
):
779 args
= expr
.get('data')
780 rets
= expr
.get('returns')
781 boxed
= expr
.get('boxed', False)
783 if boxed
and args
is None:
784 raise QAPISemError(info
, "'boxed': true requires 'data'")
785 check_type(args
, info
, "'data'", allow_dict
=not boxed
)
786 check_type(rets
, info
, "'returns'", allow_array
=True)
789 def check_event(expr
, info
):
790 args
= expr
.get('data')
791 boxed
= expr
.get('boxed', False)
793 if boxed
and args
is None:
794 raise QAPISemError(info
, "'boxed': true requires 'data'")
795 check_type(args
, info
, "'data'", allow_dict
=not boxed
)
798 def check_union(expr
, info
):
800 base
= expr
.get('base')
801 discriminator
= expr
.get('discriminator')
802 members
= expr
['data']
804 if discriminator
is None: # simple union
806 raise QAPISemError(info
, "'base' requires 'discriminator'")
808 check_type(base
, info
, "'base'", allow_dict
=name
)
810 raise QAPISemError(info
, "'discriminator' requires 'base'")
811 check_name_is_str(discriminator
, info
, "'discriminator'")
813 for (key
, value
) in members
.items():
814 source
= "'data' member '%s'" % key
815 check_name_str(key
, info
, source
)
816 check_keys(value
, info
, source
, ['type'], ['if'])
817 check_if(value
, info
, source
)
819 check_type(value
['type'], info
, source
, allow_array
=not base
)
822 def check_alternate(expr
, info
):
823 members
= expr
['data']
825 if len(members
) == 0:
826 raise QAPISemError(info
, "'data' must not be empty")
827 for (key
, value
) in members
.items():
828 source
= "'data' member '%s'" % key
829 check_name_str(key
, info
, source
)
830 check_keys(value
, info
, source
, ['type'], ['if'])
831 check_if(value
, info
, source
)
833 check_type(value
['type'], info
, source
)
836 def check_enum(expr
, info
):
838 members
= expr
['data']
839 prefix
= expr
.get('prefix')
841 if not isinstance(members
, list):
842 raise QAPISemError(info
, "'data' must be an array")
843 if prefix
is not None and not isinstance(prefix
, str):
844 raise QAPISemError(info
, "'prefix' must be a string")
846 permit_upper
= name
in info
.pragma
.name_case_whitelist
848 for member
in members
:
849 source
= "'data' member"
850 check_keys(member
, info
, source
, ['name'], ['if'])
851 check_name_is_str(member
['name'], info
, source
)
852 source
= "%s '%s'" % (source
, member
['name'])
853 check_name_str(member
['name'], info
, source
,
854 enum_member
=True, permit_upper
=permit_upper
)
855 check_if(member
, info
, source
)
859 def check_struct(expr
, info
):
860 name
= expr
['struct']
861 members
= expr
['data']
862 features
= expr
.get('features')
864 check_type(members
, info
, "'data'", allow_dict
=name
)
865 check_type(expr
.get('base'), info
, "'base'")
868 if not isinstance(features
, list):
869 raise QAPISemError(info
, "'features' must be an array")
871 source
= "'features' member"
872 assert isinstance(f
, dict)
873 check_keys(f
, info
, source
, ['name'], ['if'])
874 check_name_is_str(f
['name'], info
, source
)
875 source
= "%s '%s'" % (source
, f
['name'])
876 check_name_str(f
['name'], info
, source
)
877 check_if(f
, info
, source
)
881 def check_keys(value
, info
, source
, required
, optional
):
884 return ', '.join("'" + e
+ "'" for e
in sorted(elems
))
886 missing
= set(required
) - set(value
)
891 % (source
, 's' if len(missing
) > 1 else '',
893 allowed
= set(required
+ optional
)
894 unknown
= set(value
) - allowed
898 "%s has unknown key%s %s\nValid keys are %s."
899 % (source
, 's' if len(unknown
) > 1 else '',
900 pprint(unknown
), pprint(allowed
)))
903 def check_flags(expr
, info
):
904 for key
in ['gen', 'success-response']:
905 if key
in expr
and expr
[key
] is not False:
907 info
, "flag '%s' may only use false value" % key
)
908 for key
in ['boxed', 'allow-oob', 'allow-preconfig']:
909 if key
in expr
and expr
[key
] is not True:
911 info
, "flag '%s' may only use true value" % key
)
914 def normalize_enum(expr
):
915 if isinstance(expr
['data'], list):
916 expr
['data'] = [m
if isinstance(m
, dict) else {'name': m
}
917 for m
in expr
['data']]
920 def normalize_members(members
):
921 if isinstance(members
, OrderedDict
):
922 for key
, arg
in members
.items():
923 if isinstance(arg
, dict):
925 members
[key
] = {'type': arg
}
928 def normalize_features(features
):
929 if isinstance(features
, list):
930 features
[:] = [f
if isinstance(f
, dict) else {'name': f
}
934 def normalize_if(expr
):
935 ifcond
= expr
.get('if')
936 if isinstance(ifcond
, str):
937 expr
['if'] = [ifcond
]
940 def check_exprs(exprs
):
941 for expr_elem
in exprs
:
942 expr
= expr_elem
['expr']
943 info
= expr_elem
['info']
944 doc
= expr_elem
.get('doc')
946 if 'include' in expr
:
951 elif 'union' in expr
:
953 elif 'alternate' in expr
:
955 elif 'struct' in expr
:
957 elif 'command' in expr
:
959 elif 'event' in expr
:
962 raise QAPISemError(info
, "expression is missing metatype")
965 check_name_is_str(name
, info
, "'%s'" % meta
)
966 info
.set_defn(meta
, name
)
967 check_defn_name_str(name
, info
, meta
)
970 if doc
.symbol
!= name
:
972 info
, "documentation comment is for '%s'" % doc
.symbol
)
974 elif info
.pragma
.doc_required
:
975 raise QAPISemError(info
,
976 "documentation comment required")
979 check_keys(expr
, info
, meta
,
980 ['enum', 'data'], ['if', 'prefix'])
982 check_enum(expr
, info
)
983 elif meta
== 'union':
984 check_keys(expr
, info
, meta
,
986 ['base', 'discriminator', 'if'])
987 normalize_members(expr
.get('base'))
988 normalize_members(expr
['data'])
989 check_union(expr
, info
)
990 elif meta
== 'alternate':
991 check_keys(expr
, info
, meta
,
992 ['alternate', 'data'], ['if'])
993 normalize_members(expr
['data'])
994 check_alternate(expr
, info
)
995 elif meta
== 'struct':
996 check_keys(expr
, info
, meta
,
997 ['struct', 'data'], ['base', 'if', 'features'])
998 normalize_members(expr
['data'])
999 normalize_features(expr
.get('features'))
1000 check_struct(expr
, info
)
1001 elif meta
== 'command':
1002 check_keys(expr
, info
, meta
,
1004 ['data', 'returns', 'boxed', 'if',
1005 'gen', 'success-response', 'allow-oob',
1007 normalize_members(expr
.get('data'))
1008 check_command(expr
, info
)
1009 elif meta
== 'event':
1010 check_keys(expr
, info
, meta
,
1011 ['event'], ['data', 'boxed', 'if'])
1012 normalize_members(expr
.get('data'))
1013 check_event(expr
, info
)
1015 assert False, 'unexpected meta type'
1018 check_if(expr
, info
, meta
)
1019 check_flags(expr
, info
)
1025 # Schema compiler frontend
1026 # TODO catching name collisions in generated code would be nice
1029 class QAPISchemaEntity(object):
1032 def __init__(self
, name
, info
, doc
, ifcond
=None):
1033 assert name
is None or isinstance(name
, str)
1036 # For explicitly defined entities, info points to the (explicit)
1037 # definition. For builtins (and their arrays), info is None.
1038 # For implicitly defined entities, info points to a place that
1039 # triggered the implicit definition (there may be more than one
1043 self
._ifcond
= ifcond
or []
1044 self
._checked
= False
1047 return c_name(self
.name
)
1049 def check(self
, schema
):
1050 assert not self
._checked
1052 self
._module
= os
.path
.relpath(self
.info
.fname
,
1053 os
.path
.dirname(schema
.fname
))
1054 self
._checked
= True
1058 assert self
._checked
1063 assert self
._checked
1066 def is_implicit(self
):
1067 return not self
.info
1069 def visit(self
, visitor
):
1070 assert self
._checked
1074 return "%s '%s'" % (self
.meta
, self
.name
)
1077 class QAPISchemaVisitor(object):
1078 def visit_begin(self
, schema
):
1081 def visit_end(self
):
1084 def visit_module(self
, fname
):
1087 def visit_needed(self
, entity
):
1088 # Default to visiting everything
1091 def visit_include(self
, fname
, info
):
1094 def visit_builtin_type(self
, name
, info
, json_type
):
1097 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
1100 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
1103 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
,
1107 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
1111 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
1114 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
1115 success_response
, boxed
, allow_oob
, allow_preconfig
):
1118 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
1122 class QAPISchemaInclude(QAPISchemaEntity
):
1124 def __init__(self
, fname
, info
):
1125 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1128 def visit(self
, visitor
):
1129 QAPISchemaEntity
.visit(self
, visitor
)
1130 visitor
.visit_include(self
.fname
, self
.info
)
1133 class QAPISchemaType(QAPISchemaEntity
):
1134 # Return the C type for common use.
1135 # For the types we commonly box, this is a pointer type.
1139 # Return the C type to be used in a parameter list.
1140 def c_param_type(self
):
1141 return self
.c_type()
1143 # Return the C type to be used where we suppress boxing.
1144 def c_unboxed_type(self
):
1145 return self
.c_type()
1147 def json_type(self
):
1150 def alternate_qtype(self
):
1152 'null': 'QTYPE_QNULL',
1153 'string': 'QTYPE_QSTRING',
1154 'number': 'QTYPE_QNUM',
1155 'int': 'QTYPE_QNUM',
1156 'boolean': 'QTYPE_QBOOL',
1157 'object': 'QTYPE_QDICT'
1159 return json2qtype
.get(self
.json_type())
1162 if self
.is_implicit():
1168 return "%s type '%s'" % (self
.meta
, self
.name
)
1171 class QAPISchemaBuiltinType(QAPISchemaType
):
1174 def __init__(self
, name
, json_type
, c_type
):
1175 QAPISchemaType
.__init
__(self
, name
, None, None)
1176 assert not c_type
or isinstance(c_type
, str)
1177 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1179 self
._json
_type
_name
= json_type
1180 self
._c
_type
_name
= c_type
1186 return self
._c
_type
_name
1188 def c_param_type(self
):
1189 if self
.name
== 'str':
1190 return 'const ' + self
._c
_type
_name
1191 return self
._c
_type
_name
1193 def json_type(self
):
1194 return self
._json
_type
_name
1197 return self
.json_type()
1199 def visit(self
, visitor
):
1200 QAPISchemaType
.visit(self
, visitor
)
1201 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1204 class QAPISchemaEnumType(QAPISchemaType
):
1207 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
1208 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1210 assert isinstance(m
, QAPISchemaEnumMember
)
1211 m
.set_defined_in(name
)
1212 assert prefix
is None or isinstance(prefix
, str)
1213 self
.members
= members
1214 self
.prefix
= prefix
1216 def check(self
, schema
):
1217 QAPISchemaType
.check(self
, schema
)
1219 for m
in self
.members
:
1220 m
.check_clash(self
.info
, seen
)
1222 self
.doc
.connect_member(m
)
1224 def is_implicit(self
):
1225 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1226 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1229 return c_name(self
.name
)
1231 def member_names(self
):
1232 return [m
.name
for m
in self
.members
]
1234 def json_type(self
):
1237 def visit(self
, visitor
):
1238 QAPISchemaType
.visit(self
, visitor
)
1239 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
1240 self
.members
, self
.prefix
)
1243 class QAPISchemaArrayType(QAPISchemaType
):
1246 def __init__(self
, name
, info
, element_type
):
1247 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
1248 assert isinstance(element_type
, str)
1249 self
._element
_type
_name
= element_type
1250 self
.element_type
= None
1252 def check(self
, schema
):
1253 QAPISchemaType
.check(self
, schema
)
1254 self
.element_type
= schema
.resolve_type(
1255 self
._element
_type
_name
, self
.info
,
1256 self
.info
and self
.info
.defn_meta
)
1257 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
1261 assert self
._checked
1262 return self
.element_type
.ifcond
1266 assert self
._checked
1267 return self
.element_type
.module
1269 def is_implicit(self
):
1273 return c_name(self
.name
) + pointer_suffix
1275 def json_type(self
):
1279 elt_doc_type
= self
.element_type
.doc_type()
1280 if not elt_doc_type
:
1282 return 'array of ' + elt_doc_type
1284 def visit(self
, visitor
):
1285 QAPISchemaType
.visit(self
, visitor
)
1286 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
1291 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
1294 class QAPISchemaObjectType(QAPISchemaType
):
1295 def __init__(self
, name
, info
, doc
, ifcond
,
1296 base
, local_members
, variants
, features
):
1297 # struct has local_members, optional base, and no variants
1298 # flat union has base, variants, and no local_members
1299 # simple union has local_members, variants, and no base
1300 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1301 self
.meta
= 'union' if variants
else 'struct'
1302 assert base
is None or isinstance(base
, str)
1303 for m
in local_members
:
1304 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1305 m
.set_defined_in(name
)
1306 if variants
is not None:
1307 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1308 variants
.set_defined_in(name
)
1310 assert isinstance(f
, QAPISchemaFeature
)
1311 f
.set_defined_in(name
)
1312 self
._base
_name
= base
1314 self
.local_members
= local_members
1315 self
.variants
= variants
1317 self
.features
= features
1319 def check(self
, schema
):
1320 # This calls another type T's .check() exactly when the C
1321 # struct emitted by gen_object() contains that T's C struct
1322 # (pointers don't count).
1323 if self
.members
is not None:
1324 # A previous .check() completed: nothing to do
1327 # Recursed: C struct contains itself
1328 raise QAPISemError(self
.info
,
1329 "object %s contains itself" % self
.name
)
1331 QAPISchemaType
.check(self
, schema
)
1332 assert self
._checked
and self
.members
is None
1334 seen
= OrderedDict()
1336 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
1338 if (not isinstance(self
.base
, QAPISchemaObjectType
)
1339 or self
.base
.variants
):
1342 "'base' requires a struct type, %s isn't"
1343 % self
.base
.describe())
1344 self
.base
.check(schema
)
1345 self
.base
.check_clash(self
.info
, seen
)
1346 for m
in self
.local_members
:
1348 m
.check_clash(self
.info
, seen
)
1350 self
.doc
.connect_member(m
)
1351 members
= seen
.values()
1354 self
.variants
.check(schema
, seen
)
1355 self
.variants
.check_clash(self
.info
, seen
)
1357 # Features are in a name space separate from members
1359 for f
in self
.features
:
1360 f
.check_clash(self
.info
, seen
)
1365 self
.members
= members
# mark completed
1367 # Check that the members of this type do not cause duplicate JSON members,
1368 # and update seen to track the members seen so far. Report any errors
1369 # on behalf of info, which is not necessarily self.info
1370 def check_clash(self
, info
, seen
):
1371 assert self
._checked
1372 assert not self
.variants
# not implemented
1373 for m
in self
.members
:
1374 m
.check_clash(info
, seen
)
1378 assert self
._checked
1379 if isinstance(self
._ifcond
, QAPISchemaType
):
1380 # Simple union wrapper type inherits from wrapped type;
1381 # see _make_implicit_object_type()
1382 return self
._ifcond
.ifcond
1385 def is_implicit(self
):
1386 # See QAPISchema._make_implicit_object_type(), as well as
1387 # _def_predefineds()
1388 return self
.name
.startswith('q_')
1391 assert self
.members
is not None
1392 return not self
.members
and not self
.variants
1395 assert self
.name
!= 'q_empty'
1396 return QAPISchemaType
.c_name(self
)
1399 assert not self
.is_implicit()
1400 return c_name(self
.name
) + pointer_suffix
1402 def c_unboxed_type(self
):
1403 return c_name(self
.name
)
1405 def json_type(self
):
1408 def visit(self
, visitor
):
1409 QAPISchemaType
.visit(self
, visitor
)
1410 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
1411 self
.base
, self
.local_members
, self
.variants
,
1413 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
1414 self
.members
, self
.variants
,
1418 class QAPISchemaMember(object):
1419 """ Represents object members, enum members and features """
1422 def __init__(self
, name
, info
, ifcond
=None):
1423 assert isinstance(name
, str)
1426 self
.ifcond
= ifcond
or []
1427 self
.defined_in
= None
1429 def set_defined_in(self
, name
):
1430 assert not self
.defined_in
1431 self
.defined_in
= name
1433 def check_clash(self
, info
, seen
):
1434 cname
= c_name(self
.name
)
1438 "%s collides with %s"
1439 % (self
.describe(info
), seen
[cname
].describe(info
)))
1442 def describe(self
, info
):
1444 defined_in
= self
.defined_in
1447 if defined_in
.startswith('q_obj_'):
1448 # See QAPISchema._make_implicit_object_type() - reverse the
1449 # mapping there to create a nice human-readable description
1450 defined_in
= defined_in
[6:]
1451 if defined_in
.endswith('-arg'):
1452 # Implicit type created for a command's dict 'data'
1453 assert role
== 'member'
1455 elif defined_in
.endswith('-base'):
1456 # Implicit type created for a flat union's dict 'base'
1457 role
= 'base ' + role
1459 # Implicit type created for a simple union's branch
1460 assert defined_in
.endswith('-wrapper')
1461 # Unreachable and not implemented
1463 elif defined_in
.endswith('Kind'):
1464 # See QAPISchema._make_implicit_enum_type()
1465 # Implicit enum created for simple union's branches
1466 assert role
== 'value'
1468 elif defined_in
!= info
.defn_name
:
1469 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
1470 return "%s '%s'" % (role
, self
.name
)
1473 class QAPISchemaEnumMember(QAPISchemaMember
):
1477 class QAPISchemaFeature(QAPISchemaMember
):
1481 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1482 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None):
1483 QAPISchemaMember
.__init
__(self
, name
, info
, ifcond
)
1484 assert isinstance(typ
, str)
1485 assert isinstance(optional
, bool)
1486 self
._type
_name
= typ
1488 self
.optional
= optional
1490 def check(self
, schema
):
1491 assert self
.defined_in
1492 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
1496 class QAPISchemaObjectTypeVariants(object):
1497 def __init__(self
, tag_name
, info
, tag_member
, variants
):
1498 # Flat unions pass tag_name but not tag_member.
1499 # Simple unions and alternates pass tag_member but not tag_name.
1500 # After check(), tag_member is always set, and tag_name remains
1501 # a reliable witness of being used by a flat union.
1502 assert bool(tag_member
) != bool(tag_name
)
1503 assert (isinstance(tag_name
, str) or
1504 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1506 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1507 self
._tag
_name
= tag_name
1509 self
.tag_member
= tag_member
1510 self
.variants
= variants
1512 def set_defined_in(self
, name
):
1513 for v
in self
.variants
:
1514 v
.set_defined_in(name
)
1516 def check(self
, schema
, seen
):
1517 if not self
.tag_member
: # flat union
1518 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
1520 # Pointing to the base type when not implicit would be
1521 # nice, but we don't know it here
1522 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
1525 "discriminator '%s' is not a member of %s"
1526 % (self
._tag
_name
, base
))
1528 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
1530 if not base_type
.is_implicit():
1531 base
= "base type '%s'" % self
.tag_member
.defined_in
1532 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
1535 "discriminator member '%s' of %s must be of enum type"
1536 % (self
._tag
_name
, base
))
1537 if self
.tag_member
.optional
:
1540 "discriminator member '%s' of %s must not be optional"
1541 % (self
._tag
_name
, base
))
1542 if self
.tag_member
.ifcond
:
1545 "discriminator member '%s' of %s must not be conditional"
1546 % (self
._tag
_name
, base
))
1547 else: # simple union
1548 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1549 assert not self
.tag_member
.optional
1550 assert self
.tag_member
.ifcond
== []
1551 if self
._tag
_name
: # flat union
1552 # branches that are not explicitly covered get an empty type
1553 cases
= set([v
.name
for v
in self
.variants
])
1554 for m
in self
.tag_member
.type.members
:
1555 if m
.name
not in cases
:
1556 v
= QAPISchemaObjectTypeVariant(m
.name
, self
.info
,
1557 'q_empty', m
.ifcond
)
1558 v
.set_defined_in(self
.tag_member
.defined_in
)
1559 self
.variants
.append(v
)
1560 if not self
.variants
:
1561 raise QAPISemError(self
.info
, "union has no branches")
1562 for v
in self
.variants
:
1564 # Union names must match enum values; alternate names are
1565 # checked separately. Use 'seen' to tell the two apart.
1567 if v
.name
not in self
.tag_member
.type.member_names():
1570 "branch '%s' is not a value of %s"
1571 % (v
.name
, self
.tag_member
.type.describe()))
1572 if (not isinstance(v
.type, QAPISchemaObjectType
)
1573 or v
.type.variants
):
1577 % (v
.describe(self
.info
), v
.type.describe()))
1578 v
.type.check(schema
)
1580 def check_clash(self
, info
, seen
):
1581 for v
in self
.variants
:
1582 # Reset seen map for each variant, since qapi names from one
1583 # branch do not affect another branch
1584 v
.type.check_clash(info
, dict(seen
))
1587 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1590 def __init__(self
, name
, info
, typ
, ifcond
=None):
1591 QAPISchemaObjectTypeMember
.__init
__(self
, name
, info
, typ
,
1595 class QAPISchemaAlternateType(QAPISchemaType
):
1598 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
1599 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1600 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1601 assert variants
.tag_member
1602 variants
.set_defined_in(name
)
1603 variants
.tag_member
.set_defined_in(self
.name
)
1604 self
.variants
= variants
1606 def check(self
, schema
):
1607 QAPISchemaType
.check(self
, schema
)
1608 self
.variants
.tag_member
.check(schema
)
1609 # Not calling self.variants.check_clash(), because there's nothing
1611 self
.variants
.check(schema
, {})
1612 # Alternate branch names have no relation to the tag enum values;
1613 # so we have to check for potential name collisions ourselves.
1616 for v
in self
.variants
.variants
:
1617 v
.check_clash(self
.info
, seen
)
1618 qtype
= v
.type.alternate_qtype()
1623 % (v
.describe(self
.info
), v
.type.describe()))
1624 conflicting
= set([qtype
])
1625 if qtype
== 'QTYPE_QSTRING':
1626 if isinstance(v
.type, QAPISchemaEnumType
):
1627 for m
in v
.type.members
:
1628 if m
.name
in ['on', 'off']:
1629 conflicting
.add('QTYPE_QBOOL')
1630 if re
.match(r
'[-+0-9.]', m
.name
):
1631 # lazy, could be tightened
1632 conflicting
.add('QTYPE_QNUM')
1634 conflicting
.add('QTYPE_QNUM')
1635 conflicting
.add('QTYPE_QBOOL')
1636 for qt
in conflicting
:
1637 if qt
in types_seen
:
1640 "%s can't be distinguished from '%s'"
1641 % (v
.describe(self
.info
), types_seen
[qt
]))
1642 types_seen
[qt
] = v
.name
1644 self
.doc
.connect_member(v
)
1649 return c_name(self
.name
) + pointer_suffix
1651 def json_type(self
):
1654 def visit(self
, visitor
):
1655 QAPISchemaType
.visit(self
, visitor
)
1656 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
1660 class QAPISchemaCommand(QAPISchemaEntity
):
1663 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
1664 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
1665 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1666 assert not arg_type
or isinstance(arg_type
, str)
1667 assert not ret_type
or isinstance(ret_type
, str)
1668 self
._arg
_type
_name
= arg_type
1669 self
.arg_type
= None
1670 self
._ret
_type
_name
= ret_type
1671 self
.ret_type
= None
1673 self
.success_response
= success_response
1675 self
.allow_oob
= allow_oob
1676 self
.allow_preconfig
= allow_preconfig
1678 def check(self
, schema
):
1679 QAPISchemaEntity
.check(self
, schema
)
1680 if self
._arg
_type
_name
:
1681 self
.arg_type
= schema
.resolve_type(
1682 self
._arg
_type
_name
, self
.info
, "command's 'data'")
1683 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
1686 "command's 'data' cannot take %s"
1687 % self
.arg_type
.describe())
1688 if self
.arg_type
.variants
and not self
.boxed
:
1691 "command's 'data' can take %s only with 'boxed': true"
1692 % self
.arg_type
.describe())
1693 if self
._ret
_type
_name
:
1694 self
.ret_type
= schema
.resolve_type(
1695 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
1696 if self
.name
not in self
.info
.pragma
.returns_whitelist
:
1697 if not (isinstance(self
.ret_type
, QAPISchemaObjectType
)
1698 or (isinstance(self
.ret_type
, QAPISchemaArrayType
)
1699 and isinstance(self
.ret_type
.element_type
,
1700 QAPISchemaObjectType
))):
1703 "command's 'returns' cannot take %s"
1704 % self
.ret_type
.describe())
1706 def visit(self
, visitor
):
1707 QAPISchemaEntity
.visit(self
, visitor
)
1708 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
1709 self
.arg_type
, self
.ret_type
,
1710 self
.gen
, self
.success_response
,
1711 self
.boxed
, self
.allow_oob
,
1712 self
.allow_preconfig
)
1715 class QAPISchemaEvent(QAPISchemaEntity
):
1718 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
1719 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1720 assert not arg_type
or isinstance(arg_type
, str)
1721 self
._arg
_type
_name
= arg_type
1722 self
.arg_type
= None
1725 def check(self
, schema
):
1726 QAPISchemaEntity
.check(self
, schema
)
1727 if self
._arg
_type
_name
:
1728 self
.arg_type
= schema
.resolve_type(
1729 self
._arg
_type
_name
, self
.info
, "event's 'data'")
1730 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
1733 "event's 'data' cannot take %s"
1734 % self
.arg_type
.describe())
1735 if self
.arg_type
.variants
and not self
.boxed
:
1738 "event's 'data' can take %s only with 'boxed': true"
1739 % self
.arg_type
.describe())
1741 def visit(self
, visitor
):
1742 QAPISchemaEntity
.visit(self
, visitor
)
1743 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
1744 self
.arg_type
, self
.boxed
)
1747 class QAPISchema(object):
1748 def __init__(self
, fname
):
1750 parser
= QAPISchemaParser(fname
)
1751 exprs
= check_exprs(parser
.exprs
)
1752 self
.docs
= parser
.docs
1753 self
._entity
_list
= []
1754 self
._entity
_dict
= {}
1755 self
._predefining
= True
1756 self
._def
_predefineds
()
1757 self
._predefining
= False
1758 self
._def
_exprs
(exprs
)
1761 def _def_entity(self
, ent
):
1762 # Only the predefined types are allowed to not have info
1763 assert ent
.info
or self
._predefining
1764 self
._entity
_list
.append(ent
)
1765 if ent
.name
is None:
1767 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1768 # because they're liable to clash in generated C.
1769 other_ent
= self
._entity
_dict
.get(ent
.name
)
1772 where
= QAPIError(other_ent
.info
, None, "previous definition")
1775 "'%s' is already defined\n%s" % (ent
.name
, where
))
1777 ent
.info
, "%s is already defined" % other_ent
.describe())
1778 self
._entity
_dict
[ent
.name
] = ent
1780 def lookup_entity(self
, name
, typ
=None):
1781 ent
= self
._entity
_dict
.get(name
)
1782 if typ
and not isinstance(ent
, typ
):
1786 def lookup_type(self
, name
):
1787 return self
.lookup_entity(name
, QAPISchemaType
)
1789 def resolve_type(self
, name
, info
, what
):
1790 typ
= self
.lookup_type(name
)
1795 info
, "%s uses unknown type '%s'" % (what
, name
))
1798 def _def_include(self
, expr
, info
, doc
):
1799 include
= expr
['include']
1802 while main_info
.parent
:
1803 main_info
= main_info
.parent
1804 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
.fname
))
1805 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
1807 def _def_builtin_type(self
, name
, json_type
, c_type
):
1808 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1809 # Instantiating only the arrays that are actually used would
1810 # be nice, but we can't as long as their generated code
1811 # (qapi-builtin-types.[ch]) may be shared by some other
1813 self
._make
_array
_type
(name
, None)
1815 def _def_predefineds(self
):
1816 for t
in [('str', 'string', 'char' + pointer_suffix
),
1817 ('number', 'number', 'double'),
1818 ('int', 'int', 'int64_t'),
1819 ('int8', 'int', 'int8_t'),
1820 ('int16', 'int', 'int16_t'),
1821 ('int32', 'int', 'int32_t'),
1822 ('int64', 'int', 'int64_t'),
1823 ('uint8', 'int', 'uint8_t'),
1824 ('uint16', 'int', 'uint16_t'),
1825 ('uint32', 'int', 'uint32_t'),
1826 ('uint64', 'int', 'uint64_t'),
1827 ('size', 'int', 'uint64_t'),
1828 ('bool', 'boolean', 'bool'),
1829 ('any', 'value', 'QObject' + pointer_suffix
),
1830 ('null', 'null', 'QNull' + pointer_suffix
)]:
1831 self
._def
_builtin
_type
(*t
)
1832 self
.the_empty_object_type
= QAPISchemaObjectType(
1833 'q_empty', None, None, None, None, [], None, [])
1834 self
._def
_entity
(self
.the_empty_object_type
)
1836 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1838 qtype_values
= self
._make
_enum
_members
(
1839 [{'name': n
} for n
in qtypes
], None)
1841 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
1842 qtype_values
, 'QTYPE'))
1844 def _make_features(self
, features
, info
):
1845 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
1848 def _make_enum_members(self
, values
, info
):
1849 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
1852 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
1853 # See also QAPISchemaObjectTypeMember.describe()
1854 name
= name
+ 'Kind' # reserved by check_defn_name_str()
1855 self
._def
_entity
(QAPISchemaEnumType(
1856 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
, info
),
1860 def _make_array_type(self
, element_type
, info
):
1861 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1862 if not self
.lookup_type(name
):
1863 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1866 def _make_implicit_object_type(self
, name
, info
, doc
, ifcond
,
1870 # See also QAPISchemaObjectTypeMember.describe()
1871 name
= 'q_obj_%s-%s' % (name
, role
)
1872 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1874 # The implicit object type has multiple users. This can
1875 # happen only for simple unions' implicit wrapper types.
1876 # Its ifcond should be the disjunction of its user's
1877 # ifconds. Not implemented. Instead, we always pass the
1878 # wrapped type's ifcond, which is trivially the same for all
1879 # users. It's also necessary for the wrapper to compile.
1880 # But it's not tight: the disjunction need not imply it. We
1881 # may end up compiling useless wrapper types.
1882 # TODO kill simple unions or implement the disjunction
1883 assert (ifcond
or []) == typ
._ifcond
# pylint: disable=protected-access
1885 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
,
1886 None, members
, None, []))
1889 def _def_enum_type(self
, expr
, info
, doc
):
1892 prefix
= expr
.get('prefix')
1893 ifcond
= expr
.get('if')
1894 self
._def
_entity
(QAPISchemaEnumType(
1895 name
, info
, doc
, ifcond
,
1896 self
._make
_enum
_members
(data
, info
), prefix
))
1898 def _make_member(self
, name
, typ
, ifcond
, info
):
1900 if name
.startswith('*'):
1903 if isinstance(typ
, list):
1904 assert len(typ
) == 1
1905 typ
= self
._make
_array
_type
(typ
[0], info
)
1906 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
)
1908 def _make_members(self
, data
, info
):
1909 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
1910 for (key
, value
) in data
.items()]
1912 def _def_struct_type(self
, expr
, info
, doc
):
1913 name
= expr
['struct']
1914 base
= expr
.get('base')
1916 ifcond
= expr
.get('if')
1917 features
= expr
.get('features', [])
1918 self
._def
_entity
(QAPISchemaObjectType(
1919 name
, info
, doc
, ifcond
, base
,
1920 self
._make
_members
(data
, info
),
1922 self
._make
_features
(features
, info
)))
1924 def _make_variant(self
, case
, typ
, ifcond
, info
):
1925 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1927 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1928 if isinstance(typ
, list):
1929 assert len(typ
) == 1
1930 typ
= self
._make
_array
_type
(typ
[0], info
)
1931 typ
= self
._make
_implicit
_object
_type
(
1932 typ
, info
, None, self
.lookup_type(typ
),
1933 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
1934 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1936 def _def_union_type(self
, expr
, info
, doc
):
1937 name
= expr
['union']
1939 base
= expr
.get('base')
1940 ifcond
= expr
.get('if')
1941 tag_name
= expr
.get('discriminator')
1943 if isinstance(base
, dict):
1944 base
= self
._make
_implicit
_object
_type
(
1945 name
, info
, doc
, ifcond
,
1946 'base', self
._make
_members
(base
, info
))
1948 variants
= [self
._make
_variant
(key
, value
['type'],
1949 value
.get('if'), info
)
1950 for (key
, value
) in data
.items()]
1953 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1954 value
.get('if'), info
)
1955 for (key
, value
) in data
.items()]
1956 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1957 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1958 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1959 members
= [tag_member
]
1961 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
1962 QAPISchemaObjectTypeVariants(
1963 tag_name
, info
, tag_member
, variants
),
1966 def _def_alternate_type(self
, expr
, info
, doc
):
1967 name
= expr
['alternate']
1969 ifcond
= expr
.get('if')
1970 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1972 for (key
, value
) in data
.items()]
1973 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1975 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
1976 QAPISchemaObjectTypeVariants(
1977 None, info
, tag_member
, variants
)))
1979 def _def_command(self
, expr
, info
, doc
):
1980 name
= expr
['command']
1981 data
= expr
.get('data')
1982 rets
= expr
.get('returns')
1983 gen
= expr
.get('gen', True)
1984 success_response
= expr
.get('success-response', True)
1985 boxed
= expr
.get('boxed', False)
1986 allow_oob
= expr
.get('allow-oob', False)
1987 allow_preconfig
= expr
.get('allow-preconfig', False)
1988 ifcond
= expr
.get('if')
1989 if isinstance(data
, OrderedDict
):
1990 data
= self
._make
_implicit
_object
_type
(
1991 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1992 if isinstance(rets
, list):
1993 assert len(rets
) == 1
1994 rets
= self
._make
_array
_type
(rets
[0], info
)
1995 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
1996 gen
, success_response
,
1997 boxed
, allow_oob
, allow_preconfig
))
1999 def _def_event(self
, expr
, info
, doc
):
2000 name
= expr
['event']
2001 data
= expr
.get('data')
2002 boxed
= expr
.get('boxed', False)
2003 ifcond
= expr
.get('if')
2004 if isinstance(data
, OrderedDict
):
2005 data
= self
._make
_implicit
_object
_type
(
2006 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
2007 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
2009 def _def_exprs(self
, exprs
):
2010 for expr_elem
in exprs
:
2011 expr
= expr_elem
['expr']
2012 info
= expr_elem
['info']
2013 doc
= expr_elem
.get('doc')
2015 self
._def
_enum
_type
(expr
, info
, doc
)
2016 elif 'struct' in expr
:
2017 self
._def
_struct
_type
(expr
, info
, doc
)
2018 elif 'union' in expr
:
2019 self
._def
_union
_type
(expr
, info
, doc
)
2020 elif 'alternate' in expr
:
2021 self
._def
_alternate
_type
(expr
, info
, doc
)
2022 elif 'command' in expr
:
2023 self
._def
_command
(expr
, info
, doc
)
2024 elif 'event' in expr
:
2025 self
._def
_event
(expr
, info
, doc
)
2026 elif 'include' in expr
:
2027 self
._def
_include
(expr
, info
, doc
)
2032 for ent
in self
._entity
_list
:
2035 def visit(self
, visitor
):
2036 visitor
.visit_begin(self
)
2038 visitor
.visit_module(module
)
2039 for entity
in self
._entity
_list
:
2040 if visitor
.visit_needed(entity
):
2041 if entity
.module
!= module
:
2042 module
= entity
.module
2043 visitor
.visit_module(module
)
2044 entity
.visit(visitor
)
2049 # Code generation helpers
2052 def camel_case(name
):
2056 if ch
in ['_', '-']:
2059 new_name
+= ch
.upper()
2062 new_name
+= ch
.lower()
2066 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2067 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2068 # ENUM24_Name -> ENUM24_NAME
2069 def camel_to_upper(value
):
2070 c_fun_str
= c_name(value
, False)
2075 length
= len(c_fun_str
)
2076 for i
in range(length
):
2078 # When c is upper and no '_' appears before, do more checks
2079 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
2080 if i
< length
- 1 and c_fun_str
[i
+ 1].islower():
2082 elif c_fun_str
[i
- 1].isdigit():
2085 return new_name
.lstrip('_').upper()
2088 def c_enum_const(type_name
, const_name
, prefix
=None):
2089 if prefix
is not None:
2091 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
2094 if hasattr(str, 'maketrans'):
2095 c_name_trans
= str.maketrans('.-', '__')
2097 c_name_trans
= string
.maketrans('.-', '__')
2100 # Map @name to a valid C identifier.
2101 # If @protect, avoid returning certain ticklish identifiers (like
2102 # C keywords) by prepending 'q_'.
2104 # Used for converting 'name' from a 'name':'type' qapi definition
2105 # into a generated struct member, as well as converting type names
2106 # into substrings of a generated C function name.
2107 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2108 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2109 def c_name(name
, protect
=True):
2110 # ANSI X3J11/88-090, 3.1.1
2111 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
2112 'default', 'do', 'double', 'else', 'enum', 'extern',
2113 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2114 'return', 'short', 'signed', 'sizeof', 'static',
2115 'struct', 'switch', 'typedef', 'union', 'unsigned',
2116 'void', 'volatile', 'while'])
2117 # ISO/IEC 9899:1999, 6.4.1
2118 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2119 # ISO/IEC 9899:2011, 6.4.1
2120 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2121 '_Noreturn', '_Static_assert', '_Thread_local'])
2122 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2124 gcc_words
= set(['asm', 'typeof'])
2125 # C++ ISO/IEC 14882:2003 2.11
2126 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
2127 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2128 'namespace', 'new', 'operator', 'private', 'protected',
2129 'public', 'reinterpret_cast', 'static_cast', 'template',
2130 'this', 'throw', 'true', 'try', 'typeid', 'typename',
2131 'using', 'virtual', 'wchar_t',
2132 # alternative representations
2133 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2134 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2135 # namespace pollution:
2136 polluted_words
= set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2137 name
= name
.translate(c_name_trans
)
2138 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
2139 | cpp_words | polluted_words
):
2144 eatspace
= '\033EATSPACE.'
2145 pointer_suffix
= ' *' + eatspace
2148 def genindent(count
):
2150 for _
in range(count
):
2158 def push_indent(indent_amount
=4):
2160 indent_level
+= indent_amount
2163 def pop_indent(indent_amount
=4):
2165 indent_level
-= indent_amount
2168 # Generate @code with @kwds interpolated.
2169 # Obey indent_level, and strip eatspace.
2170 def cgen(code
, **kwds
):
2173 indent
= genindent(indent_level
)
2174 # re.subn() lacks flags support before Python 2.7, use re.compile()
2175 raw
= re
.subn(re
.compile(r
'^(?!(#|$))', re
.MULTILINE
),
2178 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
2181 def mcgen(code
, **kwds
):
2184 return cgen(code
, **kwds
)
2187 def c_fname(filename
):
2188 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
)
2191 def guardstart(name
):
2197 name
=c_fname(name
).upper())
2203 #endif /* %(name)s */
2205 name
=c_fname(name
).upper())
2217 def gen_endif(ifcond
):
2219 for ifc
in reversed(ifcond
):
2221 #endif /* %(cond)s */
2226 def _wrap_ifcond(ifcond
, before
, after
):
2228 return after
# suppress empty #if ... #endif
2230 assert after
.startswith(before
)
2232 added
= after
[len(before
):]
2233 if added
[0] == '\n':
2236 out
+= gen_if(ifcond
)
2238 out
+= gen_endif(ifcond
)
2242 def build_params(arg_type
, boxed
, extra
=None):
2247 ret
+= '%s arg' % arg_type
.c_param_type()
2250 assert not arg_type
.variants
2251 for memb
in arg_type
.members
:
2255 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
2256 ret
+= '%s %s' % (memb
.type.c_param_type(),
2260 return ret
if ret
else 'void'
2264 # Accumulate and write output
2267 class QAPIGen(object):
2269 def __init__(self
, fname
):
2274 def preamble_add(self
, text
):
2275 self
._preamble
+= text
2277 def add(self
, text
):
2280 def get_content(self
):
2281 return self
._top
() + self
._preamble
+ self
._body
+ self
._bottom
()
2289 def write(self
, output_dir
):
2290 pathname
= os
.path
.join(output_dir
, self
.fname
)
2291 dir = os
.path
.dirname(pathname
)
2295 except os
.error
as e
:
2296 if e
.errno
!= errno
.EEXIST
:
2298 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2299 if sys
.version_info
[0] >= 3:
2300 f
= open(fd
, 'r+', encoding
='utf-8')
2302 f
= os
.fdopen(fd
, 'r+')
2303 text
= self
.get_content()
2304 oldtext
= f
.read(len(text
) + 1)
2313 def ifcontext(ifcond
, *args
):
2314 """A 'with' statement context manager to wrap with start_if()/end_if()
2316 *args: any number of QAPIGenCCode
2320 with ifcontext(ifcond, self._genh, self._genc):
2321 modify self._genh and self._genc ...
2323 Is equivalent to calling::
2325 self._genh.start_if(ifcond)
2326 self._genc.start_if(ifcond)
2327 modify self._genh and self._genc ...
2332 arg
.start_if(ifcond
)
2338 class QAPIGenCCode(QAPIGen
):
2340 def __init__(self
, fname
):
2341 QAPIGen
.__init
__(self
, fname
)
2342 self
._start
_if
= None
2344 def start_if(self
, ifcond
):
2345 assert self
._start
_if
is None
2346 self
._start
_if
= (ifcond
, self
._body
, self
._preamble
)
2349 assert self
._start
_if
2351 self
._start
_if
= None
2353 def _wrap_ifcond(self
):
2354 self
._body
= _wrap_ifcond(self
._start
_if
[0],
2355 self
._start
_if
[1], self
._body
)
2356 self
._preamble
= _wrap_ifcond(self
._start
_if
[0],
2357 self
._start
_if
[2], self
._preamble
)
2359 def get_content(self
):
2360 assert self
._start
_if
is None
2361 return QAPIGen
.get_content(self
)
2364 class QAPIGenC(QAPIGenCCode
):
2366 def __init__(self
, fname
, blurb
, pydoc
):
2367 QAPIGenCCode
.__init
__(self
, fname
)
2369 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2374 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2381 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2382 * See the COPYING.LIB file in the top-level directory.
2386 blurb
=self
._blurb
, copyright
=self
._copyright
)
2391 /* Dummy declaration to prevent empty .o file */
2392 char qapi_dummy_%(name)s;
2394 name
=c_fname(self
.fname
))
2397 class QAPIGenH(QAPIGenC
):
2400 return QAPIGenC
._top
(self
) + guardstart(self
.fname
)
2403 return guardend(self
.fname
)
2406 class QAPIGenDoc(QAPIGen
):
2409 return (QAPIGen
._top
(self
)
2410 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2413 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2415 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2416 self
._prefix
= prefix
2418 self
._genc
= QAPIGenC(self
._prefix
+ self
._what
+ '.c',
2420 self
._genh
= QAPIGenH(self
._prefix
+ self
._what
+ '.h',
2423 def write(self
, output_dir
):
2424 self
._genc
.write(output_dir
)
2425 self
._genh
.write(output_dir
)
2428 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2430 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2431 self
._prefix
= prefix
2438 self
._main
_module
= None
2441 def _is_user_module(name
):
2442 return name
and not name
.startswith('./')
2445 def _is_builtin_module(name
):
2448 def _module_dirname(self
, what
, name
):
2449 if self
._is
_user
_module
(name
):
2450 return os
.path
.dirname(name
)
2453 def _module_basename(self
, what
, name
):
2454 ret
= '' if self
._is
_builtin
_module
(name
) else self
._prefix
2455 if self
._is
_user
_module
(name
):
2456 basename
= os
.path
.basename(name
)
2458 if name
!= self
._main
_module
:
2459 ret
+= '-' + os
.path
.splitext(basename
)[0]
2461 name
= name
[2:] if name
else 'builtin'
2462 ret
+= re
.sub(r
'-', '-' + name
+ '-', what
)
2465 def _module_filename(self
, what
, name
):
2466 return os
.path
.join(self
._module
_dirname
(what
, name
),
2467 self
._module
_basename
(what
, name
))
2469 def _add_module(self
, name
, blurb
):
2470 basename
= self
._module
_filename
(self
._what
, name
)
2471 genc
= QAPIGenC(basename
+ '.c', blurb
, self
._pydoc
)
2472 genh
= QAPIGenH(basename
+ '.h', blurb
, self
._pydoc
)
2473 self
._module
[name
] = (genc
, genh
)
2474 self
._set
_module
(name
)
2476 def _add_user_module(self
, name
, blurb
):
2477 assert self
._is
_user
_module
(name
)
2478 if self
._main
_module
is None:
2479 self
._main
_module
= name
2480 self
._add
_module
(name
, blurb
)
2482 def _add_system_module(self
, name
, blurb
):
2483 self
._add
_module
(name
and './' + name
, blurb
)
2485 def _set_module(self
, name
):
2486 self
._genc
, self
._genh
= self
._module
[name
]
2488 def write(self
, output_dir
, opt_builtins
=False):
2489 for name
in self
._module
:
2490 if self
._is
_builtin
_module
(name
) and not opt_builtins
:
2492 (genc
, genh
) = self
._module
[name
]
2493 genc
.write(output_dir
)
2494 genh
.write(output_dir
)
2496 def _begin_user_module(self
, name
):
2499 def visit_module(self
, name
):
2500 if name
in self
._module
:
2501 self
._set
_module
(name
)
2502 elif self
._is
_builtin
_module
(name
):
2503 # The built-in module has not been created. No code may
2508 self
._add
_user
_module
(name
, self
._blurb
)
2509 self
._begin
_user
_module
(name
)
2511 def visit_include(self
, name
, info
):
2512 relname
= os
.path
.relpath(self
._module
_filename
(self
._what
, name
),
2513 os
.path
.dirname(self
._genh
.fname
))
2514 self
._genh
.preamble_add(mcgen('''
2515 #include "%(relname)s.h"