From fb0bc835e56b894cbc7236294921e5393c786ad8 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 26 Feb 2018 13:48:58 -0600 Subject: qapi-gen: New common driver for code and doc generators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whenever qapi-schema.json changes, we run six programs eleven times to update eleven files. Similar for qga/qapi-schema.json. This is silly. Replace the six programs by a single program that spits out all eleven files. The programs become modules in new Python package qapi, along with the helper library. This requires moving them to scripts/qapi/. While moving them, consistently drop executable mode bits. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20180211093607.27351-9-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Michael Roth [eblake: move change to one-line 'blurb' earlier in series, mention mode bit change as intentional, update qapi-code-gen.txt to match actual generated events.c file] Signed-off-by: Eric Blake --- scripts/qapi/common.py | 2041 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2041 insertions(+) create mode 100644 scripts/qapi/common.py (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py new file mode 100644 index 0000000000..3bc31a03ce --- /dev/null +++ b/scripts/qapi/common.py @@ -0,0 +1,2041 @@ +# +# QAPI helper library +# +# Copyright IBM, Corp. 2011 +# Copyright (c) 2013-2018 Red Hat Inc. +# +# Authors: +# Anthony Liguori +# Markus Armbruster +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +from __future__ import print_function +import errno +import getopt +import os +import re +import string +import sys +try: + from collections import OrderedDict +except: + from ordereddict import OrderedDict + +builtin_types = { + 'null': 'QTYPE_QNULL', + 'str': 'QTYPE_QSTRING', + 'int': 'QTYPE_QNUM', + 'number': 'QTYPE_QNUM', + 'bool': 'QTYPE_QBOOL', + 'int8': 'QTYPE_QNUM', + 'int16': 'QTYPE_QNUM', + 'int32': 'QTYPE_QNUM', + 'int64': 'QTYPE_QNUM', + 'uint8': 'QTYPE_QNUM', + 'uint16': 'QTYPE_QNUM', + 'uint32': 'QTYPE_QNUM', + 'uint64': 'QTYPE_QNUM', + 'size': 'QTYPE_QNUM', + 'any': None, # any QType possible, actually + 'QType': 'QTYPE_QSTRING', +} + +# Are documentation comments required? +doc_required = False + +# Whitelist of commands allowed to return a non-dictionary +returns_whitelist = [] + +# Whitelist of entities allowed to violate case conventions +name_case_whitelist = [] + +enum_types = {} +struct_types = {} +union_types = {} +all_names = {} + +# +# Parsing the schema into expressions +# + + +def error_path(parent): + res = '' + while parent: + res = ('In file included from %s:%d:\n' % (parent['file'], + parent['line'])) + res + parent = parent['parent'] + return res + + +class QAPIError(Exception): + def __init__(self, fname, line, col, incl_info, msg): + Exception.__init__(self) + self.fname = fname + self.line = line + self.col = col + self.info = incl_info + self.msg = msg + + def __str__(self): + loc = '%s:%d' % (self.fname, self.line) + if self.col is not None: + loc += ':%s' % self.col + return error_path(self.info) + '%s: %s' % (loc, self.msg) + + +class QAPIParseError(QAPIError): + def __init__(self, parser, msg): + col = 1 + for ch in parser.src[parser.line_pos:parser.pos]: + if ch == '\t': + col = (col + 7) % 8 + 1 + else: + col += 1 + QAPIError.__init__(self, parser.fname, parser.line, col, + parser.incl_info, msg) + + +class QAPISemError(QAPIError): + def __init__(self, info, msg): + QAPIError.__init__(self, info['file'], info['line'], None, + info['parent'], msg) + + +class QAPIDoc(object): + class Section(object): + def __init__(self, name=None): + # optional section name (argument/member or section name) + self.name = name + # the list of lines for this section + self.text = '' + + def append(self, line): + self.text += line.rstrip() + '\n' + + class ArgSection(Section): + def __init__(self, name): + QAPIDoc.Section.__init__(self, name) + self.member = None + + def connect(self, member): + self.member = member + + def __init__(self, parser, info): + # self._parser is used to report errors with QAPIParseError. The + # resulting error position depends on the state of the parser. + # It happens to be the beginning of the comment. More or less + # servicable, but action at a distance. + self._parser = parser + self.info = info + self.symbol = None + self.body = QAPIDoc.Section() + # dict mapping parameter name to ArgSection + self.args = OrderedDict() + # a list of Section + self.sections = [] + # the current section + self._section = self.body + + def has_section(self, name): + """Return True if we have a section with this name.""" + for i in self.sections: + if i.name == name: + return True + return False + + def append(self, line): + """Parse a comment line and add it to the documentation.""" + line = line[1:] + if not line: + self._append_freeform(line) + return + + if line[0] != ' ': + raise QAPIParseError(self._parser, "Missing space after #") + line = line[1:] + + # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't + # recognized, and get silently treated as ordinary text + if self.symbol: + self._append_symbol_line(line) + elif not self.body.text and line.startswith('@'): + if not line.endswith(':'): + raise QAPIParseError(self._parser, "Line should end with :") + self.symbol = line[1:-1] + # FIXME invalid names other than the empty string aren't flagged + if not self.symbol: + raise QAPIParseError(self._parser, "Invalid name") + else: + self._append_freeform(line) + + def end_comment(self): + self._end_section() + + def _append_symbol_line(self, line): + name = line.split(' ', 1)[0] + + if name.startswith('@') and name.endswith(':'): + line = line[len(name)+1:] + self._start_args_section(name[1:-1]) + elif name in ('Returns:', 'Since:', + # those are often singular or plural + 'Note:', 'Notes:', + 'Example:', 'Examples:', + 'TODO:'): + line = line[len(name)+1:] + self._start_section(name[:-1]) + + self._append_freeform(line) + + def _start_args_section(self, name): + # FIXME invalid names other than the empty string aren't flagged + if not name: + raise QAPIParseError(self._parser, "Invalid parameter name") + if name in self.args: + raise QAPIParseError(self._parser, + "'%s' parameter name duplicated" % name) + if self.sections: + raise QAPIParseError(self._parser, + "'@%s:' can't follow '%s' section" + % (name, self.sections[0].name)) + self._end_section() + self._section = QAPIDoc.ArgSection(name) + self.args[name] = self._section + + def _start_section(self, name=None): + if name in ('Returns', 'Since') and self.has_section(name): + raise QAPIParseError(self._parser, + "Duplicated '%s' section" % name) + self._end_section() + self._section = QAPIDoc.Section(name) + self.sections.append(self._section) + + def _end_section(self): + if self._section: + text = self._section.text = self._section.text.strip() + if self._section.name and (not text or text.isspace()): + raise QAPIParseError(self._parser, "Empty doc section '%s'" + % self._section.name) + self._section = None + + def _append_freeform(self, line): + in_arg = isinstance(self._section, QAPIDoc.ArgSection) + if (in_arg and self._section.text.endswith('\n\n') + and line and not line[0].isspace()): + self._start_section() + if (in_arg or not self._section.name + or not self._section.name.startswith('Example')): + line = line.strip() + match = re.match(r'(@\S+:)', line) + if match: + raise QAPIParseError(self._parser, + "'%s' not allowed in free-form documentation" + % match.group(1)) + self._section.append(line) + + def connect_member(self, member): + if member.name not in self.args: + # Undocumented TODO outlaw + self.args[member.name] = QAPIDoc.ArgSection(member.name) + self.args[member.name].connect(member) + + def check_expr(self, expr): + if self.has_section('Returns') and 'command' not in expr: + raise QAPISemError(self.info, + "'Returns:' is only valid for commands") + + def check(self): + bogus = [name for name, section in self.args.items() + if not section.member] + if bogus: + raise QAPISemError( + self.info, + "The following documented members are not in " + "the declaration: %s" % ", ".join(bogus)) + + +class QAPISchemaParser(object): + + def __init__(self, fp, previously_included=[], incl_info=None): + abs_fname = os.path.abspath(fp.name) + self.fname = fp.name + previously_included.append(abs_fname) + self.incl_info = incl_info + self.src = fp.read() + if self.src == '' or self.src[-1] != '\n': + self.src += '\n' + self.cursor = 0 + self.line = 1 + self.line_pos = 0 + self.exprs = [] + self.docs = [] + self.accept() + cur_doc = None + + while self.tok is not None: + info = {'file': self.fname, 'line': self.line, + 'parent': self.incl_info} + if self.tok == '#': + self.reject_expr_doc(cur_doc) + cur_doc = self.get_doc(info) + self.docs.append(cur_doc) + continue + + expr = self.get_expr(False) + if 'include' in expr: + self.reject_expr_doc(cur_doc) + if len(expr) != 1: + raise QAPISemError(info, "Invalid 'include' directive") + include = expr['include'] + if not isinstance(include, str): + raise QAPISemError(info, + "Value of 'include' must be a string") + self._include(include, info, os.path.dirname(abs_fname), + previously_included) + elif "pragma" in expr: + self.reject_expr_doc(cur_doc) + if len(expr) != 1: + raise QAPISemError(info, "Invalid 'pragma' directive") + pragma = expr['pragma'] + if not isinstance(pragma, dict): + raise QAPISemError( + info, "Value of 'pragma' must be a dictionary") + for name, value in pragma.items(): + self._pragma(name, value, info) + else: + expr_elem = {'expr': expr, + 'info': info} + if cur_doc: + if not cur_doc.symbol: + raise QAPISemError( + cur_doc.info, "Expression documentation required") + expr_elem['doc'] = cur_doc + self.exprs.append(expr_elem) + cur_doc = None + self.reject_expr_doc(cur_doc) + + @staticmethod + def reject_expr_doc(doc): + if doc and doc.symbol: + raise QAPISemError( + doc.info, + "Documentation for '%s' is not followed by the definition" + % doc.symbol) + + def _include(self, include, info, base_dir, previously_included): + incl_abs_fname = os.path.join(base_dir, include) + # catch inclusion cycle + inf = info + while inf: + if incl_abs_fname == os.path.abspath(inf['file']): + raise QAPISemError(info, "Inclusion loop for %s" % include) + inf = inf['parent'] + + # skip multiple include of the same file + if incl_abs_fname in previously_included: + return + try: + fobj = open(incl_abs_fname, 'r') + except IOError as e: + raise QAPISemError(info, '%s: %s' % (e.strerror, include)) + exprs_include = QAPISchemaParser(fobj, previously_included, info) + self.exprs.extend(exprs_include.exprs) + self.docs.extend(exprs_include.docs) + + def _pragma(self, name, value, info): + global doc_required, returns_whitelist, name_case_whitelist + if name == 'doc-required': + if not isinstance(value, bool): + raise QAPISemError(info, + "Pragma 'doc-required' must be boolean") + doc_required = value + elif name == 'returns-whitelist': + if (not isinstance(value, list) + or any([not isinstance(elt, str) for elt in value])): + raise QAPISemError(info, + "Pragma returns-whitelist must be" + " a list of strings") + returns_whitelist = value + elif name == 'name-case-whitelist': + if (not isinstance(value, list) + or any([not isinstance(elt, str) for elt in value])): + raise QAPISemError(info, + "Pragma name-case-whitelist must be" + " a list of strings") + name_case_whitelist = value + else: + raise QAPISemError(info, "Unknown pragma '%s'" % name) + + def accept(self, skip_comment=True): + while True: + self.tok = self.src[self.cursor] + self.pos = self.cursor + self.cursor += 1 + self.val = None + + if self.tok == '#': + if self.src[self.cursor] == '#': + # Start of doc comment + skip_comment = False + self.cursor = self.src.find('\n', self.cursor) + if not skip_comment: + self.val = self.src[self.pos:self.cursor] + return + elif self.tok in '{}:,[]': + return + elif self.tok == "'": + string = '' + esc = False + while True: + ch = self.src[self.cursor] + self.cursor += 1 + if ch == '\n': + raise QAPIParseError(self, 'Missing terminating "\'"') + if esc: + if ch == 'b': + string += '\b' + elif ch == 'f': + string += '\f' + elif ch == 'n': + string += '\n' + elif ch == 'r': + string += '\r' + elif ch == 't': + string += '\t' + elif ch == 'u': + value = 0 + for _ in range(0, 4): + ch = self.src[self.cursor] + self.cursor += 1 + if ch not in '0123456789abcdefABCDEF': + raise QAPIParseError(self, + '\\u escape needs 4 ' + 'hex digits') + value = (value << 4) + int(ch, 16) + # If Python 2 and 3 didn't disagree so much on + # how to handle Unicode, then we could allow + # Unicode string defaults. But most of QAPI is + # ASCII-only, so we aren't losing much for now. + if not value or value > 0x7f: + raise QAPIParseError(self, + 'For now, \\u escape ' + 'only supports non-zero ' + 'values up to \\u007f') + string += chr(value) + elif ch in '\\/\'"': + string += ch + else: + raise QAPIParseError(self, + "Unknown escape \\%s" % ch) + esc = False + elif ch == '\\': + esc = True + elif ch == "'": + self.val = string + return + else: + string += ch + elif self.src.startswith('true', self.pos): + self.val = True + self.cursor += 3 + return + elif self.src.startswith('false', self.pos): + self.val = False + self.cursor += 4 + return + elif self.src.startswith('null', self.pos): + self.val = None + self.cursor += 3 + return + elif self.tok == '\n': + if self.cursor == len(self.src): + self.tok = None + return + self.line += 1 + self.line_pos = self.cursor + elif not self.tok.isspace(): + raise QAPIParseError(self, 'Stray "%s"' % self.tok) + + def get_members(self): + expr = OrderedDict() + if self.tok == '}': + self.accept() + return expr + if self.tok != "'": + raise QAPIParseError(self, 'Expected string or "}"') + while True: + key = self.val + self.accept() + if self.tok != ':': + raise QAPIParseError(self, 'Expected ":"') + self.accept() + if key in expr: + raise QAPIParseError(self, 'Duplicate key "%s"' % key) + expr[key] = self.get_expr(True) + if self.tok == '}': + self.accept() + return expr + if self.tok != ',': + raise QAPIParseError(self, 'Expected "," or "}"') + self.accept() + if self.tok != "'": + raise QAPIParseError(self, 'Expected string') + + def get_values(self): + expr = [] + if self.tok == ']': + self.accept() + return expr + if self.tok not in "{['tfn": + raise QAPIParseError(self, 'Expected "{", "[", "]", string, ' + 'boolean or "null"') + while True: + expr.append(self.get_expr(True)) + if self.tok == ']': + self.accept() + return expr + if self.tok != ',': + raise QAPIParseError(self, 'Expected "," or "]"') + self.accept() + + def get_expr(self, nested): + if self.tok != '{' and not nested: + raise QAPIParseError(self, 'Expected "{"') + if self.tok == '{': + self.accept() + expr = self.get_members() + elif self.tok == '[': + self.accept() + expr = self.get_values() + elif self.tok in "'tfn": + expr = self.val + self.accept() + else: + raise QAPIParseError(self, 'Expected "{", "[", string, ' + 'boolean or "null"') + return expr + + def get_doc(self, info): + if self.val != '##': + raise QAPIParseError(self, "Junk after '##' at start of " + "documentation comment") + + doc = QAPIDoc(self, info) + self.accept(False) + while self.tok == '#': + if self.val.startswith('##'): + # End of doc comment + if self.val != '##': + raise QAPIParseError(self, "Junk after '##' at end of " + "documentation comment") + doc.end_comment() + self.accept() + return doc + else: + doc.append(self.val) + self.accept(False) + + raise QAPIParseError(self, "Documentation comment must end with '##'") + + +# +# Semantic analysis of schema expressions +# TODO fold into QAPISchema +# TODO catching name collisions in generated code would be nice +# + + +def find_base_members(base): + if isinstance(base, dict): + return base + base_struct_define = struct_types.get(base) + if not base_struct_define: + return None + return base_struct_define['data'] + + +# Return the qtype of an alternate branch, or None on error. +def find_alternate_member_qtype(qapi_type): + if qapi_type in builtin_types: + return builtin_types[qapi_type] + elif qapi_type in struct_types: + return 'QTYPE_QDICT' + elif qapi_type in enum_types: + return 'QTYPE_QSTRING' + elif qapi_type in union_types: + return 'QTYPE_QDICT' + return None + + +# Return the discriminator enum define if discriminator is specified as an +# enum type, otherwise return None. +def discriminator_find_enum_define(expr): + base = expr.get('base') + discriminator = expr.get('discriminator') + + if not (discriminator and base): + return None + + base_members = find_base_members(base) + if not base_members: + return None + + discriminator_type = base_members.get(discriminator) + if not discriminator_type: + return None + + return enum_types.get(discriminator_type) + + +# Names must be letters, numbers, -, and _. They must start with letter, +# except for downstream extensions which must start with __RFQDN_. +# Dots are only valid in the downstream extension prefix. +valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?' + '[a-zA-Z][a-zA-Z0-9_-]*$') + + +def check_name(info, source, name, allow_optional=False, + enum_member=False): + global valid_name + membername = name + + if not isinstance(name, str): + raise QAPISemError(info, "%s requires a string name" % source) + if name.startswith('*'): + membername = name[1:] + if not allow_optional: + raise QAPISemError(info, "%s does not allow optional name '%s'" + % (source, name)) + # Enum members can start with a digit, because the generated C + # code always prefixes it with the enum name + if enum_member and membername[0].isdigit(): + membername = 'D' + membername + # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty' + # and 'q_obj_*' implicit type names. + if not valid_name.match(membername) or \ + c_name(membername, False).startswith('q_'): + raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name)) + + +def add_name(name, info, meta, implicit=False): + global all_names + check_name(info, "'%s'" % meta, name) + # FIXME should reject names that differ only in '_' vs. '.' + # vs. '-', because they're liable to clash in generated C. + if name in all_names: + raise QAPISemError(info, "%s '%s' is already defined" + % (all_names[name], name)) + if not implicit and (name.endswith('Kind') or name.endswith('List')): + raise QAPISemError(info, "%s '%s' should not end in '%s'" + % (meta, name, name[-4:])) + all_names[name] = meta + + +def check_type(info, source, value, allow_array=False, + allow_dict=False, allow_optional=False, + allow_metas=[]): + global all_names + + if value is None: + return + + # Check if array type for value is okay + if isinstance(value, list): + if not allow_array: + raise QAPISemError(info, "%s cannot be an array" % source) + if len(value) != 1 or not isinstance(value[0], str): + raise QAPISemError(info, + "%s: array type must contain single type name" % + source) + value = value[0] + + # Check if type name for value is okay + if isinstance(value, str): + if value not in all_names: + raise QAPISemError(info, "%s uses unknown type '%s'" + % (source, value)) + if not all_names[value] in allow_metas: + raise QAPISemError(info, "%s cannot use %s type '%s'" % + (source, all_names[value], value)) + return + + if not allow_dict: + raise QAPISemError(info, "%s should be a type name" % source) + + if not isinstance(value, OrderedDict): + raise QAPISemError(info, + "%s should be a dictionary or type name" % source) + + # value is a dictionary, check that each member is okay + for (key, arg) in value.items(): + check_name(info, "Member of %s" % source, key, + allow_optional=allow_optional) + if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): + raise QAPISemError(info, "Member of %s uses reserved name '%s'" + % (source, key)) + # Todo: allow dictionaries to represent default values of + # an optional argument. + check_type(info, "Member '%s' of %s" % (key, source), arg, + allow_array=True, + allow_metas=['built-in', 'union', 'alternate', 'struct', + 'enum']) + + +def check_command(expr, info): + name = expr['command'] + boxed = expr.get('boxed', False) + + args_meta = ['struct'] + if boxed: + args_meta += ['union', 'alternate'] + check_type(info, "'data' for command '%s'" % name, + expr.get('data'), allow_dict=not boxed, allow_optional=True, + allow_metas=args_meta) + returns_meta = ['union', 'struct'] + if name in returns_whitelist: + returns_meta += ['built-in', 'alternate', 'enum'] + check_type(info, "'returns' for command '%s'" % name, + expr.get('returns'), allow_array=True, + allow_optional=True, allow_metas=returns_meta) + + +def check_event(expr, info): + name = expr['event'] + boxed = expr.get('boxed', False) + + meta = ['struct'] + if boxed: + meta += ['union', 'alternate'] + check_type(info, "'data' for event '%s'" % name, + expr.get('data'), allow_dict=not boxed, allow_optional=True, + allow_metas=meta) + + +def check_union(expr, info): + name = expr['union'] + base = expr.get('base') + discriminator = expr.get('discriminator') + members = expr['data'] + + # Two types of unions, determined by discriminator. + + # With no discriminator it is a simple union. + if discriminator is None: + enum_define = None + allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum'] + if base is not None: + raise QAPISemError(info, "Simple union '%s' must not have a base" % + name) + + # Else, it's a flat union. + else: + # The object must have a string or dictionary 'base'. + check_type(info, "'base' for union '%s'" % name, + base, allow_dict=True, allow_optional=True, + allow_metas=['struct']) + if not base: + raise QAPISemError(info, "Flat union '%s' must have a base" + % name) + base_members = find_base_members(base) + assert base_members is not None + + # The value of member 'discriminator' must name a non-optional + # member of the base struct. + check_name(info, "Discriminator of flat union '%s'" % name, + discriminator) + discriminator_type = base_members.get(discriminator) + if not discriminator_type: + raise QAPISemError(info, + "Discriminator '%s' is not a member of base " + "struct '%s'" + % (discriminator, base)) + enum_define = enum_types.get(discriminator_type) + allow_metas = ['struct'] + # Do not allow string discriminator + if not enum_define: + raise QAPISemError(info, + "Discriminator '%s' must be of enumeration " + "type" % discriminator) + + # Check every branch; don't allow an empty union + if len(members) == 0: + raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name) + for (key, value) in members.items(): + check_name(info, "Member of union '%s'" % name, key) + + # Each value must name a known type + check_type(info, "Member '%s' of union '%s'" % (key, name), + value, allow_array=not base, allow_metas=allow_metas) + + # If the discriminator names an enum type, then all members + # of 'data' must also be members of the enum type. + if enum_define: + if key not in enum_define['data']: + raise QAPISemError(info, + "Discriminator value '%s' is not found in " + "enum '%s'" + % (key, enum_define['enum'])) + + # If discriminator is user-defined, ensure all values are covered + if enum_define: + for value in enum_define['data']: + if value not in members.keys(): + raise QAPISemError(info, "Union '%s' data missing '%s' branch" + % (name, value)) + + +def check_alternate(expr, info): + name = expr['alternate'] + members = expr['data'] + types_seen = {} + + # Check every branch; require at least two branches + if len(members) < 2: + raise QAPISemError(info, + "Alternate '%s' should have at least two branches " + "in 'data'" % name) + for (key, value) in members.items(): + check_name(info, "Member of alternate '%s'" % name, key) + + # Ensure alternates have no type conflicts. + check_type(info, "Member '%s' of alternate '%s'" % (key, name), + value, + allow_metas=['built-in', 'union', 'struct', 'enum']) + qtype = find_alternate_member_qtype(value) + if not qtype: + raise QAPISemError(info, "Alternate '%s' member '%s' cannot use " + "type '%s'" % (name, key, value)) + conflicting = set([qtype]) + if qtype == 'QTYPE_QSTRING': + enum_expr = enum_types.get(value) + if enum_expr: + for v in enum_expr['data']: + if v in ['on', 'off']: + conflicting.add('QTYPE_QBOOL') + if re.match(r'[-+0-9.]', v): # lazy, could be tightened + conflicting.add('QTYPE_QNUM') + else: + conflicting.add('QTYPE_QNUM') + conflicting.add('QTYPE_QBOOL') + for qt in conflicting: + if qt in types_seen: + raise QAPISemError(info, "Alternate '%s' member '%s' can't " + "be distinguished from member '%s'" + % (name, key, types_seen[qt])) + types_seen[qt] = key + + +def check_enum(expr, info): + name = expr['enum'] + members = expr.get('data') + prefix = expr.get('prefix') + + if not isinstance(members, list): + raise QAPISemError(info, + "Enum '%s' requires an array for 'data'" % name) + if prefix is not None and not isinstance(prefix, str): + raise QAPISemError(info, + "Enum '%s' requires a string for 'prefix'" % name) + for member in members: + check_name(info, "Member of enum '%s'" % name, member, + enum_member=True) + + +def check_struct(expr, info): + name = expr['struct'] + members = expr['data'] + + check_type(info, "'data' for struct '%s'" % name, members, + allow_dict=True, allow_optional=True) + check_type(info, "'base' for struct '%s'" % name, expr.get('base'), + allow_metas=['struct']) + + +def check_keys(expr_elem, meta, required, optional=[]): + expr = expr_elem['expr'] + info = expr_elem['info'] + name = expr[meta] + if not isinstance(name, str): + raise QAPISemError(info, "'%s' key must have a string value" % meta) + required = required + [meta] + for (key, value) in expr.items(): + if key not in required and key not in optional: + raise QAPISemError(info, "Unknown key '%s' in %s '%s'" + % (key, meta, name)) + if (key == 'gen' or key == 'success-response') and value is not False: + raise QAPISemError(info, + "'%s' of %s '%s' should only use false value" + % (key, meta, name)) + if key == 'boxed' and value is not True: + raise QAPISemError(info, + "'%s' of %s '%s' should only use true value" + % (key, meta, name)) + for key in required: + if key not in expr: + raise QAPISemError(info, "Key '%s' is missing from %s '%s'" + % (key, meta, name)) + + +def check_exprs(exprs): + global all_names + + # Populate name table with names of built-in types + for builtin in builtin_types.keys(): + all_names[builtin] = 'built-in' + + # Learn the types and check for valid expression keys + for expr_elem in exprs: + expr = expr_elem['expr'] + info = expr_elem['info'] + doc = expr_elem.get('doc') + + if not doc and doc_required: + raise QAPISemError(info, + "Expression missing documentation comment") + + if 'enum' in expr: + meta = 'enum' + check_keys(expr_elem, 'enum', ['data'], ['prefix']) + enum_types[expr[meta]] = expr + elif 'union' in expr: + meta = 'union' + check_keys(expr_elem, 'union', ['data'], + ['base', 'discriminator']) + union_types[expr[meta]] = expr + elif 'alternate' in expr: + meta = 'alternate' + check_keys(expr_elem, 'alternate', ['data']) + elif 'struct' in expr: + meta = 'struct' + check_keys(expr_elem, 'struct', ['data'], ['base']) + struct_types[expr[meta]] = expr + elif 'command' in expr: + meta = 'command' + check_keys(expr_elem, 'command', [], + ['data', 'returns', 'gen', 'success-response', 'boxed']) + elif 'event' in expr: + meta = 'event' + check_keys(expr_elem, 'event', [], ['data', 'boxed']) + else: + raise QAPISemError(expr_elem['info'], + "Expression is missing metatype") + name = expr[meta] + add_name(name, info, meta) + if doc and doc.symbol != name: + raise QAPISemError(info, "Definition of '%s' follows documentation" + " for '%s'" % (name, doc.symbol)) + + # Try again for hidden UnionKind enum + for expr_elem in exprs: + expr = expr_elem['expr'] + if 'union' in expr and not discriminator_find_enum_define(expr): + name = '%sKind' % expr['union'] + elif 'alternate' in expr: + name = '%sKind' % expr['alternate'] + else: + continue + enum_types[name] = {'enum': name} + add_name(name, info, 'enum', implicit=True) + + # Validate that exprs make sense + for expr_elem in exprs: + expr = expr_elem['expr'] + info = expr_elem['info'] + doc = expr_elem.get('doc') + + if 'enum' in expr: + check_enum(expr, info) + elif 'union' in expr: + check_union(expr, info) + elif 'alternate' in expr: + check_alternate(expr, info) + elif 'struct' in expr: + check_struct(expr, info) + elif 'command' in expr: + check_command(expr, info) + elif 'event' in expr: + check_event(expr, info) + else: + assert False, 'unexpected meta type' + + if doc: + doc.check_expr(expr) + + return exprs + + +# +# Schema compiler frontend +# + +class QAPISchemaEntity(object): + def __init__(self, name, info, doc): + assert isinstance(name, str) + self.name = name + # For explicitly defined entities, info points to the (explicit) + # definition. For builtins (and their arrays), info is None. + # For implicitly defined entities, info points to a place that + # triggered the implicit definition (there may be more than one + # such place). + self.info = info + self.doc = doc + + def c_name(self): + return c_name(self.name) + + def check(self, schema): + pass + + def is_implicit(self): + return not self.info + + def visit(self, visitor): + pass + + +class QAPISchemaVisitor(object): + def visit_begin(self, schema): + pass + + def visit_end(self): + pass + + def visit_needed(self, entity): + # Default to visiting everything + return True + + def visit_builtin_type(self, name, info, json_type): + pass + + def visit_enum_type(self, name, info, values, prefix): + pass + + def visit_array_type(self, name, info, element_type): + pass + + def visit_object_type(self, name, info, base, members, variants): + pass + + def visit_object_type_flat(self, name, info, members, variants): + pass + + def visit_alternate_type(self, name, info, variants): + pass + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response, boxed): + pass + + def visit_event(self, name, info, arg_type, boxed): + pass + + +class QAPISchemaType(QAPISchemaEntity): + # Return the C type for common use. + # For the types we commonly box, this is a pointer type. + def c_type(self): + pass + + # Return the C type to be used in a parameter list. + def c_param_type(self): + return self.c_type() + + # Return the C type to be used where we suppress boxing. + def c_unboxed_type(self): + return self.c_type() + + def json_type(self): + pass + + def alternate_qtype(self): + json2qtype = { + 'null': 'QTYPE_QNULL', + 'string': 'QTYPE_QSTRING', + 'number': 'QTYPE_QNUM', + 'int': 'QTYPE_QNUM', + 'boolean': 'QTYPE_QBOOL', + 'object': 'QTYPE_QDICT' + } + return json2qtype.get(self.json_type()) + + def doc_type(self): + if self.is_implicit(): + return None + return self.name + + +class QAPISchemaBuiltinType(QAPISchemaType): + def __init__(self, name, json_type, c_type): + QAPISchemaType.__init__(self, name, None, None) + assert not c_type or isinstance(c_type, str) + assert json_type in ('string', 'number', 'int', 'boolean', 'null', + 'value') + self._json_type_name = json_type + self._c_type_name = c_type + + def c_name(self): + return self.name + + def c_type(self): + return self._c_type_name + + def c_param_type(self): + if self.name == 'str': + return 'const ' + self._c_type_name + return self._c_type_name + + def json_type(self): + return self._json_type_name + + def doc_type(self): + return self.json_type() + + def visit(self, visitor): + visitor.visit_builtin_type(self.name, self.info, self.json_type()) + + +class QAPISchemaEnumType(QAPISchemaType): + def __init__(self, name, info, doc, values, prefix): + QAPISchemaType.__init__(self, name, info, doc) + for v in values: + assert isinstance(v, QAPISchemaMember) + v.set_owner(name) + assert prefix is None or isinstance(prefix, str) + self.values = values + self.prefix = prefix + + def check(self, schema): + seen = {} + for v in self.values: + v.check_clash(self.info, seen) + if self.doc: + self.doc.connect_member(v) + + def is_implicit(self): + # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() + return self.name.endswith('Kind') or self.name == 'QType' + + def c_type(self): + return c_name(self.name) + + def member_names(self): + return [v.name for v in self.values] + + def json_type(self): + return 'string' + + def visit(self, visitor): + visitor.visit_enum_type(self.name, self.info, + self.member_names(), self.prefix) + + +class QAPISchemaArrayType(QAPISchemaType): + def __init__(self, name, info, element_type): + QAPISchemaType.__init__(self, name, info, None) + assert isinstance(element_type, str) + self._element_type_name = element_type + self.element_type = None + + def check(self, schema): + self.element_type = schema.lookup_type(self._element_type_name) + assert self.element_type + + def is_implicit(self): + return True + + def c_type(self): + return c_name(self.name) + pointer_suffix + + def json_type(self): + return 'array' + + def doc_type(self): + elt_doc_type = self.element_type.doc_type() + if not elt_doc_type: + return None + return 'array of ' + elt_doc_type + + def visit(self, visitor): + visitor.visit_array_type(self.name, self.info, self.element_type) + + +class QAPISchemaObjectType(QAPISchemaType): + def __init__(self, name, info, doc, base, local_members, variants): + # struct has local_members, optional base, and no variants + # flat union has base, variants, and no local_members + # simple union has local_members, variants, and no base + QAPISchemaType.__init__(self, name, info, doc) + assert base is None or isinstance(base, str) + for m in local_members: + assert isinstance(m, QAPISchemaObjectTypeMember) + m.set_owner(name) + if variants is not None: + assert isinstance(variants, QAPISchemaObjectTypeVariants) + variants.set_owner(name) + self._base_name = base + self.base = None + self.local_members = local_members + self.variants = variants + self.members = None + + def check(self, schema): + if self.members is False: # check for cycles + raise QAPISemError(self.info, + "Object %s contains itself" % self.name) + if self.members: + return + self.members = False # mark as being checked + seen = OrderedDict() + if self._base_name: + self.base = schema.lookup_type(self._base_name) + assert isinstance(self.base, QAPISchemaObjectType) + self.base.check(schema) + self.base.check_clash(self.info, seen) + for m in self.local_members: + m.check(schema) + m.check_clash(self.info, seen) + if self.doc: + self.doc.connect_member(m) + self.members = seen.values() + if self.variants: + self.variants.check(schema, seen) + assert self.variants.tag_member in self.members + self.variants.check_clash(self.info, seen) + if self.doc: + self.doc.check() + + # Check that the members of this type do not cause duplicate JSON members, + # and update seen to track the members seen so far. Report any errors + # on behalf of info, which is not necessarily self.info + def check_clash(self, info, seen): + assert not self.variants # not implemented + for m in self.members: + m.check_clash(info, seen) + + def is_implicit(self): + # See QAPISchema._make_implicit_object_type(), as well as + # _def_predefineds() + return self.name.startswith('q_') + + def is_empty(self): + assert self.members is not None + return not self.members and not self.variants + + def c_name(self): + assert self.name != 'q_empty' + return QAPISchemaType.c_name(self) + + def c_type(self): + assert not self.is_implicit() + return c_name(self.name) + pointer_suffix + + def c_unboxed_type(self): + return c_name(self.name) + + def json_type(self): + return 'object' + + def visit(self, visitor): + visitor.visit_object_type(self.name, self.info, + self.base, self.local_members, self.variants) + visitor.visit_object_type_flat(self.name, self.info, + self.members, self.variants) + + +class QAPISchemaMember(object): + role = 'member' + + def __init__(self, name): + assert isinstance(name, str) + self.name = name + self.owner = None + + def set_owner(self, name): + assert not self.owner + self.owner = name + + def check_clash(self, info, seen): + cname = c_name(self.name) + if cname.lower() != cname and self.owner not in name_case_whitelist: + raise QAPISemError(info, + "%s should not use uppercase" % self.describe()) + if cname in seen: + raise QAPISemError(info, "%s collides with %s" % + (self.describe(), seen[cname].describe())) + seen[cname] = self + + def _pretty_owner(self): + owner = self.owner + if owner.startswith('q_obj_'): + # See QAPISchema._make_implicit_object_type() - reverse the + # mapping there to create a nice human-readable description + owner = owner[6:] + if owner.endswith('-arg'): + return '(parameter of %s)' % owner[:-4] + elif owner.endswith('-base'): + return '(base of %s)' % owner[:-5] + else: + assert owner.endswith('-wrapper') + # Unreachable and not implemented + assert False + if owner.endswith('Kind'): + # See QAPISchema._make_implicit_enum_type() + return '(branch of %s)' % owner[:-4] + return '(%s of %s)' % (self.role, owner) + + def describe(self): + return "'%s' %s" % (self.name, self._pretty_owner()) + + +class QAPISchemaObjectTypeMember(QAPISchemaMember): + def __init__(self, name, typ, optional): + QAPISchemaMember.__init__(self, name) + assert isinstance(typ, str) + assert isinstance(optional, bool) + self._type_name = typ + self.type = None + self.optional = optional + + def check(self, schema): + assert self.owner + self.type = schema.lookup_type(self._type_name) + assert self.type + + +class QAPISchemaObjectTypeVariants(object): + def __init__(self, tag_name, tag_member, variants): + # Flat unions pass tag_name but not tag_member. + # Simple unions and alternates pass tag_member but not tag_name. + # After check(), tag_member is always set, and tag_name remains + # a reliable witness of being used by a flat union. + assert bool(tag_member) != bool(tag_name) + assert (isinstance(tag_name, str) or + isinstance(tag_member, QAPISchemaObjectTypeMember)) + assert len(variants) > 0 + for v in variants: + assert isinstance(v, QAPISchemaObjectTypeVariant) + self._tag_name = tag_name + self.tag_member = tag_member + self.variants = variants + + def set_owner(self, name): + for v in self.variants: + v.set_owner(name) + + def check(self, schema, seen): + if not self.tag_member: # flat union + self.tag_member = seen[c_name(self._tag_name)] + assert self._tag_name == self.tag_member.name + assert isinstance(self.tag_member.type, QAPISchemaEnumType) + for v in self.variants: + v.check(schema) + # Union names must match enum values; alternate names are + # checked separately. Use 'seen' to tell the two apart. + if seen: + assert v.name in self.tag_member.type.member_names() + assert isinstance(v.type, QAPISchemaObjectType) + v.type.check(schema) + + def check_clash(self, info, seen): + for v in self.variants: + # Reset seen map for each variant, since qapi names from one + # branch do not affect another branch + assert isinstance(v.type, QAPISchemaObjectType) + v.type.check_clash(info, dict(seen)) + + +class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): + role = 'branch' + + def __init__(self, name, typ): + QAPISchemaObjectTypeMember.__init__(self, name, typ, False) + + +class QAPISchemaAlternateType(QAPISchemaType): + def __init__(self, name, info, doc, variants): + QAPISchemaType.__init__(self, name, info, doc) + assert isinstance(variants, QAPISchemaObjectTypeVariants) + assert variants.tag_member + variants.set_owner(name) + variants.tag_member.set_owner(self.name) + self.variants = variants + + def check(self, schema): + self.variants.tag_member.check(schema) + # Not calling self.variants.check_clash(), because there's nothing + # to clash with + self.variants.check(schema, {}) + # Alternate branch names have no relation to the tag enum values; + # so we have to check for potential name collisions ourselves. + seen = {} + for v in self.variants.variants: + v.check_clash(self.info, seen) + if self.doc: + self.doc.connect_member(v) + if self.doc: + self.doc.check() + + def c_type(self): + return c_name(self.name) + pointer_suffix + + def json_type(self): + return 'value' + + def visit(self, visitor): + visitor.visit_alternate_type(self.name, self.info, self.variants) + + def is_empty(self): + return False + + +class QAPISchemaCommand(QAPISchemaEntity): + def __init__(self, name, info, doc, arg_type, ret_type, + gen, success_response, boxed): + QAPISchemaEntity.__init__(self, name, info, doc) + assert not arg_type or isinstance(arg_type, str) + assert not ret_type or isinstance(ret_type, str) + self._arg_type_name = arg_type + self.arg_type = None + self._ret_type_name = ret_type + self.ret_type = None + self.gen = gen + self.success_response = success_response + self.boxed = boxed + + def check(self, schema): + if self._arg_type_name: + self.arg_type = schema.lookup_type(self._arg_type_name) + assert (isinstance(self.arg_type, QAPISchemaObjectType) or + isinstance(self.arg_type, QAPISchemaAlternateType)) + self.arg_type.check(schema) + if self.boxed: + if self.arg_type.is_empty(): + raise QAPISemError(self.info, + "Cannot use 'boxed' with empty type") + else: + assert not isinstance(self.arg_type, QAPISchemaAlternateType) + assert not self.arg_type.variants + elif self.boxed: + raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") + if self._ret_type_name: + self.ret_type = schema.lookup_type(self._ret_type_name) + assert isinstance(self.ret_type, QAPISchemaType) + + def visit(self, visitor): + visitor.visit_command(self.name, self.info, + self.arg_type, self.ret_type, + self.gen, self.success_response, self.boxed) + + +class QAPISchemaEvent(QAPISchemaEntity): + def __init__(self, name, info, doc, arg_type, boxed): + QAPISchemaEntity.__init__(self, name, info, doc) + assert not arg_type or isinstance(arg_type, str) + self._arg_type_name = arg_type + self.arg_type = None + self.boxed = boxed + + def check(self, schema): + if self._arg_type_name: + self.arg_type = schema.lookup_type(self._arg_type_name) + assert (isinstance(self.arg_type, QAPISchemaObjectType) or + isinstance(self.arg_type, QAPISchemaAlternateType)) + self.arg_type.check(schema) + if self.boxed: + if self.arg_type.is_empty(): + raise QAPISemError(self.info, + "Cannot use 'boxed' with empty type") + else: + assert not isinstance(self.arg_type, QAPISchemaAlternateType) + assert not self.arg_type.variants + elif self.boxed: + raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") + + def visit(self, visitor): + visitor.visit_event(self.name, self.info, self.arg_type, self.boxed) + + +class QAPISchema(object): + def __init__(self, fname): + try: + parser = QAPISchemaParser(open(fname, 'r')) + self.exprs = check_exprs(parser.exprs) + self.docs = parser.docs + self._entity_dict = {} + self._predefining = True + self._def_predefineds() + self._predefining = False + self._def_exprs() + self.check() + except QAPIError as err: + print(err, file=sys.stderr) + exit(1) + + def _def_entity(self, ent): + # Only the predefined types are allowed to not have info + assert ent.info or self._predefining + assert ent.name not in self._entity_dict + self._entity_dict[ent.name] = ent + + def lookup_entity(self, name, typ=None): + ent = self._entity_dict.get(name) + if typ and not isinstance(ent, typ): + return None + return ent + + def lookup_type(self, name): + return self.lookup_entity(name, QAPISchemaType) + + def _def_builtin_type(self, name, json_type, c_type): + self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) + # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple + # qapi-types.h from a single .c, all arrays of builtins must be + # declared in the first file whether or not they are used. Nicer + # would be to use lazy instantiation, while figuring out how to + # avoid compilation issues with multiple qapi-types.h. + self._make_array_type(name, None) + + def _def_predefineds(self): + for t in [('str', 'string', 'char' + pointer_suffix), + ('number', 'number', 'double'), + ('int', 'int', 'int64_t'), + ('int8', 'int', 'int8_t'), + ('int16', 'int', 'int16_t'), + ('int32', 'int', 'int32_t'), + ('int64', 'int', 'int64_t'), + ('uint8', 'int', 'uint8_t'), + ('uint16', 'int', 'uint16_t'), + ('uint32', 'int', 'uint32_t'), + ('uint64', 'int', 'uint64_t'), + ('size', 'int', 'uint64_t'), + ('bool', 'boolean', 'bool'), + ('any', 'value', 'QObject' + pointer_suffix), + ('null', 'null', 'QNull' + pointer_suffix)]: + self._def_builtin_type(*t) + self.the_empty_object_type = QAPISchemaObjectType( + 'q_empty', None, None, None, [], None) + self._def_entity(self.the_empty_object_type) + qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', + 'qstring', 'qdict', 'qlist', + 'qbool']) + self._def_entity(QAPISchemaEnumType('QType', None, None, + qtype_values, 'QTYPE')) + + def _make_enum_members(self, values): + return [QAPISchemaMember(v) for v in values] + + def _make_implicit_enum_type(self, name, info, values): + # See also QAPISchemaObjectTypeMember._pretty_owner() + name = name + 'Kind' # Use namespace reserved by add_name() + self._def_entity(QAPISchemaEnumType( + name, info, None, self._make_enum_members(values), None)) + return name + + def _make_array_type(self, element_type, info): + name = element_type + 'List' # Use namespace reserved by add_name() + if not self.lookup_type(name): + self._def_entity(QAPISchemaArrayType(name, info, element_type)) + return name + + def _make_implicit_object_type(self, name, info, doc, role, members): + if not members: + return None + # See also QAPISchemaObjectTypeMember._pretty_owner() + name = 'q_obj_%s-%s' % (name, role) + if not self.lookup_entity(name, QAPISchemaObjectType): + self._def_entity(QAPISchemaObjectType(name, info, doc, None, + members, None)) + return name + + def _def_enum_type(self, expr, info, doc): + name = expr['enum'] + data = expr['data'] + prefix = expr.get('prefix') + self._def_entity(QAPISchemaEnumType( + name, info, doc, self._make_enum_members(data), prefix)) + + def _make_member(self, name, typ, info): + optional = False + if name.startswith('*'): + name = name[1:] + optional = True + if isinstance(typ, list): + assert len(typ) == 1 + typ = self._make_array_type(typ[0], info) + return QAPISchemaObjectTypeMember(name, typ, optional) + + def _make_members(self, data, info): + return [self._make_member(key, value, info) + for (key, value) in data.items()] + + def _def_struct_type(self, expr, info, doc): + name = expr['struct'] + base = expr.get('base') + data = expr['data'] + self._def_entity(QAPISchemaObjectType(name, info, doc, base, + self._make_members(data, info), + None)) + + def _make_variant(self, case, typ): + return QAPISchemaObjectTypeVariant(case, typ) + + def _make_simple_variant(self, case, typ, info): + if isinstance(typ, list): + assert len(typ) == 1 + typ = self._make_array_type(typ[0], info) + typ = self._make_implicit_object_type( + typ, info, None, 'wrapper', [self._make_member('data', typ, info)]) + return QAPISchemaObjectTypeVariant(case, typ) + + def _def_union_type(self, expr, info, doc): + name = expr['union'] + data = expr['data'] + base = expr.get('base') + tag_name = expr.get('discriminator') + tag_member = None + if isinstance(base, dict): + base = (self._make_implicit_object_type( + name, info, doc, 'base', self._make_members(base, info))) + if tag_name: + variants = [self._make_variant(key, value) + for (key, value) in data.items()] + members = [] + else: + variants = [self._make_simple_variant(key, value, info) + for (key, value) in data.items()] + typ = self._make_implicit_enum_type(name, info, + [v.name for v in variants]) + tag_member = QAPISchemaObjectTypeMember('type', typ, False) + members = [tag_member] + self._def_entity( + QAPISchemaObjectType(name, info, doc, base, members, + QAPISchemaObjectTypeVariants(tag_name, + tag_member, + variants))) + + def _def_alternate_type(self, expr, info, doc): + name = expr['alternate'] + data = expr['data'] + variants = [self._make_variant(key, value) + for (key, value) in data.items()] + tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) + self._def_entity( + QAPISchemaAlternateType(name, info, doc, + QAPISchemaObjectTypeVariants(None, + tag_member, + variants))) + + def _def_command(self, expr, info, doc): + name = expr['command'] + data = expr.get('data') + rets = expr.get('returns') + gen = expr.get('gen', True) + success_response = expr.get('success-response', True) + boxed = expr.get('boxed', False) + if isinstance(data, OrderedDict): + data = self._make_implicit_object_type( + name, info, doc, 'arg', self._make_members(data, info)) + if isinstance(rets, list): + assert len(rets) == 1 + rets = self._make_array_type(rets[0], info) + self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, + gen, success_response, boxed)) + + def _def_event(self, expr, info, doc): + name = expr['event'] + data = expr.get('data') + boxed = expr.get('boxed', False) + if isinstance(data, OrderedDict): + data = self._make_implicit_object_type( + name, info, doc, 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) + + def _def_exprs(self): + for expr_elem in self.exprs: + expr = expr_elem['expr'] + info = expr_elem['info'] + doc = expr_elem.get('doc') + if 'enum' in expr: + self._def_enum_type(expr, info, doc) + elif 'struct' in expr: + self._def_struct_type(expr, info, doc) + elif 'union' in expr: + self._def_union_type(expr, info, doc) + elif 'alternate' in expr: + self._def_alternate_type(expr, info, doc) + elif 'command' in expr: + self._def_command(expr, info, doc) + elif 'event' in expr: + self._def_event(expr, info, doc) + else: + assert False + + def check(self): + for (name, ent) in sorted(self._entity_dict.items()): + ent.check(self) + + def visit(self, visitor): + visitor.visit_begin(self) + for (name, entity) in sorted(self._entity_dict.items()): + if visitor.visit_needed(entity): + entity.visit(visitor) + visitor.visit_end() + + +# +# Code generation helpers +# + +def camel_case(name): + new_name = '' + first = True + for ch in name: + if ch in ['_', '-']: + first = True + elif first: + new_name += ch.upper() + first = False + else: + new_name += ch.lower() + return new_name + + +# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 +# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 +# ENUM24_Name -> ENUM24_NAME +def camel_to_upper(value): + c_fun_str = c_name(value, False) + if value.isupper(): + return c_fun_str + + new_name = '' + l = len(c_fun_str) + for i in range(l): + c = c_fun_str[i] + # When c is upper and no '_' appears before, do more checks + if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_': + if i < l - 1 and c_fun_str[i + 1].islower(): + new_name += '_' + elif c_fun_str[i - 1].isdigit(): + new_name += '_' + new_name += c + return new_name.lstrip('_').upper() + + +def c_enum_const(type_name, const_name, prefix=None): + if prefix is not None: + type_name = prefix + return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() + +if hasattr(str, 'maketrans'): + c_name_trans = str.maketrans('.-', '__') +else: + c_name_trans = string.maketrans('.-', '__') + + +# Map @name to a valid C identifier. +# If @protect, avoid returning certain ticklish identifiers (like +# C keywords) by prepending 'q_'. +# +# Used for converting 'name' from a 'name':'type' qapi definition +# into a generated struct member, as well as converting type names +# into substrings of a generated C function name. +# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo' +# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int' +def c_name(name, protect=True): + # ANSI X3J11/88-090, 3.1.1 + c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', + 'default', 'do', 'double', 'else', 'enum', 'extern', + 'float', 'for', 'goto', 'if', 'int', 'long', 'register', + 'return', 'short', 'signed', 'sizeof', 'static', + 'struct', 'switch', 'typedef', 'union', 'unsigned', + 'void', 'volatile', 'while']) + # ISO/IEC 9899:1999, 6.4.1 + c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) + # ISO/IEC 9899:2011, 6.4.1 + c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', + '_Noreturn', '_Static_assert', '_Thread_local']) + # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html + # excluding _.* + gcc_words = set(['asm', 'typeof']) + # C++ ISO/IEC 14882:2003 2.11 + cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', + 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', + 'namespace', 'new', 'operator', 'private', 'protected', + 'public', 'reinterpret_cast', 'static_cast', 'template', + 'this', 'throw', 'true', 'try', 'typeid', 'typename', + 'using', 'virtual', 'wchar_t', + # alternative representations + 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', + 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) + # namespace pollution: + polluted_words = set(['unix', 'errno', 'mips', 'sparc']) + name = name.translate(c_name_trans) + if protect and (name in c89_words | c99_words | c11_words | gcc_words + | cpp_words | polluted_words): + return 'q_' + name + return name + +eatspace = '\033EATSPACE.' +pointer_suffix = ' *' + eatspace + + +def genindent(count): + ret = '' + for _ in range(count): + ret += ' ' + return ret + +indent_level = 0 + + +def push_indent(indent_amount=4): + global indent_level + indent_level += indent_amount + + +def pop_indent(indent_amount=4): + global indent_level + indent_level -= indent_amount + + +# Generate @code with @kwds interpolated. +# Obey indent_level, and strip eatspace. +def cgen(code, **kwds): + raw = code % kwds + if indent_level: + indent = genindent(indent_level) + # re.subn() lacks flags support before Python 2.7, use re.compile() + raw = re.subn(re.compile(r'^.', re.MULTILINE), + indent + r'\g<0>', raw) + raw = raw[0] + return re.sub(re.escape(eatspace) + r' *', '', raw) + + +def mcgen(code, **kwds): + if code[0] == '\n': + code = code[1:] + return cgen(code, **kwds) + + +def guardname(filename): + return c_name(filename, protect=False).upper() + + +def guardstart(name): + return mcgen(''' +#ifndef %(name)s +#define %(name)s + +''', + name=guardname(name)) + + +def guardend(name): + return mcgen(''' + +#endif /* %(name)s */ +''', + name=guardname(name)) + + +def gen_enum_lookup(name, values, prefix=None): + ret = mcgen(''' + +const QEnumLookup %(c_name)s_lookup = { + .array = (const char *const[]) { +''', + c_name=c_name(name)) + for value in values: + index = c_enum_const(name, value, prefix) + ret += mcgen(''' + [%(index)s] = "%(value)s", +''', + index=index, value=value) + + ret += mcgen(''' + }, + .size = %(max_index)s +}; +''', + max_index=c_enum_const(name, '_MAX', prefix)) + return ret + + +def gen_enum(name, values, prefix=None): + # append automatically generated _MAX value + enum_values = values + ['_MAX'] + + ret = mcgen(''' + +typedef enum %(c_name)s { +''', + c_name=c_name(name)) + + i = 0 + for value in enum_values: + ret += mcgen(''' + %(c_enum)s = %(i)d, +''', + c_enum=c_enum_const(name, value, prefix), + i=i) + i += 1 + + ret += mcgen(''' +} %(c_name)s; +''', + c_name=c_name(name)) + + ret += mcgen(''' + +#define %(c_name)s_str(val) \\ + qapi_enum_lookup(&%(c_name)s_lookup, (val)) + +extern const QEnumLookup %(c_name)s_lookup; +''', + c_name=c_name(name)) + return ret + + +def build_params(arg_type, boxed, extra): + if not arg_type: + assert not boxed + return extra + ret = '' + sep = '' + if boxed: + ret += '%s arg' % arg_type.c_param_type() + sep = ', ' + else: + assert not arg_type.variants + for memb in arg_type.members: + ret += sep + sep = ', ' + if memb.optional: + ret += 'bool has_%s, ' % c_name(memb.name) + ret += '%s %s' % (memb.type.c_param_type(), + c_name(memb.name)) + if extra: + ret += sep + extra + return ret + + +# +# Common command line parsing +# + + +def parse_command_line(extra_options='', extra_long_options=[]): + + try: + opts, args = getopt.gnu_getopt(sys.argv[1:], + 'p:o:' + extra_options, + ['prefix=', 'output-dir='] + + extra_long_options) + except getopt.GetoptError as err: + print("%s: %s" % (sys.argv[0], str(err)), file=sys.stderr) + sys.exit(1) + + output_dir = '' + prefix = '' + extra_opts = [] + + for oa in opts: + o, a = oa + if o in ('-p', '--prefix'): + match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a) + if match.end() != len(a): + print("%s: 'funny character '%s' in argument of --prefix" \ + % (sys.argv[0], a[match.end()]), file=sys.stderr) + sys.exit(1) + prefix = a + elif o in ('-o', '--output-dir'): + output_dir = a + '/' + else: + extra_opts.append(oa) + + if len(args) != 1: + print("%s: need exactly one argument" % sys.argv[0], file=sys.stderr) + sys.exit(1) + fname = args[0] + + return (fname, output_dir, prefix, extra_opts) + + +# +# Accumulate and write output +# + +class QAPIGen(object): + + def __init__(self): + self._preamble = '' + self._body = '' + + def preamble_add(self, text): + self._preamble += text + + def add(self, text): + self._body += text + + def _top(self, fname): + return '' + + def _bottom(self, fname): + return '' + + def write(self, output_dir, fname): + if output_dir: + try: + os.makedirs(output_dir) + except os.error as e: + if e.errno != errno.EEXIST: + raise + f = open(os.path.join(output_dir, fname), 'w') + f.write(self._top(fname) + self._preamble + self._body + + self._bottom(fname)) + f.close() + + +class QAPIGenC(QAPIGen): + + def __init__(self, blurb, pydoc): + QAPIGen.__init__(self) + self._blurb = blurb + self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, + re.MULTILINE)) + + def _top(self, fname): + return mcgen(''' +/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* +%(blurb)s + * + * %(copyright)s + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +''', + blurb=self._blurb, copyright=self._copyright) + + +class QAPIGenH(QAPIGenC): + + def _top(self, fname): + return QAPIGenC._top(self, fname) + guardstart(fname) + + def _bottom(self, fname): + return guardend(fname) + + +class QAPIGenDoc(QAPIGen): + + def _top(self, fname): + return (QAPIGen._top(self, fname) + + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') -- cgit 1.4.1 From 3b446a1817289d89844ad77c719bdc44bbcd1198 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:35:47 +0100 Subject: qapi-gen: Convert from getopt to argparse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit argparse is nicer to use than getopt, and gives us --help almost for free. Signed-off-by: Markus Armbruster Message-Id: <20180211093607.27351-10-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Marc-André Lureau [eblake: Fix --output-dir editing accident] Signed-off-by: Eric Blake --- scripts/qapi-gen.py | 54 ++++++++++++++++++++++++++++++-------------------- scripts/qapi/common.py | 43 ---------------------------------------- 2 files changed, 33 insertions(+), 64 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi-gen.py b/scripts/qapi-gen.py index 2100ca1145..cb56ba7cff 100755 --- a/scripts/qapi-gen.py +++ b/scripts/qapi-gen.py @@ -4,8 +4,11 @@ # This work is licensed under the terms of the GNU GPL, version 2 or later. # See the COPYING file in the top-level directory. +from __future__ import print_function +import argparse +import re import sys -from qapi.common import parse_command_line, QAPISchema +from qapi.common import QAPISchema from qapi.types import gen_types from qapi.visit import gen_visit from qapi.commands import gen_commands @@ -15,26 +18,35 @@ from qapi.doc import gen_doc def main(argv): - (input_file, output_dir, prefix, opts) = \ - parse_command_line('bu', ['builtins', 'unmask-non-abi-names']) - - opt_builtins = False - opt_unmask = False - - for o, a in opts: - if o in ('-b', '--builtins'): - opt_builtins = True - if o in ('-u', '--unmask-non-abi-names'): - opt_unmask = True - - schema = QAPISchema(input_file) - - gen_types(schema, output_dir, prefix, opt_builtins) - gen_visit(schema, output_dir, prefix, opt_builtins) - gen_commands(schema, output_dir, prefix) - gen_events(schema, output_dir, prefix) - gen_introspect(schema, output_dir, prefix, opt_unmask) - gen_doc(schema, output_dir, prefix) + parser = argparse.ArgumentParser( + description='Generate code from a QAPI schema') + parser.add_argument('-b', '--builtins', action='store_true', + help="generate code for built-in types") + parser.add_argument('-o', '--output-dir', action='store', default='', + help="write output to directory OUTPUT_DIR") + parser.add_argument('-p', '--prefix', action='store', default='', + help="prefix for symbols") + parser.add_argument('-u', '--unmask-non-abi-names', action='store_true', + dest='unmask', + help="expose non-ABI names in introspection") + parser.add_argument('schema', action='store') + args = parser.parse_args() + + match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', args.prefix) + if match.end() != len(args.prefix): + print("%s: 'funny character '%s' in argument of --prefix" + % (sys.argv[0], args.prefix[match.end()]), + file=sys.stderr) + sys.exit(1) + + schema = QAPISchema(args.schema) + + gen_types(schema, args.output_dir, args.prefix, args.builtins) + gen_visit(schema, args.output_dir, args.prefix, args.builtins) + gen_commands(schema, args.output_dir, args.prefix) + gen_events(schema, args.output_dir, args.prefix) + gen_introspect(schema, args.output_dir, args.prefix, args.unmask) + gen_doc(schema, args.output_dir, args.prefix) if __name__ == '__main__': diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 3bc31a03ce..c3ae590202 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -13,7 +13,6 @@ from __future__ import print_function import errno -import getopt import os import re import string @@ -1923,48 +1922,6 @@ def build_params(arg_type, boxed, extra): return ret -# -# Common command line parsing -# - - -def parse_command_line(extra_options='', extra_long_options=[]): - - try: - opts, args = getopt.gnu_getopt(sys.argv[1:], - 'p:o:' + extra_options, - ['prefix=', 'output-dir='] - + extra_long_options) - except getopt.GetoptError as err: - print("%s: %s" % (sys.argv[0], str(err)), file=sys.stderr) - sys.exit(1) - - output_dir = '' - prefix = '' - extra_opts = [] - - for oa in opts: - o, a = oa - if o in ('-p', '--prefix'): - match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a) - if match.end() != len(a): - print("%s: 'funny character '%s' in argument of --prefix" \ - % (sys.argv[0], a[match.end()]), file=sys.stderr) - sys.exit(1) - prefix = a - elif o in ('-o', '--output-dir'): - output_dir = a + '/' - else: - extra_opts.append(oa) - - if len(args) != 1: - print("%s: need exactly one argument" % sys.argv[0], file=sys.stderr) - sys.exit(1) - fname = args[0] - - return (fname, output_dir, prefix, extra_opts) - - # # Accumulate and write output # -- cgit 1.4.1 From 907b846653fb3757bf2ab98d6d66f92df34d875f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:35:48 +0100 Subject: qapi: Touch generated files only when they change A massive number of objects depends on QAPI-generated headers. In my "build everything" tree, it's roughly 4800 out of 5100. This is particularly annoying when only some of the generated files change, say for a doc fix. Improve qapi-gen.py to touch its output files only if they actually change. Rebuild time for a QAPI doc fix drops from many minutes to a few seconds. Rebuilds get faster for certain code changes, too. For instance, adding a simple QMP event now recompiles less than 200 instead of 4800 objects. But adding a QAPI type is as bad as ever; we've clearly got more work to do. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180211093607.27351-11-armbru@redhat.com> Reviewed-by: Michael Roth [eblake: fix octal constant for python3] Signed-off-by: Eric Blake --- scripts/qapi/common.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index c3ae590202..47673928dc 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1951,9 +1951,16 @@ class QAPIGen(object): except os.error as e: if e.errno != errno.EEXIST: raise - f = open(os.path.join(output_dir, fname), 'w') - f.write(self._top(fname) + self._preamble + self._body + fd = os.open(os.path.join(output_dir, fname), + os.O_RDWR | os.O_CREAT, 0o666) + f = os.fdopen(fd, 'r+') + text = (self._top(fname) + self._preamble + self._body + self._bottom(fname)) + oldtext = f.read(len(text) + 1) + if text != oldtext: + f.seek(0) + f.truncate(0) + f.write(text) f.close() -- cgit 1.4.1 From af97502ce9c648ae5c746b9e562d6e4586f02eee Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:35:49 +0100 Subject: qapi: Improve include file name reporting in error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Error messages print absolute file names of included files even if the user gave a relative one on the command line: $ PYTHONPATH=scripts python -B tests/qapi-schema/test-qapi.py tests/qapi-schema/include-cycle.json In file included from tests/qapi-schema/include-cycle.json:1: In file included from /work/armbru/qemu/tests/qapi-schema/include-cycle-b.json:1: /work/armbru/qemu/tests/qapi-schema/include-cycle-c.json:1: Inclusion loop for include-cycle.json Improve this to In file included from tests/qapi-schema/include-cycle.json:1: In file included from tests/qapi-schema/include-cycle-b.json:1: tests/qapi-schema/include-cycle-c.json:1: Inclusion loop for include-cycle.json The error message when an include file can't be opened prints the include directive's file name, which is relative to the including file. Change this to print the file name relative to the working directory. Visible in tests/qapi-schema/include-no-file.err. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Marc-André Lureau Message-Id: <20180211093607.27351-12-armbru@redhat.com> Reviewed-by: Michael Roth Signed-off-by: Eric Blake --- scripts/qapi/common.py | 12 ++++++------ tests/qapi-schema/include-no-file.err | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 47673928dc..bfa9bfec01 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -259,9 +259,8 @@ class QAPIDoc(object): class QAPISchemaParser(object): def __init__(self, fp, previously_included=[], incl_info=None): - abs_fname = os.path.abspath(fp.name) self.fname = fp.name - previously_included.append(abs_fname) + previously_included.append(os.path.abspath(fp.name)) self.incl_info = incl_info self.src = fp.read() if self.src == '' or self.src[-1] != '\n': @@ -292,7 +291,7 @@ class QAPISchemaParser(object): if not isinstance(include, str): raise QAPISemError(info, "Value of 'include' must be a string") - self._include(include, info, os.path.dirname(abs_fname), + self._include(include, info, os.path.dirname(self.fname), previously_included) elif "pragma" in expr: self.reject_expr_doc(cur_doc) @@ -325,7 +324,8 @@ class QAPISchemaParser(object): % doc.symbol) def _include(self, include, info, base_dir, previously_included): - incl_abs_fname = os.path.join(base_dir, include) + incl_fname = os.path.join(base_dir, include) + incl_abs_fname = os.path.abspath(incl_fname) # catch inclusion cycle inf = info while inf: @@ -337,9 +337,9 @@ class QAPISchemaParser(object): if incl_abs_fname in previously_included: return try: - fobj = open(incl_abs_fname, 'r') + fobj = open(incl_fname, 'r') except IOError as e: - raise QAPISemError(info, '%s: %s' % (e.strerror, include)) + raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname)) exprs_include = QAPISchemaParser(fobj, previously_included, info) self.exprs.extend(exprs_include.exprs) self.docs.extend(exprs_include.docs) diff --git a/tests/qapi-schema/include-no-file.err b/tests/qapi-schema/include-no-file.err index d5b9b22d85..e42bcf4bc1 100644 --- a/tests/qapi-schema/include-no-file.err +++ b/tests/qapi-schema/include-no-file.err @@ -1 +1 @@ -tests/qapi-schema/include-no-file.json:1: No such file or directory: include-no-file-sub.json +tests/qapi-schema/include-no-file.json:1: No such file or directory: tests/qapi-schema/include-no-file-sub.json -- cgit 1.4.1 From 71a7510baf8a5745eb844ab289f007ff8bbbee41 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:35:50 +0100 Subject: qapi/common: Eliminate QAPISchema.exprs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Marc-André Lureau Message-Id: <20180211093607.27351-13-armbru@redhat.com> Reviewed-by: Michael Roth Signed-off-by: Eric Blake --- scripts/qapi/common.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index bfa9bfec01..60a4748606 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1461,13 +1461,13 @@ class QAPISchema(object): def __init__(self, fname): try: parser = QAPISchemaParser(open(fname, 'r')) - self.exprs = check_exprs(parser.exprs) + exprs = check_exprs(parser.exprs) self.docs = parser.docs self._entity_dict = {} self._predefining = True self._def_predefineds() self._predefining = False - self._def_exprs() + self._def_exprs(exprs) self.check() except QAPIError as err: print(err, file=sys.stderr) @@ -1652,8 +1652,8 @@ class QAPISchema(object): name, info, doc, 'arg', self._make_members(data, info)) self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) - def _def_exprs(self): - for expr_elem in self.exprs: + def _def_exprs(self, exprs): + for expr_elem in exprs: expr = expr_elem['expr'] info = expr_elem['info'] doc = expr_elem.get('doc') -- cgit 1.4.1 From 181feaf3555136dd7883e2434c4498ca1939bf1a Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:35:51 +0100 Subject: qapi: Lift error reporting from QAPISchema.__init__() to callers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20180211093607.27351-14-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Michael Roth Signed-off-by: Eric Blake --- scripts/qapi-gen.py | 8 ++++++-- scripts/qapi/common.py | 23 +++++++++-------------- tests/qapi-schema/test-qapi.py | 10 ++++++++-- 3 files changed, 23 insertions(+), 18 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi-gen.py b/scripts/qapi-gen.py index cb56ba7cff..3d98ca2e0c 100755 --- a/scripts/qapi-gen.py +++ b/scripts/qapi-gen.py @@ -8,7 +8,7 @@ from __future__ import print_function import argparse import re import sys -from qapi.common import QAPISchema +from qapi.common import QAPIError, QAPISchema from qapi.types import gen_types from qapi.visit import gen_visit from qapi.commands import gen_commands @@ -39,7 +39,11 @@ def main(argv): file=sys.stderr) sys.exit(1) - schema = QAPISchema(args.schema) + try: + schema = QAPISchema(args.schema) + except QAPIError as err: + print(err, file=sys.stderr) + exit(1) gen_types(schema, args.output_dir, args.prefix, args.builtins) gen_visit(schema, args.output_dir, args.prefix, args.builtins) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 60a4748606..4cc4d3ee8f 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -16,7 +16,6 @@ import errno import os import re import string -import sys try: from collections import OrderedDict except: @@ -1459,19 +1458,15 @@ class QAPISchemaEvent(QAPISchemaEntity): class QAPISchema(object): def __init__(self, fname): - try: - parser = QAPISchemaParser(open(fname, 'r')) - exprs = check_exprs(parser.exprs) - self.docs = parser.docs - self._entity_dict = {} - self._predefining = True - self._def_predefineds() - self._predefining = False - self._def_exprs(exprs) - self.check() - except QAPIError as err: - print(err, file=sys.stderr) - exit(1) + parser = QAPISchemaParser(open(fname, 'r')) + exprs = check_exprs(parser.exprs) + self.docs = parser.docs + self._entity_dict = {} + self._predefining = True + self._def_predefineds() + self._predefining = False + self._def_exprs(exprs) + self.check() def _def_entity(self, ent): # Only the predefined types are allowed to not have info diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index bb1b6dd297..4da14b43af 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -12,7 +12,7 @@ from __future__ import print_function import sys -from qapi.common import QAPISchema, QAPISchemaVisitor +from qapi.common import QAPIError, QAPISchema, QAPISchemaVisitor class QAPISchemaTestVisitor(QAPISchemaVisitor): @@ -52,7 +52,13 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): for v in variants.variants: print(' case %s: %s' % (v.name, v.type.name)) -schema = QAPISchema(sys.argv[1]) + +try: + schema = QAPISchema(sys.argv[1]) +except QAPIError as err: + print(err, file=sys.stderr) + exit(1) + schema.visit(QAPISchemaTestVisitor()) for doc in schema.docs: -- cgit 1.4.1 From 4257053083775c1f670fa828003915e25d13e9d7 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:35:52 +0100 Subject: qapi: Concentrate QAPISchemaParser.exprs updates in .__init__() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20180211093607.27351-15-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Michael Roth Signed-off-by: Eric Blake --- scripts/qapi/common.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 4cc4d3ee8f..1d0d30f9ec 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -290,8 +290,12 @@ class QAPISchemaParser(object): if not isinstance(include, str): raise QAPISemError(info, "Value of 'include' must be a string") - self._include(include, info, os.path.dirname(self.fname), - previously_included) + exprs_include = self._include(include, info, + os.path.dirname(self.fname), + previously_included) + if exprs_include: + self.exprs.extend(exprs_include.exprs) + self.docs.extend(exprs_include.docs) elif "pragma" in expr: self.reject_expr_doc(cur_doc) if len(expr) != 1: @@ -334,14 +338,13 @@ class QAPISchemaParser(object): # skip multiple include of the same file if incl_abs_fname in previously_included: - return + return None + try: fobj = open(incl_fname, 'r') except IOError as e: raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname)) - exprs_include = QAPISchemaParser(fobj, previously_included, info) - self.exprs.extend(exprs_include.exprs) - self.docs.extend(exprs_include.docs) + return QAPISchemaParser(fobj, previously_included, info) def _pragma(self, name, value, info): global doc_required, returns_whitelist, name_case_whitelist -- cgit 1.4.1 From 97f0249474d19c1d60fb9d934c8bc08625a619ca Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:35:53 +0100 Subject: qapi: Record 'include' directives in parse tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parse tree is a list of expressions. Except include expressions currently get replaced by the included file's parse tree. Instead of throwing away the include expression, keep it with the file name expanded so you don't have to track the including file's directory to make sense of it. A future commit will put this include expression to use. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20180211093607.27351-16-armbru@redhat.com> Reviewed-by: Eric Blake [eblake: fix check of expr after assignment] Signed-off-by: Eric Blake --- scripts/qapi/common.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 1d0d30f9ec..961331a210 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -290,8 +290,11 @@ class QAPISchemaParser(object): if not isinstance(include, str): raise QAPISemError(info, "Value of 'include' must be a string") - exprs_include = self._include(include, info, - os.path.dirname(self.fname), + incl_fname = os.path.join(os.path.dirname(self.fname), + include) + self.exprs.append({'expr': {'include': incl_fname}, + 'info': info}) + exprs_include = self._include(include, info, incl_fname, previously_included) if exprs_include: self.exprs.extend(exprs_include.exprs) @@ -326,8 +329,7 @@ class QAPISchemaParser(object): "Documentation for '%s' is not followed by the definition" % doc.symbol) - def _include(self, include, info, base_dir, previously_included): - incl_fname = os.path.join(base_dir, include) + def _include(self, include, info, incl_fname, previously_included): incl_abs_fname = os.path.abspath(incl_fname) # catch inclusion cycle inf = info @@ -893,6 +895,9 @@ def check_exprs(exprs): info = expr_elem['info'] doc = expr_elem.get('doc') + if 'include' in expr: + continue + if not doc and doc_required: raise QAPISemError(info, "Expression missing documentation comment") @@ -932,6 +937,9 @@ def check_exprs(exprs): # Try again for hidden UnionKind enum for expr_elem in exprs: expr = expr_elem['expr'] + + if 'include' in expr: + continue if 'union' in expr and not discriminator_find_enum_define(expr): name = '%sKind' % expr['union'] elif 'alternate' in expr: @@ -947,6 +955,8 @@ def check_exprs(exprs): info = expr_elem['info'] doc = expr_elem.get('doc') + if 'include' in expr: + continue if 'enum' in expr: check_enum(expr, info) elif 'union' in expr: @@ -1667,6 +1677,8 @@ class QAPISchema(object): self._def_command(expr, info, doc) elif 'event' in expr: self._def_event(expr, info, doc) + elif 'include' in expr: + pass else: assert False -- cgit 1.4.1 From 8a84767cc4f7e00e5dd62435c32be9e7d2cbe4d3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:35:54 +0100 Subject: qapi: Generate in source order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generators' conversion to visitors (merge commit 9e72681d16) changed the processing order of entities from source order to alphabetical order. The next commit needs source order, so change it back. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20180211093607.27351-17-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Michael Roth Signed-off-by: Eric Blake --- scripts/qapi/common.py | 6 +- tests/qapi-schema/comments.out | 2 +- tests/qapi-schema/doc-bad-section.out | 4 +- tests/qapi-schema/doc-good.out | 32 ++-- tests/qapi-schema/empty.out | 2 +- tests/qapi-schema/event-case.out | 2 +- tests/qapi-schema/ident-with-escape.out | 6 +- tests/qapi-schema/include-relpath.out | 2 +- tests/qapi-schema/include-repetition.out | 2 +- tests/qapi-schema/include-simple.out | 2 +- tests/qapi-schema/indented-expr.out | 2 +- tests/qapi-schema/qapi-schema-test.out | 318 +++++++++++++++---------------- 12 files changed, 191 insertions(+), 189 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 961331a210..1aa1cd3b9b 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1474,6 +1474,7 @@ class QAPISchema(object): parser = QAPISchemaParser(open(fname, 'r')) exprs = check_exprs(parser.exprs) self.docs = parser.docs + self._entity_list = [] self._entity_dict = {} self._predefining = True self._def_predefineds() @@ -1485,6 +1486,7 @@ class QAPISchema(object): # Only the predefined types are allowed to not have info assert ent.info or self._predefining assert ent.name not in self._entity_dict + self._entity_list.append(ent) self._entity_dict[ent.name] = ent def lookup_entity(self, name, typ=None): @@ -1683,12 +1685,12 @@ class QAPISchema(object): assert False def check(self): - for (name, ent) in sorted(self._entity_dict.items()): + for ent in self._entity_list: ent.check(self) def visit(self, visitor): visitor.visit_begin(self) - for (name, entity) in sorted(self._entity_dict.items()): + for entity in self._entity_list: if visitor.visit_needed(entity): entity.visit(visitor) visitor.visit_end() diff --git a/tests/qapi-schema/comments.out b/tests/qapi-schema/comments.out index 17e652535c..0261ddf202 100644 --- a/tests/qapi-schema/comments.out +++ b/tests/qapi-schema/comments.out @@ -1,4 +1,4 @@ +object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE enum Status ['good', 'bad', 'ugly'] -object q_empty diff --git a/tests/qapi-schema/doc-bad-section.out b/tests/qapi-schema/doc-bad-section.out index 089bde1381..23bf8c71ab 100644 --- a/tests/qapi-schema/doc-bad-section.out +++ b/tests/qapi-schema/doc-bad-section.out @@ -1,7 +1,7 @@ -enum Enum ['one', 'two'] +object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE -object q_empty +enum Enum ['one', 'two'] doc symbol=Enum body= == Produces *invalid* texinfo diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 1d2c250527..0c07301f07 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -1,35 +1,35 @@ +object q_empty +enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] + prefix QTYPE +enum Enum ['one', 'two'] object Base member base1: Enum optional=False -enum Enum ['one', 'two'] +object Variant1 + member var1: str optional=False +object Variant2 object Object base Base tag base1 case one: Variant1 case two: Variant2 -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] - prefix QTYPE +object q_obj_Variant1-wrapper + member data: Variant1 optional=False +object q_obj_Variant2-wrapper + member data: Variant2 optional=False +enum SugaredUnionKind ['one', 'two'] object SugaredUnion member type: SugaredUnionKind optional=False tag type case one: q_obj_Variant1-wrapper case two: q_obj_Variant2-wrapper -enum SugaredUnionKind ['one', 'two'] -object Variant1 - member var1: str optional=False -object Variant2 -command cmd q_obj_cmd-arg -> Object - gen=True success_response=True boxed=False -command cmd-boxed Object -> None - gen=True success_response=True boxed=True -object q_empty -object q_obj_Variant1-wrapper - member data: Variant1 optional=False -object q_obj_Variant2-wrapper - member data: Variant2 optional=False object q_obj_cmd-arg member arg1: int optional=False member arg2: str optional=True member arg3: bool optional=False +command cmd q_obj_cmd-arg -> Object + gen=True success_response=True boxed=False +command cmd-boxed Object -> None + gen=True success_response=True boxed=True doc freeform body= = Section diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out index 40b886ddae..0ec234eec4 100644 --- a/tests/qapi-schema/empty.out +++ b/tests/qapi-schema/empty.out @@ -1,3 +1,3 @@ +object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE -object q_empty diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out index 313c0fe7be..110571b793 100644 --- a/tests/qapi-schema/event-case.out +++ b/tests/qapi-schema/event-case.out @@ -1,5 +1,5 @@ +object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE event oops None boxed=False -object q_empty diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out index b5637cb2e0..8336aa7629 100644 --- a/tests/qapi-schema/ident-with-escape.out +++ b/tests/qapi-schema/ident-with-escape.out @@ -1,7 +1,7 @@ +object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE -command fooA q_obj_fooA-arg -> None - gen=True success_response=True boxed=False -object q_empty object q_obj_fooA-arg member bar1: str optional=False +command fooA q_obj_fooA-arg -> None + gen=True success_response=True boxed=False diff --git a/tests/qapi-schema/include-relpath.out b/tests/qapi-schema/include-relpath.out index 17e652535c..0261ddf202 100644 --- a/tests/qapi-schema/include-relpath.out +++ b/tests/qapi-schema/include-relpath.out @@ -1,4 +1,4 @@ +object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE enum Status ['good', 'bad', 'ugly'] -object q_empty diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out index 17e652535c..0261ddf202 100644 --- a/tests/qapi-schema/include-repetition.out +++ b/tests/qapi-schema/include-repetition.out @@ -1,4 +1,4 @@ +object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE enum Status ['good', 'bad', 'ugly'] -object q_empty diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out index 17e652535c..0261ddf202 100644 --- a/tests/qapi-schema/include-simple.out +++ b/tests/qapi-schema/include-simple.out @@ -1,4 +1,4 @@ +object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE enum Status ['good', 'bad', 'ugly'] -object q_empty diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index 586795f44d..34de8be426 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -1,7 +1,7 @@ +object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE command eins None -> None gen=True success_response=True boxed=False -object q_empty command zwei None -> None gen=True success_response=True boxed=False diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 3b1e9082d3..50706b0136 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,87 +1,129 @@ -alternate AltEnumBool - tag type - case e: EnumOne - case b: bool -alternate AltEnumInt - tag type - case e: EnumOne - case i: int -alternate AltEnumNum - tag type - case e: EnumOne - case n: number -alternate AltNumEnum - tag type - case n: number - case e: EnumOne -alternate AltStrObj - tag type - case s: str - case o: TestStruct -event EVENT_A None - boxed=False -event EVENT_B None - boxed=False -event EVENT_C q_obj_EVENT_C-arg - boxed=False -event EVENT_D q_obj_EVENT_D-arg - boxed=False -event EVENT_E UserDefZero - boxed=True -event EVENT_F UserDefAlternate - boxed=True -object Empty1 -object Empty2 - base Empty1 -enum EnumOne ['value1', 'value2', 'value3'] -object EventStructOne - member struct1: UserDefOne optional=False +object q_empty +enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] + prefix QTYPE +object TestStruct + member integer: int optional=False + member boolean: bool optional=False member string: str optional=False - member enum2: EnumOne optional=True -object ForceArrays - member unused1: UserDefOneList optional=False - member unused2: UserDefTwoList optional=False - member unused3: TestStructList optional=False -enum MyEnum [] object NestedEnumsOne member enum1: EnumOne optional=False member enum2: EnumOne optional=True member enum3: EnumOne optional=False member enum4: EnumOne optional=True +enum MyEnum [] +object Empty1 +object Empty2 + base Empty1 +command user_def_cmd0 Empty2 -> Empty2 + gen=True success_response=True boxed=False enum QEnumTwo ['value1', 'value2'] prefix QENUM_TWO -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] - prefix QTYPE -object TestStruct +object UserDefOne + base UserDefZero + member string: str optional=False + member enum1: EnumOne optional=True +enum EnumOne ['value1', 'value2', 'value3'] +object UserDefZero member integer: int optional=False - member boolean: bool optional=False +object UserDefTwoDictDict + member userdef: UserDefOne optional=False member string: str optional=False +object UserDefTwoDict + member string1: str optional=False + member dict2: UserDefTwoDictDict optional=False + member dict3: UserDefTwoDictDict optional=True +object UserDefTwo + member string0: str optional=False + member dict1: UserDefTwoDict optional=False +object ForceArrays + member unused1: UserDefOneList optional=False + member unused2: UserDefTwoList optional=False + member unused3: TestStructList optional=False object UserDefA member boolean: bool optional=False member a_b: int optional=True -alternate UserDefAlternate - tag type - case udfu: UserDefFlatUnion - case e: EnumOne - case i: int - case n: null object UserDefB member intb: int optional=False member a-b: bool optional=True -object UserDefC - member string1: str optional=False - member string2: str optional=False object UserDefFlatUnion base UserDefUnionBase tag enum1 case value1: UserDefA case value2: UserDefB case value3: UserDefB +object UserDefUnionBase + base UserDefZero + member string: str optional=False + member enum1: EnumOne optional=False +object q_obj_UserDefFlatUnion2-base + member integer: int optional=True + member string: str optional=False + member enum1: QEnumTwo optional=False object UserDefFlatUnion2 base q_obj_UserDefFlatUnion2-base tag enum1 case value1: UserDefC case value2: UserDefB +object WrapAlternate + member alt: UserDefAlternate optional=False +alternate UserDefAlternate + tag type + case udfu: UserDefFlatUnion + case e: EnumOne + case i: int + case n: null +object UserDefC + member string1: str optional=False + member string2: str optional=False +alternate AltEnumBool + tag type + case e: EnumOne + case b: bool +alternate AltEnumNum + tag type + case e: EnumOne + case n: number +alternate AltNumEnum + tag type + case n: number + case e: EnumOne +alternate AltEnumInt + tag type + case e: EnumOne + case i: int +alternate AltStrObj + tag type + case s: str + case o: TestStruct +object q_obj_intList-wrapper + member data: intList optional=False +object q_obj_int8List-wrapper + member data: int8List optional=False +object q_obj_int16List-wrapper + member data: int16List optional=False +object q_obj_int32List-wrapper + member data: int32List optional=False +object q_obj_int64List-wrapper + member data: int64List optional=False +object q_obj_uint8List-wrapper + member data: uint8List optional=False +object q_obj_uint16List-wrapper + member data: uint16List optional=False +object q_obj_uint32List-wrapper + member data: uint32List optional=False +object q_obj_uint64List-wrapper + member data: uint64List optional=False +object q_obj_numberList-wrapper + member data: numberList optional=False +object q_obj_boolList-wrapper + member data: boolList optional=False +object q_obj_strList-wrapper + member data: strList optional=False +object q_obj_sizeList-wrapper + member data: sizeList optional=False +object q_obj_anyList-wrapper + member data: anyList optional=False +enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any'] object UserDefNativeListUnion member type: UserDefNativeListUnionKind optional=False tag type @@ -99,133 +141,91 @@ object UserDefNativeListUnion case string: q_obj_strList-wrapper case sizes: q_obj_sizeList-wrapper case any: q_obj_anyList-wrapper -enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any'] -object UserDefOne - base UserDefZero - member string: str optional=False - member enum1: EnumOne optional=True +command user_def_cmd None -> None + gen=True success_response=True boxed=False +object q_obj_user_def_cmd1-arg + member ud1a: UserDefOne optional=False +command user_def_cmd1 q_obj_user_def_cmd1-arg -> None + gen=True success_response=True boxed=False +object q_obj_user_def_cmd2-arg + member ud1a: UserDefOne optional=False + member ud1b: UserDefOne optional=True +command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo + gen=True success_response=True boxed=False +object q_obj_guest-get-time-arg + member a: int optional=False + member b: int optional=True +command guest-get-time q_obj_guest-get-time-arg -> int + gen=True success_response=True boxed=False +object q_obj_guest-sync-arg + member arg: any optional=False +command guest-sync q_obj_guest-sync-arg -> any + gen=True success_response=True boxed=False +command boxed-struct UserDefZero -> None + gen=True success_response=True boxed=True +command boxed-union UserDefNativeListUnion -> None + gen=True success_response=True boxed=True object UserDefOptions member i64: intList optional=True member u64: uint64List optional=True member u16: uint16List optional=True member i64x: int optional=True member u64x: uint64 optional=True -object UserDefTwo - member string0: str optional=False - member dict1: UserDefTwoDict optional=False -object UserDefTwoDict - member string1: str optional=False - member dict2: UserDefTwoDictDict optional=False - member dict3: UserDefTwoDictDict optional=True -object UserDefTwoDictDict - member userdef: UserDefOne optional=False - member string: str optional=False -object UserDefUnionBase - base UserDefZero +object EventStructOne + member struct1: UserDefOne optional=False member string: str optional=False - member enum1: EnumOne optional=False -object UserDefZero - member integer: int optional=False -object WrapAlternate - member alt: UserDefAlternate optional=False -event __ORG.QEMU_X-EVENT __org.qemu_x-Struct + member enum2: EnumOne optional=True +event EVENT_A None boxed=False -alternate __org.qemu_x-Alt - tag type - case __org.qemu_x-branch: str - case b: __org.qemu_x-Base +event EVENT_B None + boxed=False +object q_obj_EVENT_C-arg + member a: int optional=True + member b: UserDefOne optional=True + member c: str optional=False +event EVENT_C q_obj_EVENT_C-arg + boxed=False +object q_obj_EVENT_D-arg + member a: EventStructOne optional=False + member b: str optional=False + member c: str optional=True + member enum3: EnumOne optional=True +event EVENT_D q_obj_EVENT_D-arg + boxed=False +event EVENT_E UserDefZero + boxed=True +event EVENT_F UserDefAlternate + boxed=True +enum __org.qemu_x-Enum ['__org.qemu_x-value'] object __org.qemu_x-Base member __org.qemu_x-member1: __org.qemu_x-Enum optional=False -enum __org.qemu_x-Enum ['__org.qemu_x-value'] object __org.qemu_x-Struct base __org.qemu_x-Base member __org.qemu_x-member2: str optional=False member wchar-t: int optional=True -object __org.qemu_x-Struct2 - member array: __org.qemu_x-Union1List optional=False +object q_obj_str-wrapper + member data: str optional=False +enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch'] object __org.qemu_x-Union1 member type: __org.qemu_x-Union1Kind optional=False tag type case __org.qemu_x-branch: q_obj_str-wrapper -enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch'] +object __org.qemu_x-Struct2 + member array: __org.qemu_x-Union1List optional=False object __org.qemu_x-Union2 base __org.qemu_x-Base tag __org.qemu_x-member1 case __org.qemu_x-value: __org.qemu_x-Struct2 -command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 - gen=True success_response=True boxed=False -command boxed-struct UserDefZero -> None - gen=True success_response=True boxed=True -command boxed-union UserDefNativeListUnion -> None - gen=True success_response=True boxed=True -command guest-get-time q_obj_guest-get-time-arg -> int - gen=True success_response=True boxed=False -command guest-sync q_obj_guest-sync-arg -> any - gen=True success_response=True boxed=False -object q_empty -object q_obj_EVENT_C-arg - member a: int optional=True - member b: UserDefOne optional=True - member c: str optional=False -object q_obj_EVENT_D-arg - member a: EventStructOne optional=False - member b: str optional=False - member c: str optional=True - member enum3: EnumOne optional=True -object q_obj_UserDefFlatUnion2-base - member integer: int optional=True - member string: str optional=False - member enum1: QEnumTwo optional=False +alternate __org.qemu_x-Alt + tag type + case __org.qemu_x-branch: str + case b: __org.qemu_x-Base +event __ORG.QEMU_X-EVENT __org.qemu_x-Struct + boxed=False object q_obj___org.qemu_x-command-arg member a: __org.qemu_x-EnumList optional=False member b: __org.qemu_x-StructList optional=False member c: __org.qemu_x-Union2 optional=False member d: __org.qemu_x-Alt optional=False -object q_obj_anyList-wrapper - member data: anyList optional=False -object q_obj_boolList-wrapper - member data: boolList optional=False -object q_obj_guest-get-time-arg - member a: int optional=False - member b: int optional=True -object q_obj_guest-sync-arg - member arg: any optional=False -object q_obj_int16List-wrapper - member data: int16List optional=False -object q_obj_int32List-wrapper - member data: int32List optional=False -object q_obj_int64List-wrapper - member data: int64List optional=False -object q_obj_int8List-wrapper - member data: int8List optional=False -object q_obj_intList-wrapper - member data: intList optional=False -object q_obj_numberList-wrapper - member data: numberList optional=False -object q_obj_sizeList-wrapper - member data: sizeList optional=False -object q_obj_str-wrapper - member data: str optional=False -object q_obj_strList-wrapper - member data: strList optional=False -object q_obj_uint16List-wrapper - member data: uint16List optional=False -object q_obj_uint32List-wrapper - member data: uint32List optional=False -object q_obj_uint64List-wrapper - member data: uint64List optional=False -object q_obj_uint8List-wrapper - member data: uint8List optional=False -object q_obj_user_def_cmd1-arg - member ud1a: UserDefOne optional=False -object q_obj_user_def_cmd2-arg - member ud1a: UserDefOne optional=False - member ud1b: UserDefOne optional=True -command user_def_cmd None -> None - gen=True success_response=True boxed=False -command user_def_cmd0 Empty2 -> Empty2 - gen=True success_response=True boxed=False -command user_def_cmd1 q_obj_user_def_cmd1-arg -> None - gen=True success_response=True boxed=False -command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo +command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 gen=True success_response=True boxed=False -- cgit 1.4.1 From cf40a0a5c2e1091846974cc8cc95a60e0b1db4af Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:35:55 +0100 Subject: qapi: Record 'include' directives in intermediate representation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The include directive permits modular QAPI schemata, but the generated code is monolithic all the same. To permit generating modular code, the front end needs to pass more information on inclusions to the back ends. The commit before last added the necessary information to the parse tree. This commit adds it to the intermediate representation and its QAPISchemaVisitor. A later commit will use this to to generate modular code. New entity QAPISchemaInclude represents inclusions. Call new visitor method visit_include() for it, so visitors can see the sub-modules a module includes. Note that unlike other entities, QAPISchemaInclude has no name, and is therefore not added to entity_dict. New QAPISchemaEntity attribute @module names the entity's source file. Call new visitor method visit_module() when it changes during a visit, so visitors can keep track of the module being visited. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Message-Id: <20180211093607.27351-18-armbru@redhat.com> [eblake: avoid accidental deletion of self._predefining] Signed-off-by: Eric Blake --- scripts/qapi/common.py | 43 +++++++++++++++++++++++++++++--- tests/qapi-schema/comments.out | 1 + tests/qapi-schema/doc-bad-section.out | 1 + tests/qapi-schema/doc-good.out | 1 + tests/qapi-schema/event-case.out | 1 + tests/qapi-schema/ident-with-escape.out | 1 + tests/qapi-schema/include-relpath.out | 5 ++++ tests/qapi-schema/include-repetition.out | 10 ++++++++ tests/qapi-schema/include-simple.out | 3 +++ tests/qapi-schema/indented-expr.out | 1 + tests/qapi-schema/qapi-schema-test.out | 1 + tests/qapi-schema/test-qapi.py | 7 ++++++ 12 files changed, 71 insertions(+), 4 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 1aa1cd3b9b..2c5c40ec0a 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -984,8 +984,9 @@ def check_exprs(exprs): class QAPISchemaEntity(object): def __init__(self, name, info, doc): - assert isinstance(name, str) + assert name is None or isinstance(name, str) self.name = name + self.module = None # For explicitly defined entities, info points to the (explicit) # definition. For builtins (and their arrays), info is None. # For implicitly defined entities, info points to a place that @@ -1014,10 +1015,16 @@ class QAPISchemaVisitor(object): def visit_end(self): pass + def visit_module(self, fname): + pass + def visit_needed(self, entity): # Default to visiting everything return True + def visit_include(self, fname, info): + pass + def visit_builtin_type(self, name, info, json_type): pass @@ -1044,6 +1051,16 @@ class QAPISchemaVisitor(object): pass +class QAPISchemaInclude(QAPISchemaEntity): + + def __init__(self, fname, info): + QAPISchemaEntity.__init__(self, None, info, None) + self.fname = fname + + def visit(self, visitor): + visitor.visit_include(self.fname, self.info) + + class QAPISchemaType(QAPISchemaEntity): # Return the C type for common use. # For the types we commonly box, this is a pointer type. @@ -1471,6 +1488,7 @@ class QAPISchemaEvent(QAPISchemaEntity): class QAPISchema(object): def __init__(self, fname): + self._fname = fname parser = QAPISchemaParser(open(fname, 'r')) exprs = check_exprs(parser.exprs) self.docs = parser.docs @@ -1485,9 +1503,13 @@ class QAPISchema(object): def _def_entity(self, ent): # Only the predefined types are allowed to not have info assert ent.info or self._predefining - assert ent.name not in self._entity_dict + assert ent.name is None or ent.name not in self._entity_dict self._entity_list.append(ent) - self._entity_dict[ent.name] = ent + if ent.name is not None: + self._entity_dict[ent.name] = ent + if ent.info: + ent.module = os.path.relpath(ent.info['file'], + os.path.dirname(self._fname)) def lookup_entity(self, name, typ=None): ent = self._entity_dict.get(name) @@ -1498,6 +1520,15 @@ class QAPISchema(object): def lookup_type(self, name): return self.lookup_entity(name, QAPISchemaType) + def _def_include(self, expr, info, doc): + include = expr['include'] + assert doc is None + main_info = info + while main_info['parent']: + main_info = main_info['parent'] + fname = os.path.relpath(include, os.path.dirname(main_info['file'])) + self._def_entity(QAPISchemaInclude(fname, info)) + def _def_builtin_type(self, name, json_type, c_type): self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple @@ -1680,7 +1711,7 @@ class QAPISchema(object): elif 'event' in expr: self._def_event(expr, info, doc) elif 'include' in expr: - pass + self._def_include(expr, info, doc) else: assert False @@ -1690,8 +1721,12 @@ class QAPISchema(object): def visit(self, visitor): visitor.visit_begin(self) + module = None for entity in self._entity_list: if visitor.visit_needed(entity): + if entity.module != module: + module = entity.module + visitor.visit_module(module) entity.visit(visitor) visitor.visit_end() diff --git a/tests/qapi-schema/comments.out b/tests/qapi-schema/comments.out index 0261ddf202..8d2f1ce8a2 100644 --- a/tests/qapi-schema/comments.out +++ b/tests/qapi-schema/comments.out @@ -1,4 +1,5 @@ object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +module comments.json enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/doc-bad-section.out b/tests/qapi-schema/doc-bad-section.out index 23bf8c71ab..cd28721568 100644 --- a/tests/qapi-schema/doc-bad-section.out +++ b/tests/qapi-schema/doc-bad-section.out @@ -1,6 +1,7 @@ object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +module doc-bad-section.json enum Enum ['one', 'two'] doc symbol=Enum body= diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 0c07301f07..430b5a87db 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -1,6 +1,7 @@ object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +module doc-good.json enum Enum ['one', 'two'] object Base member base1: Enum optional=False diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out index 110571b793..88c0964917 100644 --- a/tests/qapi-schema/event-case.out +++ b/tests/qapi-schema/event-case.out @@ -1,5 +1,6 @@ object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +module event-case.json event oops None boxed=False diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out index 8336aa7629..ee3b34e623 100644 --- a/tests/qapi-schema/ident-with-escape.out +++ b/tests/qapi-schema/ident-with-escape.out @@ -1,6 +1,7 @@ object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +module ident-with-escape.json object q_obj_fooA-arg member bar1: str optional=False command fooA q_obj_fooA-arg -> None diff --git a/tests/qapi-schema/include-relpath.out b/tests/qapi-schema/include-relpath.out index 0261ddf202..ebbabd7a18 100644 --- a/tests/qapi-schema/include-relpath.out +++ b/tests/qapi-schema/include-relpath.out @@ -1,4 +1,9 @@ object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +module include-relpath.json +include include/relpath.json +module include/relpath.json +include include-relpath-sub.json +module include-relpath-sub.json enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out index 0261ddf202..7235e055bc 100644 --- a/tests/qapi-schema/include-repetition.out +++ b/tests/qapi-schema/include-repetition.out @@ -1,4 +1,14 @@ object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +module include-repetition.json +include comments.json +module comments.json enum Status ['good', 'bad', 'ugly'] +module include-repetition.json +include include-repetition-sub.json +module include-repetition-sub.json +include comments.json +include comments.json +module include-repetition.json +include comments.json diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out index 0261ddf202..006f723eeb 100644 --- a/tests/qapi-schema/include-simple.out +++ b/tests/qapi-schema/include-simple.out @@ -1,4 +1,7 @@ object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +module include-simple.json +include include-simple-sub.json +module include-simple-sub.json enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index 34de8be426..a79935e8c3 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -1,6 +1,7 @@ object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +module indented-expr.json command eins None -> None gen=True success_response=True boxed=False command zwei None -> None diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 50706b0136..012e7fc06a 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,6 +1,7 @@ object q_empty enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +module qapi-schema-test.json object TestStruct member integer: int optional=False member boolean: bool optional=False diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 4da14b43af..67e417e298 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -16,6 +16,13 @@ from qapi.common import QAPIError, QAPISchema, QAPISchemaVisitor class QAPISchemaTestVisitor(QAPISchemaVisitor): + + def visit_module(self, name): + print('module %s' % name) + + def visit_include(self, name, info): + print('include %s' % name) + def visit_enum_type(self, name, info, values, prefix): print('enum %s %s' % (name, values)) if prefix: -- cgit 1.4.1 From 71b3f0459c460c9e16a47372ccddbfa6e2c7aadf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 26 Feb 2018 13:50:08 -0600 Subject: qapi: Make code-generating visitors use QAPIGen more MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The use of QAPIGen is rather shallow so far: most of the output accumulation is not converted. Take the next step: convert output accumulation in the code-generating visitor classes. Helper functions outside these classes are not converted. Signed-off-by: Markus Armbruster Message-Id: <20180211093607.27351-20-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Marc-André Lureau Reviewed-by: Michael Roth [eblake: rebase to earlier guardstart cleanup] Signed-off-by: Eric Blake --- scripts/qapi/commands.py | 71 ++++++++++++++++----------------------- scripts/qapi/common.py | 13 ++++++++ scripts/qapi/doc.py | 74 ++++++++++++++++++++--------------------- scripts/qapi/events.py | 55 ++++++++++++------------------- scripts/qapi/introspect.py | 56 +++++++++++++------------------ scripts/qapi/types.py | 81 +++++++++++++++++++-------------------------- scripts/qapi/visit.py | 82 ++++++++++++++++++++-------------------------- 7 files changed, 189 insertions(+), 243 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 05fe33a03b..46757db771 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -223,44 +223,15 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) return ret -class QAPISchemaGenCommandVisitor(QAPISchemaVisitor): +class QAPISchemaGenCommandVisitor(QAPISchemaMonolithicCVisitor): + def __init__(self, prefix): - self._prefix = prefix - self.decl = None - self.defn = None - self._regy = None - self._visited_ret_types = None - - def visit_begin(self, schema): - self.decl = '' - self.defn = '' + QAPISchemaMonolithicCVisitor.__init__( + self, prefix, 'qmp-commands', + ' * Schema-defined QAPI/QMP commands', __doc__) self._regy = '' self._visited_ret_types = set() - - def visit_end(self): - self.defn += gen_registry(self._regy, self._prefix) - self._regy = None - self._visited_ret_types = None - - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed): - if not gen: - return - self.decl += gen_command_decl(name, arg_type, boxed, ret_type) - if ret_type and ret_type not in self._visited_ret_types: - self._visited_ret_types.add(ret_type) - self.defn += gen_marshal_output(ret_type) - self.decl += gen_marshal_decl(name) - self.defn += gen_marshal(name, arg_type, boxed, ret_type) - self._regy += gen_register_command(name, success_response) - - -def gen_commands(schema, output_dir, prefix): - blurb = ' * Schema-defined QAPI/QMP commands' - genc = QAPIGenC(blurb, __doc__) - genh = QAPIGenH(blurb, __doc__) - - genc.add(mcgen(''' + self._genc.add(mcgen(''' #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/module.h" @@ -275,19 +246,33 @@ def gen_commands(schema, output_dir, prefix): #include "%(prefix)sqmp-commands.h" ''', - prefix=prefix)) - - genh.add(mcgen(''' + prefix=prefix)) + self._genh.add(mcgen(''' #include "%(prefix)sqapi-types.h" #include "qapi/qmp/dispatch.h" void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); ''', - prefix=prefix, c_prefix=c_name(prefix, protect=False))) + prefix=prefix, + c_prefix=c_name(prefix, protect=False))) + + def visit_end(self): + self._genc.add(gen_registry(self._regy, self._prefix)) + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response, boxed): + if not gen: + return + self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) + if ret_type and ret_type not in self._visited_ret_types: + self._visited_ret_types.add(ret_type) + self._genc.add(gen_marshal_output(ret_type)) + self._genh.add(gen_marshal_decl(name)) + self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) + self._regy += gen_register_command(name, success_response) + +def gen_commands(schema, output_dir, prefix): vis = QAPISchemaGenCommandVisitor(prefix) schema.visit(vis) - genc.add(vis.defn) - genh.add(vis.decl) - genc.write(output_dir, prefix + 'qmp-commands.c') - genh.write(output_dir, prefix + 'qmp-commands.h') + vis.write(output_dir) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 2c5c40ec0a..23437b558f 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -2050,3 +2050,16 @@ class QAPIGenDoc(QAPIGen): def _top(self, fname): return (QAPIGen._top(self, fname) + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') + + +class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): + + def __init__(self, prefix, what, blurb, pydoc): + self._prefix = prefix + self._what = what + self._genc = QAPIGenC(blurb, pydoc) + self._genh = QAPIGenH(blurb, pydoc) + + def write(self, output_dir): + self._genc.write(output_dir, self._prefix + self._what + '.c') + self._genh.write(output_dir, self._prefix + self._what + '.h') diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index cc4d5a43fb..0ea68bf813 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -197,33 +197,35 @@ def texi_entity(doc, what, base=None, variants=None, class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): - def __init__(self): - self.out = None + def __init__(self, prefix): + self._prefix = prefix + self._gen = qapi.common.QAPIGenDoc() self.cur_doc = None - def visit_begin(self, schema): - self.out = '' + def write(self, output_dir): + self._gen.write(output_dir, self._prefix + 'qapi-doc.texi') def visit_enum_type(self, name, info, values, prefix): doc = self.cur_doc - self.out += TYPE_FMT(type='Enum', - name=doc.symbol, - body=texi_entity(doc, 'Values', - member_func=texi_enum_value)) + self._gen.add(TYPE_FMT(type='Enum', + name=doc.symbol, + body=texi_entity(doc, 'Values', + member_func=texi_enum_value))) def visit_object_type(self, name, info, base, members, variants): doc = self.cur_doc if base and base.is_implicit(): base = None - self.out += TYPE_FMT(type='Object', - name=doc.symbol, - body=texi_entity(doc, 'Members', base, variants)) + self._gen.add(TYPE_FMT(type='Object', + name=doc.symbol, + body=texi_entity(doc, 'Members', + base, variants))) def visit_alternate_type(self, name, info, variants): doc = self.cur_doc - self.out += TYPE_FMT(type='Alternate', - name=doc.symbol, - body=texi_entity(doc, 'Members')) + self._gen.add(TYPE_FMT(type='Alternate', + name=doc.symbol, + body=texi_entity(doc, 'Members'))) def visit_command(self, name, info, arg_type, ret_type, gen, success_response, boxed): @@ -235,44 +237,38 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): body += texi_sections(doc) else: body = texi_entity(doc, 'Arguments') - self.out += MSG_FMT(type='Command', - name=doc.symbol, - body=body) + self._gen.add(MSG_FMT(type='Command', + name=doc.symbol, + body=body)) def visit_event(self, name, info, arg_type, boxed): doc = self.cur_doc - self.out += MSG_FMT(type='Event', - name=doc.symbol, - body=texi_entity(doc, 'Arguments')) + self._gen.add(MSG_FMT(type='Event', + name=doc.symbol, + body=texi_entity(doc, 'Arguments'))) def symbol(self, doc, entity): - if self.out: - self.out += '\n' + if self._gen._body: + self._gen.add('\n') self.cur_doc = doc entity.visit(self) self.cur_doc = None def freeform(self, doc): assert not doc.args - if self.out: - self.out += '\n' - self.out += texi_body(doc) + texi_sections(doc) + if self._gen._body: + self._gen.add('\n') + self._gen.add(texi_body(doc) + texi_sections(doc)) -def texi_schema(schema): - """Convert QAPI schema documentation to Texinfo""" - gen = QAPISchemaGenDocVisitor() - gen.visit_begin(schema) +def gen_doc(schema, output_dir, prefix): + if not qapi.common.doc_required: + return + vis = QAPISchemaGenDocVisitor(prefix) + vis.visit_begin(schema) for doc in schema.docs: if doc.symbol: - gen.symbol(doc, schema.lookup_entity(doc.symbol)) + vis.symbol(doc, schema.lookup_entity(doc.symbol)) else: - gen.freeform(doc) - return gen.out - - -def gen_doc(schema, output_dir, prefix): - if qapi.common.doc_required: - gen = qapi.common.QAPIGenDoc() - gen.add(texi_schema(schema)) - gen.write(output_dir, prefix + 'qapi-doc.texi') + vis.freeform(doc) + vis.write(output_dir) diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index b7dc82004f..81ab3abb30 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -148,35 +148,15 @@ out: return ret -class QAPISchemaGenEventVisitor(QAPISchemaVisitor): +class QAPISchemaGenEventVisitor(QAPISchemaMonolithicCVisitor): + def __init__(self, prefix): + QAPISchemaMonolithicCVisitor.__init__( + self, prefix, 'qapi-event', + ' * Schema-defined QAPI/QMP events', __doc__) self._enum_name = c_name(prefix + 'QAPIEvent', protect=False) - self.decl = None - self.defn = None - self._event_names = None - - def visit_begin(self, schema): - self.decl = '' - self.defn = '' self._event_names = [] - - def visit_end(self): - self.decl += gen_enum(self._enum_name, self._event_names) - self.defn += gen_enum_lookup(self._enum_name, self._event_names) - self._event_names = None - - def visit_event(self, name, info, arg_type, boxed): - self.decl += gen_event_send_decl(name, arg_type, boxed) - self.defn += gen_event_send(name, arg_type, boxed, self._enum_name) - self._event_names.append(name) - - -def gen_events(schema, output_dir, prefix): - blurb = ' * Schema-defined QAPI/QMP events' - genc = QAPIGenC(blurb, __doc__) - genh = QAPIGenH(blurb, __doc__) - - genc.add(mcgen(''' + self._genc.add(mcgen(''' #include "qemu/osdep.h" #include "qemu-common.h" #include "%(prefix)sqapi-event.h" @@ -187,18 +167,25 @@ def gen_events(schema, output_dir, prefix): #include "qapi/qmp-event.h" ''', - prefix=prefix)) - - genh.add(mcgen(''' + prefix=prefix)) + self._genh.add(mcgen(''' #include "qapi/util.h" #include "%(prefix)sqapi-types.h" ''', - prefix=prefix)) + prefix=prefix)) + def visit_end(self): + self._genh.add(gen_enum(self._enum_name, self._event_names)) + self._genc.add(gen_enum_lookup(self._enum_name, self._event_names)) + + def visit_event(self, name, info, arg_type, boxed): + self._genh.add(gen_event_send_decl(name, arg_type, boxed)) + self._genc.add(gen_event_send(name, arg_type, boxed, self._enum_name)) + self._event_names.append(name) + + +def gen_events(schema, output_dir, prefix): vis = QAPISchemaGenEventVisitor(prefix) schema.visit(vis) - genc.add(vis.defn) - genh.add(vis.decl) - genc.write(output_dir, prefix + 'qapi-event.c') - genh.write(output_dir, prefix + 'qapi-event.h') + vis.write(output_dir) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 1e4f065164..f571cc134c 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -40,22 +40,26 @@ def to_c_string(string): return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"' -class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor): +class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): + def __init__(self, prefix, unmask): - self._prefix = prefix + QAPISchemaMonolithicCVisitor.__init__( + self, prefix, 'qmp-introspect', + ' * QAPI/QMP schema introspection', __doc__) self._unmask = unmask - self.defn = None - self.decl = None self._schema = None - self._jsons = None - self._used_types = None - self._name_map = None - - def visit_begin(self, schema): - self._schema = schema self._jsons = [] self._used_types = [] self._name_map = {} + self._genc.add(mcgen(''' +#include "qemu/osdep.h" +#include "%(prefix)sqmp-introspect.h" + +''', + prefix=prefix)) + + def visit_begin(self, schema): + self._schema = schema def visit_end(self): # visit the types that are actually used @@ -67,21 +71,21 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor): # TODO can generate awfully long lines jsons.extend(self._jsons) name = c_name(self._prefix, protect=False) + 'qmp_schema_json' - self.decl = mcgen(''' + self._genh.add(mcgen(''' extern const char %(c_name)s[]; ''', - c_name=c_name(name)) + c_name=c_name(name))) lines = to_json(jsons).split('\n') c_string = '\n '.join([to_c_string(line) for line in lines]) - self.defn = mcgen(''' + self._genc.add(mcgen(''' const char %(c_name)s[] = %(c_string)s; ''', - c_name=c_name(name), - c_string=c_string) + c_name=c_name(name), + c_string=c_string)) self._schema = None - self._jsons = None - self._used_types = None - self._name_map = None + self._jsons = [] + self._used_types = [] + self._name_map = {} def visit_needed(self, entity): # Ignore types on first pass; visit_end() will pick up used types @@ -169,20 +173,6 @@ const char %(c_name)s[] = %(c_string)s; def gen_introspect(schema, output_dir, prefix, opt_unmask): - blurb = ' * QAPI/QMP schema introspection' - genc = QAPIGenC(blurb, __doc__) - genh = QAPIGenH(blurb, __doc__) - - genc.add(mcgen(''' -#include "qemu/osdep.h" -#include "%(prefix)sqmp-introspect.h" - -''', - prefix=prefix)) - vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask) schema.visit(vis) - genc.add(vis.defn) - genh.add(vis.decl) - genc.write(output_dir, prefix + 'qmp-introspect.c') - genh.write(output_dir, prefix + 'qmp-introspect.h') + vis.write(output_dir) diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index aa3c01e750..d2b8423479 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -167,36 +167,41 @@ void qapi_free_%(c_name)s(%(c_name)s *obj) return ret -class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): - def __init__(self, opt_builtins): +class QAPISchemaGenTypeVisitor(QAPISchemaMonolithicCVisitor): + + def __init__(self, prefix, opt_builtins): + QAPISchemaMonolithicCVisitor.__init__( + self, prefix, 'qapi-types', ' * Schema-defined QAPI types', + __doc__) self._opt_builtins = opt_builtins - self.decl = None - self.defn = None - self._fwdecl = None - self._btin = None + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "qapi/dealloc-visitor.h" +#include "%(prefix)sqapi-types.h" +#include "%(prefix)sqapi-visit.h" +''', + prefix=prefix)) + self._genh.preamble_add(mcgen(''' +#include "qapi/util.h" +''')) + self._btin = '\n' + guardstart('QAPI_TYPES_BUILTIN') def visit_begin(self, schema): # gen_object() is recursive, ensure it doesn't visit the empty type objects_seen.add(schema.the_empty_object_type.name) - self.decl = '' - self.defn = '' - self._fwdecl = '' - self._btin = '\n' + guardstart('QAPI_TYPES_BUILTIN') def visit_end(self): - self.decl = self._fwdecl + self.decl - self._fwdecl = None # To avoid header dependency hell, we always generate # declarations for built-in types in our header files and # simply guard them. See also opt_builtins (command line # option -b). self._btin += guardend('QAPI_TYPES_BUILTIN') - self.decl = self._btin + self.decl + self._genh.preamble_add(self._btin) self._btin = None def _gen_type_cleanup(self, name): - self.decl += gen_type_cleanup_decl(name) - self.defn += gen_type_cleanup(name) + self._genh.add(gen_type_cleanup_decl(name)) + self._genc.add(gen_type_cleanup(name)) def visit_enum_type(self, name, info, values, prefix): # Special case for our lone builtin enum type @@ -204,10 +209,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): if not info: self._btin += gen_enum(name, values, prefix) if self._opt_builtins: - self.defn += gen_enum_lookup(name, values, prefix) + self._genc.add(gen_enum_lookup(name, values, prefix)) else: - self._fwdecl += gen_enum(name, values, prefix) - self.defn += gen_enum_lookup(name, values, prefix) + self._genh.preamble_add(gen_enum(name, values, prefix)) + self._genc.add(gen_enum_lookup(name, values, prefix)) def visit_array_type(self, name, info, element_type): if isinstance(element_type, QAPISchemaBuiltinType): @@ -215,20 +220,20 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): self._btin += gen_array(name, element_type) self._btin += gen_type_cleanup_decl(name) if self._opt_builtins: - self.defn += gen_type_cleanup(name) + self._genc.add(gen_type_cleanup(name)) else: - self._fwdecl += gen_fwd_object_or_array(name) - self.decl += gen_array(name, element_type) + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_array(name, element_type)) self._gen_type_cleanup(name) def visit_object_type(self, name, info, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return - self._fwdecl += gen_fwd_object_or_array(name) - self.decl += gen_object(name, base, members, variants) + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_object(name, base, members, variants)) if base and not base.is_implicit(): - self.decl += gen_upcast(name, base) + self._genh.add(gen_upcast(name, base)) # TODO Worth changing the visitor signature, so we could # directly use rather than repeat type.is_implicit()? if not name.startswith('q_'): @@ -236,31 +241,13 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): self._gen_type_cleanup(name) def visit_alternate_type(self, name, info, variants): - self._fwdecl += gen_fwd_object_or_array(name) - self.decl += gen_object(name, None, [variants.tag_member], variants) + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_object(name, None, + [variants.tag_member], variants)) self._gen_type_cleanup(name) def gen_types(schema, output_dir, prefix, opt_builtins): - blurb = ' * Schema-defined QAPI types' - genc = QAPIGenC(blurb, __doc__) - genh = QAPIGenH(blurb, __doc__) - - genc.add(mcgen(''' -#include "qemu/osdep.h" -#include "qapi/dealloc-visitor.h" -#include "%(prefix)sqapi-types.h" -#include "%(prefix)sqapi-visit.h" -''', - prefix=prefix)) - - genh.add(mcgen(''' -#include "qapi/util.h" -''')) - - vis = QAPISchemaGenTypeVisitor(opt_builtins) + vis = QAPISchemaGenTypeVisitor(prefix, opt_builtins) schema.visit(vis) - genc.add(vis.defn) - genh.add(vis.decl) - genc.write(output_dir, prefix + 'qapi-types.c') - genh.write(output_dir, prefix + 'qapi-types.h') + vis.write(output_dir) diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 3ed78165d7..3d09d44265 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -263,17 +263,28 @@ out: c_name=c_name(name)) -class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): - def __init__(self, opt_builtins): +class QAPISchemaGenVisitVisitor(QAPISchemaMonolithicCVisitor): + + def __init__(self, prefix, opt_builtins): + QAPISchemaMonolithicCVisitor.__init__( + self, prefix, 'qapi-visit', ' * Schema-defined QAPI visitors', + __doc__) self._opt_builtins = opt_builtins - self.decl = None - self.defn = None - self._btin = None + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "%(prefix)sqapi-visit.h" +''', + prefix=prefix)) + self._genh.preamble_add(mcgen(''' +#include "qapi/visitor.h" +#include "%(prefix)sqapi-types.h" - def visit_begin(self, schema): - self.decl = '' - self.defn = '' - self._btin = '\n' + guardstart('QAPI_VISIT_BUILTIN') +''', + prefix=prefix)) + self._btin = guardstart('QAPI_VISIT_BUILTIN') def visit_end(self): # To avoid header dependency hell, we always generate @@ -281,7 +292,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): # simply guard them. See also opt_builtins (command line # option -b). self._btin += guardend('QAPI_VISIT_BUILTIN') - self.decl = self._btin + self.decl + self._genh.preamble_add(self._btin) self._btin = None def visit_enum_type(self, name, info, values, prefix): @@ -290,10 +301,10 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): if not info: self._btin += gen_visit_decl(name, scalar=True) if self._opt_builtins: - self.defn += gen_visit_enum(name) + self._genc.add(gen_visit_enum(name)) else: - self.decl += gen_visit_decl(name, scalar=True) - self.defn += gen_visit_enum(name) + self._genh.add(gen_visit_decl(name, scalar=True)) + self._genc.add(gen_visit_enum(name)) def visit_array_type(self, name, info, element_type): decl = gen_visit_decl(name) @@ -301,53 +312,30 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): if isinstance(element_type, QAPISchemaBuiltinType): self._btin += decl if self._opt_builtins: - self.defn += defn + self._genc.add(defn) else: - self.decl += decl - self.defn += defn + self._genh.add(decl) + self._genc.add(defn) def visit_object_type(self, name, info, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return - self.decl += gen_visit_members_decl(name) - self.defn += gen_visit_object_members(name, base, members, variants) + self._genh.add(gen_visit_members_decl(name)) + self._genc.add(gen_visit_object_members(name, base, members, variants)) # TODO Worth changing the visitor signature, so we could # directly use rather than repeat type.is_implicit()? if not name.startswith('q_'): # only explicit types need an allocating visit - self.decl += gen_visit_decl(name) - self.defn += gen_visit_object(name, base, members, variants) + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_object(name, base, members, variants)) def visit_alternate_type(self, name, info, variants): - self.decl += gen_visit_decl(name) - self.defn += gen_visit_alternate(name, variants) + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_alternate(name, variants)) def gen_visit(schema, output_dir, prefix, opt_builtins): - blurb = ' * Schema-defined QAPI visitors' - genc = QAPIGenC(blurb, __doc__) - genh = QAPIGenH(blurb, __doc__) - - genc.add(mcgen(''' -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qapi/error.h" -#include "qapi/qmp/qerror.h" -#include "%(prefix)sqapi-visit.h" -''', - prefix=prefix)) - - genh.add(mcgen(''' -#include "qapi/visitor.h" -#include "%(prefix)sqapi-types.h" - -''', - prefix=prefix)) - - vis = QAPISchemaGenVisitVisitor(opt_builtins) + vis = QAPISchemaGenVisitVisitor(prefix, opt_builtins) schema.visit(vis) - genc.add(vis.defn) - genh.add(vis.decl) - genc.write(output_dir, prefix + 'qapi-visit.c') - genh.write(output_dir, prefix + 'qapi-visit.h') + vis.write(output_dir) -- cgit 1.4.1 From cdb6610ae4283720037bae2af1f78bd40eb5fe71 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 26 Feb 2018 16:29:21 -0600 Subject: qapi/types qapi/visit: Generate built-in stuff into separate files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linking code from multiple separate QAPI schemata into the same program is possible, but involves some weirdness around built-in types: * We generate code for built-in types into .c only with option --builtins. The user is responsible for generating code for exactly one QAPI schema per program with --builtins. * We generate code for built-in types into .h regardless of --builtins, but guarded by #ifndef QAPI_VISIT_BUILTIN. Because all copies of this code are exactly the same, including any combination of these headers works. Replace this contraption by something more conventional: generate code for built-in types into their very own files: qapi-builtin-types.c, qapi-builtin-visit.c, qapi-builtin-types.h, qapi-builtin-visit.h, but only with --builtins. Obey --output-dir, but ignore --prefix for them. Make qapi-types.h include qapi-builtin-types.h. With multiple schemata you now have multiple qapi-types.[ch], but only one qapi-builtin-types.[ch]. Same for qapi-visit.[ch] and qapi-builtin-visit.[ch]. Bonus: if all you need is built-in stuff, you can include a much smaller header. To be exploited shortly. Signed-off-by: Markus Armbruster Message-Id: <20180211093607.27351-21-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Marc-André Lureau Reviewed-by: Michael Roth [eblake: fix octal constant for python 3] Signed-off-by: Eric Blake --- .gitignore | 2 ++ Makefile | 13 ++++++---- Makefile.objs | 2 ++ scripts/qapi/common.py | 60 +++++++++++++++++++++++++++++++++++++++------- scripts/qapi/types.py | 61 +++++++++++++++++++---------------------------- scripts/qapi/visit.py | 64 +++++++++++++++++++++----------------------------- 6 files changed, 115 insertions(+), 87 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/.gitignore b/.gitignore index 7d783e6e66..9477a08b6b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,8 @@ /qga/qapi-generated /qapi-generated /qapi-gen-timestamp +/qapi-builtin-types.[ch] +/qapi-builtin-visit.[ch] /qapi-types.[ch] /qapi-visit.[ch] /qapi-event.[ch] diff --git a/Makefile b/Makefile index b9f320f6ba..494ae38279 100644 --- a/Makefile +++ b/Makefile @@ -90,10 +90,13 @@ endif include $(SRC_PATH)/rules.mak GENERATED_FILES = qemu-version.h config-host.h qemu-options.def -GENERATED_FILES += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h -GENERATED_FILES += qmp-commands.c qapi-types.c qapi-visit.c qapi-event.c -GENERATED_FILES += qmp-introspect.h -GENERATED_FILES += qmp-introspect.c +GENERATED_FILES += qapi-builtin-types.h qapi-builtin-types.c +GENERATED_FILES += qapi-types.h qapi-types.c +GENERATED_FILES += qapi-builtin-visit.h qapi-builtin-visit.c +GENERATED_FILES += qapi-visit.h qapi-visit.c +GENERATED_FILES += qmp-commands.h qmp-commands.c +GENERATED_FILES += qapi-event.h qapi-event.c +GENERATED_FILES += qmp-introspect.c qmp-introspect.h GENERATED_FILES += qapi-doc.texi GENERATED_FILES += trace/generated-tcg-tracers.h @@ -519,7 +522,9 @@ qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \ $(SRC_PATH)/qapi/transaction.json \ $(SRC_PATH)/qapi/ui.json +qapi-builtin-types.c qapi-builtin-types.h \ qapi-types.c qapi-types.h \ +qapi-builtin-visit.c qapi-builtin-visit.h \ qapi-visit.c qapi-visit.h \ qmp-commands.h qmp-commands.c \ qapi-event.c qapi-event.h \ diff --git a/Makefile.objs b/Makefile.objs index 7aa67d89f8..2ace9c13b9 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -2,6 +2,8 @@ # Common libraries for tools and emulators stub-obj-y = stubs/ crypto/ util-obj-y = util/ qobject/ qapi/ +util-obj-y += qapi-builtin-types.o +util-obj-y += qapi-builtin-visit.o util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o chardev-obj-y = chardev/ diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 23437b558f..547656c8b2 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1531,11 +1531,10 @@ class QAPISchema(object): def _def_builtin_type(self, name, json_type, c_type): self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) - # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple - # qapi-types.h from a single .c, all arrays of builtins must be - # declared in the first file whether or not they are used. Nicer - # would be to use lazy instantiation, while figuring out how to - # avoid compilation issues with multiple qapi-types.h. + # Instantiating only the arrays that are actually used would + # be nice, but we can't as long as their generated code + # (qapi-builtin-types.[ch]) may be shared by some other + # schema. self._make_array_type(name, None) def _def_predefineds(self): @@ -1992,14 +1991,15 @@ class QAPIGen(object): return '' def write(self, output_dir, fname): - if output_dir: + pathname = os.path.join(output_dir, fname) + dir = os.path.dirname(pathname) + if dir: try: - os.makedirs(output_dir) + os.makedirs(dir) except os.error as e: if e.errno != errno.EEXIST: raise - fd = os.open(os.path.join(output_dir, fname), - os.O_RDWR | os.O_CREAT, 0o666) + fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) f = os.fdopen(fd, 'r+') text = (self._top(fname) + self._preamble + self._body + self._bottom(fname)) @@ -2063,3 +2063,45 @@ class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): def write(self, output_dir): self._genc.write(output_dir, self._prefix + self._what + '.c') self._genh.write(output_dir, self._prefix + self._what + '.h') + + +class QAPISchemaModularCVisitor(QAPISchemaVisitor): + + def __init__(self, prefix, what, blurb, pydoc): + self._prefix = prefix + self._what = what + self._blurb = blurb + self._pydoc = pydoc + self._module = {} + + def _module_basename(self, what, name): + if name is None: + return re.sub(r'-', '-builtin-', what) + return self._prefix + what + + def _add_module(self, name, blurb): + genc = QAPIGenC(blurb, self._pydoc) + genh = QAPIGenH(blurb, self._pydoc) + self._module[name] = (genc, genh) + self._set_module(name) + + def _set_module(self, name): + self._genc, self._genh = self._module[name] + + def write(self, output_dir, opt_builtins): + for name in self._module: + if name is None and not opt_builtins: + continue + basename = self._module_basename(self._what, name) + (genc, genh) = self._module[name] + genc.write(output_dir, basename + '.c') + genh.write(output_dir, basename + '.h') + + def _begin_module(self, name): + pass + + def visit_module(self, name): + if len(self._module) != 1: + return + self._add_module(name, self._blurb) + self._begin_module(name) diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index d2b8423479..59826b1162 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -167,64 +167,51 @@ void qapi_free_%(c_name)s(%(c_name)s *obj) return ret -class QAPISchemaGenTypeVisitor(QAPISchemaMonolithicCVisitor): +class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): - def __init__(self, prefix, opt_builtins): - QAPISchemaMonolithicCVisitor.__init__( + def __init__(self, prefix): + QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-types', ' * Schema-defined QAPI types', __doc__) - self._opt_builtins = opt_builtins + self._add_module(None, ' * Built-in QAPI types') + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "qapi/dealloc-visitor.h" +#include "qapi-builtin-types.h" +#include "qapi-builtin-visit.h" +''')) + self._genh.preamble_add(mcgen(''' +#include "qapi/util.h" +''')) + + def _begin_module(self, name): self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" #include "%(prefix)sqapi-types.h" #include "%(prefix)sqapi-visit.h" ''', - prefix=prefix)) + prefix=self._prefix)) self._genh.preamble_add(mcgen(''' -#include "qapi/util.h" +#include "qapi-builtin-types.h" ''')) - self._btin = '\n' + guardstart('QAPI_TYPES_BUILTIN') def visit_begin(self, schema): # gen_object() is recursive, ensure it doesn't visit the empty type objects_seen.add(schema.the_empty_object_type.name) - def visit_end(self): - # To avoid header dependency hell, we always generate - # declarations for built-in types in our header files and - # simply guard them. See also opt_builtins (command line - # option -b). - self._btin += guardend('QAPI_TYPES_BUILTIN') - self._genh.preamble_add(self._btin) - self._btin = None - def _gen_type_cleanup(self, name): self._genh.add(gen_type_cleanup_decl(name)) self._genc.add(gen_type_cleanup(name)) def visit_enum_type(self, name, info, values, prefix): - # Special case for our lone builtin enum type - # TODO use something cleaner than existence of info - if not info: - self._btin += gen_enum(name, values, prefix) - if self._opt_builtins: - self._genc.add(gen_enum_lookup(name, values, prefix)) - else: - self._genh.preamble_add(gen_enum(name, values, prefix)) - self._genc.add(gen_enum_lookup(name, values, prefix)) + self._genh.preamble_add(gen_enum(name, values, prefix)) + self._genc.add(gen_enum_lookup(name, values, prefix)) def visit_array_type(self, name, info, element_type): - if isinstance(element_type, QAPISchemaBuiltinType): - self._btin += gen_fwd_object_or_array(name) - self._btin += gen_array(name, element_type) - self._btin += gen_type_cleanup_decl(name) - if self._opt_builtins: - self._genc.add(gen_type_cleanup(name)) - else: - self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_array(name, element_type)) - self._gen_type_cleanup(name) + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_array(name, element_type)) + self._gen_type_cleanup(name) def visit_object_type(self, name, info, base, members, variants): # Nothing to do for the special empty builtin @@ -248,6 +235,6 @@ class QAPISchemaGenTypeVisitor(QAPISchemaMonolithicCVisitor): def gen_types(schema, output_dir, prefix, opt_builtins): - vis = QAPISchemaGenTypeVisitor(prefix, opt_builtins) + vis = QAPISchemaGenTypeVisitor(prefix) schema.visit(vis) - vis.write(output_dir) + vis.write(output_dir, opt_builtins) diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 3d09d44265..9b678e7263 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -263,13 +263,27 @@ out: c_name=c_name(name)) -class QAPISchemaGenVisitVisitor(QAPISchemaMonolithicCVisitor): +class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): - def __init__(self, prefix, opt_builtins): - QAPISchemaMonolithicCVisitor.__init__( + def __init__(self, prefix): + QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-visit', ' * Schema-defined QAPI visitors', __doc__) - self._opt_builtins = opt_builtins + self._add_module(None, ' * Built-in QAPI visitors') + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi-builtin-visit.h" +''')) + self._genh.preamble_add(mcgen(''' +#include "qapi/visitor.h" +#include "qapi-builtin-types.h" + +''', + prefix=prefix)) + + def _begin_module(self, name): self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qemu-common.h" @@ -277,45 +291,21 @@ class QAPISchemaGenVisitVisitor(QAPISchemaMonolithicCVisitor): #include "qapi/qmp/qerror.h" #include "%(prefix)sqapi-visit.h" ''', - prefix=prefix)) + prefix=self._prefix)) self._genh.preamble_add(mcgen(''' -#include "qapi/visitor.h" +#include "qapi-builtin-visit.h" #include "%(prefix)sqapi-types.h" ''', - prefix=prefix)) - self._btin = guardstart('QAPI_VISIT_BUILTIN') - - def visit_end(self): - # To avoid header dependency hell, we always generate - # declarations for built-in types in our header files and - # simply guard them. See also opt_builtins (command line - # option -b). - self._btin += guardend('QAPI_VISIT_BUILTIN') - self._genh.preamble_add(self._btin) - self._btin = None + prefix=self._prefix)) def visit_enum_type(self, name, info, values, prefix): - # Special case for our lone builtin enum type - # TODO use something cleaner than existence of info - if not info: - self._btin += gen_visit_decl(name, scalar=True) - if self._opt_builtins: - self._genc.add(gen_visit_enum(name)) - else: - self._genh.add(gen_visit_decl(name, scalar=True)) - self._genc.add(gen_visit_enum(name)) + self._genh.add(gen_visit_decl(name, scalar=True)) + self._genc.add(gen_visit_enum(name)) def visit_array_type(self, name, info, element_type): - decl = gen_visit_decl(name) - defn = gen_visit_list(name, element_type) - if isinstance(element_type, QAPISchemaBuiltinType): - self._btin += decl - if self._opt_builtins: - self._genc.add(defn) - else: - self._genh.add(decl) - self._genc.add(defn) + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_list(name, element_type)) def visit_object_type(self, name, info, base, members, variants): # Nothing to do for the special empty builtin @@ -336,6 +326,6 @@ class QAPISchemaGenVisitVisitor(QAPISchemaMonolithicCVisitor): def gen_visit(schema, output_dir, prefix, opt_builtins): - vis = QAPISchemaGenVisitVisitor(prefix, opt_builtins) + vis = QAPISchemaGenVisitVisitor(prefix) schema.visit(vis) - vis.write(output_dir) + vis.write(output_dir, opt_builtins) -- cgit 1.4.1 From f9c146399dabefb8cd13c9c467a9e710af15ea70 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:35:59 +0100 Subject: qapi/common: Fix guardname() for funny filenames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit guardname() fails to return a valid C identifier for arguments containing anything but [A-Za-z0-9_.-']. Fix that. Don't bother protecting ticklish identifiers; header guards are all-caps, and no ticklish identifiers are. Signed-off-by: Markus Armbruster Reviewed-by: Marc-André Lureau Reviewed-by: Eric Blake Message-Id: <20180211093607.27351-22-armbru@redhat.com> Reviewed-by: Michael Roth Signed-off-by: Eric Blake --- scripts/qapi/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 547656c8b2..069ec3715d 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1867,7 +1867,7 @@ def mcgen(code, **kwds): def guardname(filename): - return c_name(filename, protect=False).upper() + return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper() def guardstart(name): -- cgit 1.4.1 From 252dc3105fc494182e236e97fe20f2d6b1d652cb Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:36:00 +0100 Subject: qapi: Generate separate .h, .c for each module Our qapi-schema.json is composed of modules connected by include directives, but the generated code is monolithic all the same: one qapi-types.h with all the types, one qapi-visit.h with all the visitors, and so forth. These monolithic headers get included all over the place. In my "build everything" tree, adding a QAPI type recompiles about 4800 out of 5100 objects. We wouldn't write such monolithic headers by hand. It stands to reason that we shouldn't generate them, either. Split up generated qapi-types.h to mirror the schema's modular structure: one header per module. Name the main module's header qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h. Mirror the schema's includes in the headers, so that qapi-types.h gets you everything exactly as before. If you need less, you can include one or more of the sub-module headers. To be exploited shortly. Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h, qmp-commands.c, qapi-event.h, qapi-event.c the same way. qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic. The split of qmp-commands.c duplicates static helper function qmp_marshal_output_str() in qapi-commands-char.c and qapi-commands-misc.c. This happens when commands returning the same type occur in multiple modules. Not worth avoiding. Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and qmp-commands.[ch] to qapi-commands.[ch], name the shards that way already, to reduce churn. This requires temporary hacks in commands.py and events.py. Similarly, c_name() must temporarily be taught to munge '/' in common.py. They'll go away with the rename. Signed-off-by: Markus Armbruster Message-Id: <20180211093607.27351-23-armbru@redhat.com> Reviewed-by: Eric Blake [eblake: declare a dummy variable in each .c file, to shut up OSX toolchain warnings about empty .o files, including hacking c_name()] Signed-off-by: Eric Blake --- .gitignore | 60 ++++++++++++++++++++++++ Makefile | 120 +++++++++++++++++++++++++++++++++++++++++++++++ Makefile.objs | 65 ++++++++++++++++++++++++- scripts/qapi/commands.py | 35 +++++++++----- scripts/qapi/common.py | 33 +++++++++++-- scripts/qapi/events.py | 19 ++++++-- 6 files changed, 310 insertions(+), 22 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/.gitignore b/.gitignore index 9477a08b6b..42c57998fd 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,67 @@ /qapi-gen-timestamp /qapi-builtin-types.[ch] /qapi-builtin-visit.[ch] +/qapi/qapi-commands-block-core.[ch] +/qapi/qapi-commands-block.[ch] +/qapi/qapi-commands-char.[ch] +/qapi/qapi-commands-common.[ch] +/qapi/qapi-commands-crypto.[ch] +/qapi/qapi-commands-introspect.[ch] +/qapi/qapi-commands-migration.[ch] +/qapi/qapi-commands-net.[ch] +/qapi/qapi-commands-rocker.[ch] +/qapi/qapi-commands-run-state.[ch] +/qapi/qapi-commands-sockets.[ch] +/qapi/qapi-commands-tpm.[ch] +/qapi/qapi-commands-trace.[ch] +/qapi/qapi-commands-transaction.[ch] +/qapi/qapi-commands-ui.[ch] +/qapi/qapi-events-block-core.[ch] +/qapi/qapi-events-block.[ch] +/qapi/qapi-events-char.[ch] +/qapi/qapi-events-common.[ch] +/qapi/qapi-events-crypto.[ch] +/qapi/qapi-events-introspect.[ch] +/qapi/qapi-events-migration.[ch] +/qapi/qapi-events-net.[ch] +/qapi/qapi-events-rocker.[ch] +/qapi/qapi-events-run-state.[ch] +/qapi/qapi-events-sockets.[ch] +/qapi/qapi-events-tpm.[ch] +/qapi/qapi-events-trace.[ch] +/qapi/qapi-events-transaction.[ch] +/qapi/qapi-events-ui.[ch] +/qapi/qapi-types-block-core.[ch] +/qapi/qapi-types-block.[ch] +/qapi/qapi-types-char.[ch] +/qapi/qapi-types-common.[ch] +/qapi/qapi-types-crypto.[ch] +/qapi/qapi-types-introspect.[ch] +/qapi/qapi-types-migration.[ch] +/qapi/qapi-types-net.[ch] +/qapi/qapi-types-rocker.[ch] +/qapi/qapi-types-run-state.[ch] +/qapi/qapi-types-sockets.[ch] +/qapi/qapi-types-tpm.[ch] +/qapi/qapi-types-trace.[ch] +/qapi/qapi-types-transaction.[ch] +/qapi/qapi-types-ui.[ch] /qapi-types.[ch] +/qapi/qapi-visit-block-core.[ch] +/qapi/qapi-visit-block.[ch] +/qapi/qapi-visit-char.[ch] +/qapi/qapi-visit-common.[ch] +/qapi/qapi-visit-crypto.[ch] +/qapi/qapi-visit-introspect.[ch] +/qapi/qapi-visit-migration.[ch] +/qapi/qapi-visit-net.[ch] +/qapi/qapi-visit-rocker.[ch] +/qapi/qapi-visit-run-state.[ch] +/qapi/qapi-visit-sockets.[ch] +/qapi/qapi-visit-tpm.[ch] +/qapi/qapi-visit-trace.[ch] +/qapi/qapi-visit-transaction.[ch] +/qapi/qapi-visit-ui.[ch] /qapi-visit.[ch] /qapi-event.[ch] /qapi-doc.texi diff --git a/Makefile b/Makefile index 494ae38279..b12fcd5d8f 100644 --- a/Makefile +++ b/Makefile @@ -92,10 +92,70 @@ include $(SRC_PATH)/rules.mak GENERATED_FILES = qemu-version.h config-host.h qemu-options.def GENERATED_FILES += qapi-builtin-types.h qapi-builtin-types.c GENERATED_FILES += qapi-types.h qapi-types.c +GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c +GENERATED_FILES += qapi/qapi-types-block.h qapi/qapi-types-block.c +GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c +GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c +GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c +GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c +GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c +GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c +GENERATED_FILES += qapi/qapi-types-rocker.h qapi/qapi-types-rocker.c +GENERATED_FILES += qapi/qapi-types-run-state.h qapi/qapi-types-run-state.c +GENERATED_FILES += qapi/qapi-types-sockets.h qapi/qapi-types-sockets.c +GENERATED_FILES += qapi/qapi-types-tpm.h qapi/qapi-types-tpm.c +GENERATED_FILES += qapi/qapi-types-trace.h qapi/qapi-types-trace.c +GENERATED_FILES += qapi/qapi-types-transaction.h qapi/qapi-types-transaction.c +GENERATED_FILES += qapi/qapi-types-ui.h qapi/qapi-types-ui.c GENERATED_FILES += qapi-builtin-visit.h qapi-builtin-visit.c GENERATED_FILES += qapi-visit.h qapi-visit.c +GENERATED_FILES += qapi/qapi-visit-block-core.h qapi/qapi-visit-block-core.c +GENERATED_FILES += qapi/qapi-visit-block.h qapi/qapi-visit-block.c +GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c +GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c +GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c +GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c +GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c +GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c +GENERATED_FILES += qapi/qapi-visit-rocker.h qapi/qapi-visit-rocker.c +GENERATED_FILES += qapi/qapi-visit-run-state.h qapi/qapi-visit-run-state.c +GENERATED_FILES += qapi/qapi-visit-sockets.h qapi/qapi-visit-sockets.c +GENERATED_FILES += qapi/qapi-visit-tpm.h qapi/qapi-visit-tpm.c +GENERATED_FILES += qapi/qapi-visit-trace.h qapi/qapi-visit-trace.c +GENERATED_FILES += qapi/qapi-visit-transaction.h qapi/qapi-visit-transaction.c +GENERATED_FILES += qapi/qapi-visit-ui.h qapi/qapi-visit-ui.c GENERATED_FILES += qmp-commands.h qmp-commands.c +GENERATED_FILES += qapi/qapi-commands-block-core.h qapi/qapi-commands-block-core.c +GENERATED_FILES += qapi/qapi-commands-block.h qapi/qapi-commands-block.c +GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c +GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c +GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c +GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c +GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c +GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c +GENERATED_FILES += qapi/qapi-commands-rocker.h qapi/qapi-commands-rocker.c +GENERATED_FILES += qapi/qapi-commands-run-state.h qapi/qapi-commands-run-state.c +GENERATED_FILES += qapi/qapi-commands-sockets.h qapi/qapi-commands-sockets.c +GENERATED_FILES += qapi/qapi-commands-tpm.h qapi/qapi-commands-tpm.c +GENERATED_FILES += qapi/qapi-commands-trace.h qapi/qapi-commands-trace.c +GENERATED_FILES += qapi/qapi-commands-transaction.h qapi/qapi-commands-transaction.c +GENERATED_FILES += qapi/qapi-commands-ui.h qapi/qapi-commands-ui.c GENERATED_FILES += qapi-event.h qapi-event.c +GENERATED_FILES += qapi/qapi-events-block-core.h qapi/qapi-events-block-core.c +GENERATED_FILES += qapi/qapi-events-block.h qapi/qapi-events-block.c +GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c +GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c +GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c +GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c +GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c +GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c +GENERATED_FILES += qapi/qapi-events-rocker.h qapi/qapi-events-rocker.c +GENERATED_FILES += qapi/qapi-events-run-state.h qapi/qapi-events-run-state.c +GENERATED_FILES += qapi/qapi-events-sockets.h qapi/qapi-events-sockets.c +GENERATED_FILES += qapi/qapi-events-tpm.h qapi/qapi-events-tpm.c +GENERATED_FILES += qapi/qapi-events-trace.h qapi/qapi-events-trace.c +GENERATED_FILES += qapi/qapi-events-transaction.h qapi/qapi-events-transaction.c +GENERATED_FILES += qapi/qapi-events-ui.h qapi/qapi-events-ui.c GENERATED_FILES += qmp-introspect.c qmp-introspect.h GENERATED_FILES += qapi-doc.texi @@ -524,10 +584,70 @@ qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \ qapi-builtin-types.c qapi-builtin-types.h \ qapi-types.c qapi-types.h \ +qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \ +qapi/qapi-types-block.c qapi/qapi-types-block.h \ +qapi/qapi-types-char.c qapi/qapi-types-char.h \ +qapi/qapi-types-common.c qapi/qapi-types-common.h \ +qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ +qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ +qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ +qapi/qapi-types-net.c qapi/qapi-types-net.h \ +qapi/qapi-types-rocker.c qapi/qapi-types-rocker.h \ +qapi/qapi-types-run-state.c qapi/qapi-types-run-state.h \ +qapi/qapi-types-sockets.c qapi/qapi-types-sockets.h \ +qapi/qapi-types-tpm.c qapi/qapi-types-tpm.h \ +qapi/qapi-types-trace.c qapi/qapi-types-trace.h \ +qapi/qapi-types-transaction.c qapi/qapi-types-transaction.h \ +qapi/qapi-types-ui.c qapi/qapi-types-ui.h \ qapi-builtin-visit.c qapi-builtin-visit.h \ qapi-visit.c qapi-visit.h \ +qapi/qapi-visit-block-core.c qapi/qapi-visit-block-core.h \ +qapi/qapi-visit-block.c qapi/qapi-visit-block.h \ +qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ +qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ +qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ +qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ +qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ +qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ +qapi/qapi-visit-rocker.c qapi/qapi-visit-rocker.h \ +qapi/qapi-visit-run-state.c qapi/qapi-visit-run-state.h \ +qapi/qapi-visit-sockets.c qapi/qapi-visit-sockets.h \ +qapi/qapi-visit-tpm.c qapi/qapi-visit-tpm.h \ +qapi/qapi-visit-trace.c qapi/qapi-visit-trace.h \ +qapi/qapi-visit-transaction.c qapi/qapi-visit-transaction.h \ +qapi/qapi-visit-ui.c qapi/qapi-visit-ui.h \ qmp-commands.h qmp-commands.c \ +qapi/qapi-commands-block-core.c qapi/qapi-commands-block-core.h \ +qapi/qapi-commands-block.c qapi/qapi-commands-block.h \ +qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ +qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ +qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ +qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ +qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ +qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ +qapi/qapi-commands-rocker.c qapi/qapi-commands-rocker.h \ +qapi/qapi-commands-run-state.c qapi/qapi-commands-run-state.h \ +qapi/qapi-commands-sockets.c qapi/qapi-commands-sockets.h \ +qapi/qapi-commands-tpm.c qapi/qapi-commands-tpm.h \ +qapi/qapi-commands-trace.c qapi/qapi-commands-trace.h \ +qapi/qapi-commands-transaction.c qapi/qapi-commands-transaction.h \ +qapi/qapi-commands-ui.c qapi/qapi-commands-ui.h \ qapi-event.c qapi-event.h \ +qapi/qapi-events-block-core.c qapi/qapi-events-block-core.h \ +qapi/qapi-events-block.c qapi/qapi-events-block.h \ +qapi/qapi-events-char.c qapi/qapi-events-char.h \ +qapi/qapi-events-common.c qapi/qapi-events-common.h \ +qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ +qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ +qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ +qapi/qapi-events-net.c qapi/qapi-events-net.h \ +qapi/qapi-events-rocker.c qapi/qapi-events-rocker.h \ +qapi/qapi-events-run-state.c qapi/qapi-events-run-state.h \ +qapi/qapi-events-sockets.c qapi/qapi-events-sockets.h \ +qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \ +qapi/qapi-events-trace.c qapi/qapi-events-trace.h \ +qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \ +qapi/qapi-events-ui.c qapi/qapi-events-ui.h \ qmp-introspect.h qmp-introspect.c \ qapi-doc.texi: \ qapi-gen-timestamp ; diff --git a/Makefile.objs b/Makefile.objs index 2ace9c13b9..5dc7a13c72 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -3,8 +3,56 @@ stub-obj-y = stubs/ crypto/ util-obj-y = util/ qobject/ qapi/ util-obj-y += qapi-builtin-types.o +util-obj-y += qapi-types.o +util-obj-y += qapi/qapi-types-block-core.o +util-obj-y += qapi/qapi-types-block.o +util-obj-y += qapi/qapi-types-char.o +util-obj-y += qapi/qapi-types-common.o +util-obj-y += qapi/qapi-types-crypto.o +util-obj-y += qapi/qapi-types-introspect.o +util-obj-y += qapi/qapi-types-migration.o +util-obj-y += qapi/qapi-types-net.o +util-obj-y += qapi/qapi-types-rocker.o +util-obj-y += qapi/qapi-types-run-state.o +util-obj-y += qapi/qapi-types-sockets.o +util-obj-y += qapi/qapi-types-tpm.o +util-obj-y += qapi/qapi-types-trace.o +util-obj-y += qapi/qapi-types-transaction.o +util-obj-y += qapi/qapi-types-ui.o util-obj-y += qapi-builtin-visit.o -util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o +util-obj-y += qapi-visit.o +util-obj-y += qapi/qapi-visit-block-core.o +util-obj-y += qapi/qapi-visit-block.o +util-obj-y += qapi/qapi-visit-char.o +util-obj-y += qapi/qapi-visit-common.o +util-obj-y += qapi/qapi-visit-crypto.o +util-obj-y += qapi/qapi-visit-introspect.o +util-obj-y += qapi/qapi-visit-migration.o +util-obj-y += qapi/qapi-visit-net.o +util-obj-y += qapi/qapi-visit-rocker.o +util-obj-y += qapi/qapi-visit-run-state.o +util-obj-y += qapi/qapi-visit-sockets.o +util-obj-y += qapi/qapi-visit-tpm.o +util-obj-y += qapi/qapi-visit-trace.o +util-obj-y += qapi/qapi-visit-transaction.o +util-obj-y += qapi/qapi-visit-ui.o +util-obj-y += qapi-event.o +util-obj-y += qapi/qapi-events-block-core.o +util-obj-y += qapi/qapi-events-block.o +util-obj-y += qapi/qapi-events-char.o +util-obj-y += qapi/qapi-events-common.o +util-obj-y += qapi/qapi-events-crypto.o +util-obj-y += qapi/qapi-events-introspect.o +util-obj-y += qapi/qapi-events-migration.o +util-obj-y += qapi/qapi-events-net.o +util-obj-y += qapi/qapi-events-rocker.o +util-obj-y += qapi/qapi-events-run-state.o +util-obj-y += qapi/qapi-events-sockets.o +util-obj-y += qapi/qapi-events-tpm.o +util-obj-y += qapi/qapi-events-trace.o +util-obj-y += qapi/qapi-events-transaction.o +util-obj-y += qapi/qapi-events-ui.o +util-obj-y += qmp-introspect.o chardev-obj-y = chardev/ @@ -81,6 +129,21 @@ common-obj-$(CONFIG_FDT) += device_tree.o # qapi common-obj-y += qmp-commands.o +common-obj-y += qapi/qapi-commands-block-core.o +common-obj-y += qapi/qapi-commands-block.o +common-obj-y += qapi/qapi-commands-char.o +common-obj-y += qapi/qapi-commands-common.o +common-obj-y += qapi/qapi-commands-crypto.o +common-obj-y += qapi/qapi-commands-introspect.o +common-obj-y += qapi/qapi-commands-migration.o +common-obj-y += qapi/qapi-commands-net.o +common-obj-y += qapi/qapi-commands-rocker.o +common-obj-y += qapi/qapi-commands-run-state.o +common-obj-y += qapi/qapi-commands-sockets.o +common-obj-y += qapi/qapi-commands-tpm.o +common-obj-y += qapi/qapi-commands-trace.o +common-obj-y += qapi/qapi-commands-transaction.o +common-obj-y += qapi/qapi-commands-ui.o common-obj-y += qmp-introspect.o common-obj-y += qmp.o hmp.o endif diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 46757db771..a43bccb190 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -223,14 +223,24 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) return ret -class QAPISchemaGenCommandVisitor(QAPISchemaMonolithicCVisitor): +class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): def __init__(self, prefix): - QAPISchemaMonolithicCVisitor.__init__( - self, prefix, 'qmp-commands', + QAPISchemaModularCVisitor.__init__( + self, prefix, 'qapi-commands', ' * Schema-defined QAPI/QMP commands', __doc__) self._regy = '' - self._visited_ret_types = set() + self._visited_ret_types = {} + + # Temporary HACK: + def _module_basename(self, what, name): + basename = QAPISchemaModularCVisitor._module_basename(self, what, name) + if name == self._main_module: + return re.sub(r'qapi-commands', 'qmp-commands', basename) + return basename + + def _begin_module(self, name): + self._visited_ret_types[self._genc] = set() self._genc.add(mcgen(''' #include "qemu/osdep.h" #include "qemu-common.h" @@ -246,26 +256,29 @@ class QAPISchemaGenCommandVisitor(QAPISchemaMonolithicCVisitor): #include "%(prefix)sqmp-commands.h" ''', - prefix=prefix)) + prefix=self._prefix)) self._genh.add(mcgen(''' #include "%(prefix)sqapi-types.h" #include "qapi/qmp/dispatch.h" -void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); ''', - prefix=prefix, - c_prefix=c_name(prefix, protect=False))) + prefix=self._prefix)) def visit_end(self): - self._genc.add(gen_registry(self._regy, self._prefix)) + (genc, genh) = self._module[self._main_module] + genh.add(mcgen(''' +void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); +''', + c_prefix=c_name(self._prefix, protect=False))) + genc.add(gen_registry(self._regy, self._prefix)) def visit_command(self, name, info, arg_type, ret_type, gen, success_response, boxed): if not gen: return self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) - if ret_type and ret_type not in self._visited_ret_types: - self._visited_ret_types.add(ret_type) + if ret_type and ret_type not in self._visited_ret_types[self._genc]: + self._visited_ret_types[self._genc].add(ret_type) self._genc.add(gen_marshal_output(ret_type)) self._genh.add(gen_marshal_decl(name)) self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 069ec3715d..e0b88f1896 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1775,10 +1775,11 @@ def c_enum_const(type_name, const_name, prefix=None): type_name = prefix return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() +# Temporary HACK for '/': if hasattr(str, 'maketrans'): - c_name_trans = str.maketrans('.-', '__') + c_name_trans = str.maketrans('.-/', '___') else: - c_name_trans = string.maketrans('.-', '__') + c_name_trans = string.maketrans('.-/', '___') # Map @name to a valid C identifier. @@ -2035,6 +2036,13 @@ class QAPIGenC(QAPIGen): ''', blurb=self._blurb, copyright=self._copyright) + def _bottom(self, fname): + return mcgen(''' +/* Dummy declaration to prevent empty .o file */ +char dummy_%(name)s; +''', + name=c_name(fname)) + class QAPIGenH(QAPIGenC): @@ -2073,13 +2081,20 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): self._blurb = blurb self._pydoc = pydoc self._module = {} + self._main_module = None def _module_basename(self, what, name): if name is None: return re.sub(r'-', '-builtin-', what) - return self._prefix + what + basename = os.path.join(os.path.dirname(name), + self._prefix + what) + if name == self._main_module: + return basename + return basename + '-' + os.path.splitext(os.path.basename(name))[0] def _add_module(self, name, blurb): + if self._main_module is None and name is not None: + self._main_module = name genc = QAPIGenC(blurb, self._pydoc) genh = QAPIGenH(blurb, self._pydoc) self._module[name] = (genc, genh) @@ -2088,7 +2103,7 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): def _set_module(self, name): self._genc, self._genh = self._module[name] - def write(self, output_dir, opt_builtins): + def write(self, output_dir, opt_builtins=False): for name in self._module: if name is None and not opt_builtins: continue @@ -2101,7 +2116,15 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): pass def visit_module(self, name): - if len(self._module) != 1: + if name in self._module: + self._set_module(name) return self._add_module(name, self._blurb) self._begin_module(name) + + def visit_include(self, name, info): + basename = self._module_basename(self._what, name) + self._genh.preamble_add(mcgen(''' +#include "%(basename)s.h" +''', + basename=basename)) diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 81ab3abb30..1e0b990f35 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -148,14 +148,23 @@ out: return ret -class QAPISchemaGenEventVisitor(QAPISchemaMonolithicCVisitor): +class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): def __init__(self, prefix): - QAPISchemaMonolithicCVisitor.__init__( - self, prefix, 'qapi-event', + QAPISchemaModularCVisitor.__init__( + self, prefix, 'qapi-events', ' * Schema-defined QAPI/QMP events', __doc__) self._enum_name = c_name(prefix + 'QAPIEvent', protect=False) self._event_names = [] + + # Temporary HACK: + def _module_basename(self, what, name): + basename = QAPISchemaModularCVisitor._module_basename(self, what, name) + if name == self._main_module: + return re.sub(r'qapi-events', 'qapi-event', basename) + return basename + + def _begin_module(self, name): self._genc.add(mcgen(''' #include "qemu/osdep.h" #include "qemu-common.h" @@ -167,13 +176,13 @@ class QAPISchemaGenEventVisitor(QAPISchemaMonolithicCVisitor): #include "qapi/qmp-event.h" ''', - prefix=prefix)) + prefix=self._prefix)) self._genh.add(mcgen(''' #include "qapi/util.h" #include "%(prefix)sqapi-types.h" ''', - prefix=prefix)) + prefix=self._prefix)) def visit_end(self): self._genh.add(gen_enum(self._enum_name, self._event_names)) -- cgit 1.4.1 From eb815e248f50cde9ab86eddd57eca5019b71ca78 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 11 Feb 2018 10:36:05 +0100 Subject: qapi: Move qapi-schema.json to qapi/, rename generated files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move qapi-schema.json to qapi/, so it's next to its modules, and all files get generated to qapi/, not just the ones generated for modules. Consistently name the generated files qapi-MODULE.EXT: qmp-commands.[ch] become qapi-commands.[ch], qapi-event.[ch] become qapi-events.[ch], and qmp-introspect.[ch] become qapi-introspect.[ch]. This gets rid of the temporary hacks in scripts/qapi/commands.py, scripts/qapi/events.py, and scripts/qapi/common.py. Signed-off-by: Markus Armbruster Message-Id: <20180211093607.27351-28-armbru@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Marc-André Lureau Reviewed-by: Michael Roth [eblake: Fix trailing dot in tpm.c, undo temporary hack for OSX toolchain] Signed-off-by: Eric Blake --- .gitignore | 16 +++---- Makefile | 42 ++++++++-------- Makefile.objs | 21 ++++---- backends/hostmem.c | 2 +- docs/devel/qapi-code-gen.txt | 30 ++++++------ docs/devel/writing-qmp-commands.txt | 2 +- docs/interop/qmp-intro.txt | 2 +- hmp.c | 2 +- include/qapi/qmp/qobject.h | 2 +- include/qapi/visitor.h | 2 +- include/qom/object.h | 2 +- monitor.c | 6 +-- net/filter-buffer.c | 2 +- qapi-schema.json | 95 ------------------------------------- qapi/misc.json | 4 +- qapi/qapi-schema.json | 95 +++++++++++++++++++++++++++++++++++++ qga/Makefile.objs | 2 +- qga/commands-posix.c | 2 +- qga/commands-win32.c | 2 +- qga/commands.c | 2 +- qga/main.c | 2 +- qom/object.c | 2 +- scripts/qapi/commands.py | 7 --- scripts/qapi/common.py | 5 +- scripts/qapi/events.py | 9 +--- scripts/qapi/introspect.py | 4 +- scripts/qapi/types.py | 6 +-- scripts/qapi/visit.py | 6 +-- tests/.gitignore | 6 +-- tests/Makefile.include | 14 +++--- tests/test-qmp-cmds.c | 2 +- tests/test-qmp-event.c | 2 +- tests/test-qobject-input-visitor.c | 6 +-- tpm.c | 3 +- ui/cocoa.m | 2 +- ui/vnc.c | 2 +- 36 files changed, 198 insertions(+), 213 deletions(-) delete mode 100644 qapi-schema.json create mode 100644 qapi/qapi-schema.json (limited to 'scripts/qapi/common.py') diff --git a/.gitignore b/.gitignore index 7f162e862f..dabfe6bea8 100644 --- a/.gitignore +++ b/.gitignore @@ -29,8 +29,8 @@ /qga/qapi-generated /qapi-generated /qapi-gen-timestamp -/qapi-builtin-types.[ch] -/qapi-builtin-visit.[ch] +/qapi/qapi-builtin-types.[ch] +/qapi/qapi-builtin-visit.[ch] /qapi/qapi-commands-block-core.[ch] /qapi/qapi-commands-block.[ch] /qapi/qapi-commands-char.[ch] @@ -47,6 +47,7 @@ /qapi/qapi-commands-trace.[ch] /qapi/qapi-commands-transaction.[ch] /qapi/qapi-commands-ui.[ch] +/qapi/qapi-commands.[ch] /qapi/qapi-events-block-core.[ch] /qapi/qapi-events-block.[ch] /qapi/qapi-events-char.[ch] @@ -63,6 +64,8 @@ /qapi/qapi-events-trace.[ch] /qapi/qapi-events-transaction.[ch] /qapi/qapi-events-ui.[ch] +/qapi/qapi-events.[ch] +/qapi/qapi-introspect.[ch] /qapi/qapi-types-block-core.[ch] /qapi/qapi-types-block.[ch] /qapi/qapi-types-char.[ch] @@ -79,7 +82,7 @@ /qapi/qapi-types-trace.[ch] /qapi/qapi-types-transaction.[ch] /qapi/qapi-types-ui.[ch] -/qapi-types.[ch] +/qapi/qapi-types.[ch] /qapi/qapi-visit-block-core.[ch] /qapi/qapi-visit-block.[ch] /qapi/qapi-visit-char.[ch] @@ -96,11 +99,8 @@ /qapi/qapi-visit-trace.[ch] /qapi/qapi-visit-transaction.[ch] /qapi/qapi-visit-ui.[ch] -/qapi-visit.[ch] -/qapi-event.[ch] -/qapi-doc.texi -/qmp-commands.[ch] -/qmp-introspect.[ch] +/qapi/qapi-visit.[ch] +/qapi/qapi-doc.texi /qemu-doc.html /qemu-doc.info /qemu-doc.txt diff --git a/Makefile b/Makefile index 26ed98e030..a470168d98 100644 --- a/Makefile +++ b/Makefile @@ -90,8 +90,8 @@ endif include $(SRC_PATH)/rules.mak GENERATED_FILES = qemu-version.h config-host.h qemu-options.def -GENERATED_FILES += qapi-builtin-types.h qapi-builtin-types.c -GENERATED_FILES += qapi-types.h qapi-types.c +GENERATED_FILES += qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c +GENERATED_FILES += qapi/qapi-types.h qapi/qapi-types.c GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c GENERATED_FILES += qapi/qapi-types-block.h qapi/qapi-types-block.c GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c @@ -108,8 +108,8 @@ GENERATED_FILES += qapi/qapi-types-tpm.h qapi/qapi-types-tpm.c GENERATED_FILES += qapi/qapi-types-trace.h qapi/qapi-types-trace.c GENERATED_FILES += qapi/qapi-types-transaction.h qapi/qapi-types-transaction.c GENERATED_FILES += qapi/qapi-types-ui.h qapi/qapi-types-ui.c -GENERATED_FILES += qapi-builtin-visit.h qapi-builtin-visit.c -GENERATED_FILES += qapi-visit.h qapi-visit.c +GENERATED_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c +GENERATED_FILES += qapi/qapi-visit.h qapi/qapi-visit.c GENERATED_FILES += qapi/qapi-visit-block-core.h qapi/qapi-visit-block-core.c GENERATED_FILES += qapi/qapi-visit-block.h qapi/qapi-visit-block.c GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c @@ -126,7 +126,7 @@ GENERATED_FILES += qapi/qapi-visit-tpm.h qapi/qapi-visit-tpm.c GENERATED_FILES += qapi/qapi-visit-trace.h qapi/qapi-visit-trace.c GENERATED_FILES += qapi/qapi-visit-transaction.h qapi/qapi-visit-transaction.c GENERATED_FILES += qapi/qapi-visit-ui.h qapi/qapi-visit-ui.c -GENERATED_FILES += qmp-commands.h qmp-commands.c +GENERATED_FILES += qapi/qapi-commands.h qapi/qapi-commands.c GENERATED_FILES += qapi/qapi-commands-block-core.h qapi/qapi-commands-block-core.c GENERATED_FILES += qapi/qapi-commands-block.h qapi/qapi-commands-block.c GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c @@ -143,7 +143,7 @@ GENERATED_FILES += qapi/qapi-commands-tpm.h qapi/qapi-commands-tpm.c GENERATED_FILES += qapi/qapi-commands-trace.h qapi/qapi-commands-trace.c GENERATED_FILES += qapi/qapi-commands-transaction.h qapi/qapi-commands-transaction.c GENERATED_FILES += qapi/qapi-commands-ui.h qapi/qapi-commands-ui.c -GENERATED_FILES += qapi-event.h qapi-event.c +GENERATED_FILES += qapi/qapi-events.h qapi/qapi-events.c GENERATED_FILES += qapi/qapi-events-block-core.h qapi/qapi-events-block-core.c GENERATED_FILES += qapi/qapi-events-block.h qapi/qapi-events-block.c GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c @@ -160,8 +160,8 @@ GENERATED_FILES += qapi/qapi-events-tpm.h qapi/qapi-events-tpm.c GENERATED_FILES += qapi/qapi-events-trace.h qapi/qapi-events-trace.c GENERATED_FILES += qapi/qapi-events-transaction.h qapi/qapi-events-transaction.c GENERATED_FILES += qapi/qapi-events-ui.h qapi/qapi-events-ui.c -GENERATED_FILES += qmp-introspect.c qmp-introspect.h -GENERATED_FILES += qapi-doc.texi +GENERATED_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h +GENERATED_FILES += qapi/qapi-doc.texi GENERATED_FILES += trace/generated-tcg-tracers.h @@ -562,7 +562,7 @@ $(SRC_PATH)/scripts/qapi-gen.py qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h \ -qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-commands.c \ +qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \ qga/qapi-generated/qga-qapi-doc.texi: \ qga/qapi-generated/qapi-gen-timestamp ; qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) @@ -571,7 +571,7 @@ qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-p "GEN","$(@:%-timestamp=%)") @>$@ -qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \ +qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ $(SRC_PATH)/qapi/char.json \ $(SRC_PATH)/qapi/crypto.json \ @@ -587,8 +587,8 @@ qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \ $(SRC_PATH)/qapi/transaction.json \ $(SRC_PATH)/qapi/ui.json -qapi-builtin-types.c qapi-builtin-types.h \ -qapi-types.c qapi-types.h \ +qapi/qapi-builtin-types.c qapi/qapi-builtin-types.h \ +qapi/qapi-types.c qapi/qapi-types.h \ qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \ qapi/qapi-types-block.c qapi/qapi-types-block.h \ qapi/qapi-types-char.c qapi/qapi-types-char.h \ @@ -605,8 +605,8 @@ qapi/qapi-types-tpm.c qapi/qapi-types-tpm.h \ qapi/qapi-types-trace.c qapi/qapi-types-trace.h \ qapi/qapi-types-transaction.c qapi/qapi-types-transaction.h \ qapi/qapi-types-ui.c qapi/qapi-types-ui.h \ -qapi-builtin-visit.c qapi-builtin-visit.h \ -qapi-visit.c qapi-visit.h \ +qapi/qapi-builtin-visit.c qapi/qapi-builtin-visit.h \ +qapi/qapi-visit.c qapi/qapi-visit.h \ qapi/qapi-visit-block-core.c qapi/qapi-visit-block-core.h \ qapi/qapi-visit-block.c qapi/qapi-visit-block.h \ qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ @@ -623,7 +623,7 @@ qapi/qapi-visit-tpm.c qapi/qapi-visit-tpm.h \ qapi/qapi-visit-trace.c qapi/qapi-visit-trace.h \ qapi/qapi-visit-transaction.c qapi/qapi-visit-transaction.h \ qapi/qapi-visit-ui.c qapi/qapi-visit-ui.h \ -qmp-commands.h qmp-commands.c \ +qapi/qapi-commands.h qapi/qapi-commands.c \ qapi/qapi-commands-block-core.c qapi/qapi-commands-block-core.h \ qapi/qapi-commands-block.c qapi/qapi-commands-block.h \ qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ @@ -640,7 +640,7 @@ qapi/qapi-commands-tpm.c qapi/qapi-commands-tpm.h \ qapi/qapi-commands-trace.c qapi/qapi-commands-trace.h \ qapi/qapi-commands-transaction.c qapi/qapi-commands-transaction.h \ qapi/qapi-commands-ui.c qapi/qapi-commands-ui.h \ -qapi-event.c qapi-event.h \ +qapi/qapi-events.c qapi/qapi-events.h \ qapi/qapi-events-block-core.c qapi/qapi-events-block-core.h \ qapi/qapi-events-block.c qapi/qapi-events-block.h \ qapi/qapi-events-char.c qapi/qapi-events-char.h \ @@ -657,16 +657,16 @@ qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \ qapi/qapi-events-trace.c qapi/qapi-events-trace.h \ qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \ qapi/qapi-events-ui.c qapi/qapi-events-ui.h \ -qmp-introspect.h qmp-introspect.c \ -qapi-doc.texi: \ +qapi/qapi-introspect.h qapi/qapi-introspect.c \ +qapi/qapi-doc.texi: \ qapi-gen-timestamp ; qapi-gen-timestamp: $(qapi-modules) $(qapi-py) $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ - -o "." -b $<, \ + -o "qapi" -b $<, \ "GEN","$(@:%-timestamp=%)") @>$@ -QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h) +QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h) $(qga-obj-y): $(QGALIB_GEN) qemu-ga$(EXESUF): $(qga-obj-y) $(COMMON_LDADDS) @@ -933,7 +933,7 @@ qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") -docs/interop/qemu-qmp-qapi.texi: qapi-doc.texi +docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi @cp -p $< $@ docs/interop/qemu-ga-qapi.texi: qga/qapi-generated/qga-qapi-doc.texi diff --git a/Makefile.objs b/Makefile.objs index 149627b589..d741134cc7 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -2,8 +2,8 @@ # Common libraries for tools and emulators stub-obj-y = stubs/ crypto/ util-obj-y = util/ qobject/ qapi/ -util-obj-y += qapi-builtin-types.o -util-obj-y += qapi-types.o +util-obj-y += qapi/qapi-builtin-types.o +util-obj-y += qapi/qapi-types.o util-obj-y += qapi/qapi-types-block-core.o util-obj-y += qapi/qapi-types-block.o util-obj-y += qapi/qapi-types-char.o @@ -20,8 +20,8 @@ util-obj-y += qapi/qapi-types-tpm.o util-obj-y += qapi/qapi-types-trace.o util-obj-y += qapi/qapi-types-transaction.o util-obj-y += qapi/qapi-types-ui.o -util-obj-y += qapi-builtin-visit.o -util-obj-y += qapi-visit.o +util-obj-y += qapi/qapi-builtin-visit.o +util-obj-y += qapi/qapi-visit.o util-obj-y += qapi/qapi-visit-block-core.o util-obj-y += qapi/qapi-visit-block.o util-obj-y += qapi/qapi-visit-char.o @@ -38,7 +38,7 @@ util-obj-y += qapi/qapi-visit-tpm.o util-obj-y += qapi/qapi-visit-trace.o util-obj-y += qapi/qapi-visit-transaction.o util-obj-y += qapi/qapi-visit-ui.o -util-obj-y += qapi-event.o +util-obj-y += qapi/qapi-events.o util-obj-y += qapi/qapi-events-block-core.o util-obj-y += qapi/qapi-events-block.o util-obj-y += qapi/qapi-events-char.o @@ -55,7 +55,7 @@ util-obj-y += qapi/qapi-events-tpm.o util-obj-y += qapi/qapi-events-trace.o util-obj-y += qapi/qapi-events-transaction.o util-obj-y += qapi/qapi-events-ui.o -util-obj-y += qmp-introspect.o +util-obj-y += qapi/qapi-introspect.o chardev-obj-y = chardev/ @@ -131,7 +131,7 @@ common-obj-$(CONFIG_FDT) += device_tree.o ###################################################################### # qapi -common-obj-y += qmp-commands.o +common-obj-y += qapi/qapi-commands.o common-obj-y += qapi/qapi-commands-block-core.o common-obj-y += qapi/qapi-commands-block.o common-obj-y += qapi/qapi-commands-char.o @@ -148,7 +148,7 @@ common-obj-y += qapi/qapi-commands-tpm.o common-obj-y += qapi/qapi-commands-trace.o common-obj-y += qapi/qapi-commands-transaction.o common-obj-y += qapi/qapi-commands-ui.o -common-obj-y += qmp-introspect.o +common-obj-y += qapi/qapi-introspect.o common-obj-y += qmp.o hmp.o endif @@ -171,8 +171,9 @@ target-obj-y += trace/ ###################################################################### # guest agent -# FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed -# by libqemuutil.a. These should be moved to a separate .json schema. +# FIXME: a few definitions from qapi/qapi-types.o and +# qapi/qapi-visit.o are needed by libqemuutil.a. These should be +# extracted into a QAPI schema module, or perhaps a separate schema. qga-obj-y = qga/ qga-vss-dll-obj-y = qga/ diff --git a/backends/hostmem.c b/backends/hostmem.c index 74fc04a362..f61093654e 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -14,7 +14,7 @@ #include "sysemu/hostmem.h" #include "hw/boards.h" #include "qapi/error.h" -#include "qapi-builtin-visit.h" +#include "qapi/qapi-builtin-visit.h" #include "qapi/visitor.h" #include "qemu/config-file.h" #include "qom/object_interfaces.h" diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index c86792add2..25b7180a18 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -647,7 +647,7 @@ name an event 'MAX', since the generator also produces a C enumeration of all event names with a generated _MAX value at the end. When 'data' is also specified, additional info will be included in the event, with similar semantics to a 'struct' expression. Finally there -will be C API generated in qapi-event.h; when called by QEMU code, a +will be C API generated in qapi-events.h; when called by QEMU code, a message with timestamp will be emitted on the wire. An example event is: @@ -1147,15 +1147,15 @@ declares qmp_COMMAND() that the user must implement. The following files are generated: -$(prefix)qmp-commands.c: Command marshal/dispatch functions for each - QMP command defined in the schema +$(prefix)qapi-commands.c: Command marshal/dispatch functions for each + QMP command defined in the schema -$(prefix)qmp-commands.h: Function prototypes for the QMP commands - specified in the schema +$(prefix)qapi-commands.h: Function prototypes for the QMP commands + specified in the schema Example: - $ cat qapi-generated/example-qmp-commands.h + $ cat qapi-generated/example-qapi-commands.h [Uninteresting stuff omitted...] #ifndef EXAMPLE_QMP_COMMANDS_H @@ -1170,7 +1170,7 @@ Example: void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp); #endif - $ cat qapi-generated/example-qmp-commands.c + $ cat qapi-generated/example-qapi-commands.c [Uninteresting stuff omitted...] static void qmp_marshal_output_UserDefOne(UserDefOne *ret_in, QObject **ret_out, Error **errp) @@ -1243,14 +1243,14 @@ qapi_event_send_EVENT(). The following files are created: -$(prefix)qapi-event.h - Function prototypes for each event type, plus an +$(prefix)qapi-events.h - Function prototypes for each event type, plus an enumeration of all event names -$(prefix)qapi-event.c - Implementation of functions to send an event +$(prefix)qapi-events.c - Implementation of functions to send an event Example: - $ cat qapi-generated/example-qapi-event.h + $ cat qapi-generated/example-qapi-events.h [Uninteresting stuff omitted...] #ifndef EXAMPLE_QAPI_EVENT_H @@ -1273,7 +1273,7 @@ Example: extern const char *const example_QAPIEvent_lookup[]; #endif - $ cat qapi-generated/example-qapi-event.c + $ cat qapi-generated/example-qapi-events.c [Uninteresting stuff omitted...] void qapi_event_send_my_event(Error **errp) @@ -1306,14 +1306,14 @@ Example: The following files are created: -$(prefix)qmp-introspect.c - Defines a string holding a JSON +$(prefix)qapi-introspect.c - Defines a string holding a JSON description of the schema -$(prefix)qmp-introspect.h - Declares the above string +$(prefix)qapi-introspect.h - Declares the above string Example: - $ cat qapi-generated/example-qmp-introspect.h + $ cat qapi-generated/example-qapi-introspect.h [Uninteresting stuff omitted...] #ifndef EXAMPLE_QMP_INTROSPECT_H @@ -1322,7 +1322,7 @@ Example: extern const char example_qmp_schema_json[]; #endif - $ cat qapi-generated/example-qmp-introspect.c + $ cat qapi-generated/example-qapi-introspect.c [Uninteresting stuff omitted...] const char example_qmp_schema_json[] = "[" diff --git a/docs/devel/writing-qmp-commands.txt b/docs/devel/writing-qmp-commands.txt index 50385eff27..9dfc62bf5a 100644 --- a/docs/devel/writing-qmp-commands.txt +++ b/docs/devel/writing-qmp-commands.txt @@ -419,7 +419,7 @@ There are a number of things to be noticed: allocated by the implementation. This is so because the QAPI also generates a function to free its types and it cannot distinguish between dynamically or statically allocated strings -6. You have to include "qapi/qmp-commands-misc.h" in qemu-timer.c +6. You have to include "qapi/qapi-commands-misc.h" in qemu-timer.c Time to test the new command. Build qemu, run it as described in the "Testing" section and try this: diff --git a/docs/interop/qmp-intro.txt b/docs/interop/qmp-intro.txt index 430fe1b747..900d69d612 100644 --- a/docs/interop/qmp-intro.txt +++ b/docs/interop/qmp-intro.txt @@ -79,7 +79,7 @@ Escape character is '^]'. } Please refer to docs/interop/qemu-qmp-ref.* for a complete command -reference, generated from qapi-schema.json. +reference, generated from qapi/qapi-schema.json. QMP wiki page ------------- diff --git a/hmp.c b/hmp.c index 8ea227dac4..016cb5c4f1 100644 --- a/hmp.c +++ b/hmp.c @@ -28,7 +28,7 @@ #include "monitor/qdev.h" #include "qapi/error.h" #include "qapi/opts-visitor.h" -#include "qapi-builtin-visit.h" +#include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-char.h" #include "qapi/qapi-commands-migration.h" diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index a2964fbf25..012439a2e3 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -32,7 +32,7 @@ #ifndef QOBJECT_H #define QOBJECT_H -#include "qapi-builtin-types.h" +#include "qapi/qapi-builtin-types.h" struct QObject { QType type; diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index 9e57508446..5b2ed3f202 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -15,7 +15,7 @@ #ifndef QAPI_VISITOR_H #define QAPI_VISITOR_H -#include "qapi-builtin-types.h" +#include "qapi/qapi-builtin-types.h" /* * The QAPI schema defines both a set of C data types, and a QMP wire diff --git a/include/qom/object.h b/include/qom/object.h index 5b5c016d8f..30db296af4 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -14,7 +14,7 @@ #ifndef QEMU_OBJECT_H #define QEMU_OBJECT_H -#include "qapi-builtin-types.h" +#include "qapi/qapi-builtin-types.h" #include "qemu/queue.h" struct TypeImpl; diff --git a/monitor.c b/monitor.c index fc9df6253a..57957b3969 100644 --- a/monitor.c +++ b/monitor.c @@ -69,14 +69,14 @@ #include "exec/exec-all.h" #include "qemu/log.h" #include "qemu/option.h" -#include "qmp-commands.h" #include "hmp.h" #include "qemu/thread.h" #include "block/qapi.h" +#include "qapi/qapi-commands.h" +#include "qapi/qapi-events.h" #include "qapi/error.h" #include "qapi/qmp-event.h" -#include "qapi-event.h" -#include "qmp-introspect.h" +#include "qapi/qapi-introspect.h" #include "sysemu/qtest.h" #include "sysemu/cpus.h" #include "qemu/cutils.h" diff --git a/net/filter-buffer.c b/net/filter-buffer.c index 7c487629f9..f7265c50a8 100644 --- a/net/filter-buffer.c +++ b/net/filter-buffer.c @@ -13,7 +13,7 @@ #include "qemu-common.h" #include "qemu/timer.h" #include "qemu/iov.h" -#include "qapi-builtin-visit.h" +#include "qapi/qapi-builtin-visit.h" #include "qapi/qmp/qerror.h" #include "qom/object.h" diff --git a/qapi-schema.json b/qapi-schema.json deleted file mode 100644 index 689d06c530..0000000000 --- a/qapi-schema.json +++ /dev/null @@ -1,95 +0,0 @@ -# -*- Mode: Python -*- -## -# = Introduction -# -# This document describes all commands currently supported by QMP. -# -# Most of the time their usage is exactly the same as in the user Monitor, this -# means that any other document which also describe commands (the manpage, -# QEMU's manual, etc) can and should be consulted. -# -# QMP has two types of commands: regular and query commands. Regular commands -# usually change the Virtual Machine's state someway, while query commands just -# return information. The sections below are divided accordingly. -# -# It's important to observe that all communication examples are formatted in -# a reader-friendly way, so that they're easier to understand. However, in real -# protocol usage, they're emitted as a single line. -# -# Also, the following notation is used to denote data flow: -# -# Example: -# -# | -> data issued by the Client -# | <- Server data response -# -# Please, refer to the QMP specification (docs/interop/qmp-spec.txt) for -# detailed information on the Server command and response formats. -# -# = Stability Considerations -# -# The current QMP command set (described in this file) may be useful for a -# number of use cases, however it's limited and several commands have bad -# defined semantics, specially with regard to command completion. -# -# These problems are going to be solved incrementally in the next QEMU releases -# and we're going to establish a deprecation policy for badly defined commands. -# -# If you're planning to adopt QMP, please observe the following: -# -# 1. The deprecation policy will take effect and be documented soon, please -# check the documentation of each used command as soon as a new release of -# QEMU is available -# -# 2. DO NOT rely on anything which is not explicit documented -# -# 3. Errors, in special, are not documented. Applications should NOT check -# for specific errors classes or data (it's strongly recommended to only -# check for the "error" key) -# -## - -{ 'pragma': { 'doc-required': true } } - -# Whitelists to permit QAPI rule violations; think twice before you -# add to them! -{ 'pragma': { - # Commands allowed to return a non-dictionary: - 'returns-whitelist': [ - 'human-monitor-command', - 'qom-get', - 'query-migrate-cache-size', - 'query-tpm-models', - 'query-tpm-types', - 'ringbuf-read' ], - 'name-case-whitelist': [ - 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status - 'CpuInfoMIPS', # PC, visible through query-cpu - 'CpuInfoTricore', # PC, visible through query-cpu - 'QapiErrorClass', # all members, visible through errors - 'UuidInfo', # UUID, visible through query-uuid - 'X86CPURegister32', # all members, visible indirectly through qom-get - 'q_obj_CpuInfo-base' # CPU, visible through query-cpu - ] } } - -# Documentation generated with qapi-gen.py is in source order, with -# included sub-schemas inserted at the first include directive -# (subsequent include directives have no effect). To get a sane and -# stable order, it's best to include each sub-schema just once, or -# include it first in qapi-schema.json. - -{ 'include': 'qapi/common.json' } -{ 'include': 'qapi/sockets.json' } -{ 'include': 'qapi/run-state.json' } -{ 'include': 'qapi/crypto.json' } -{ 'include': 'qapi/block.json' } -{ 'include': 'qapi/char.json' } -{ 'include': 'qapi/net.json' } -{ 'include': 'qapi/rocker.json' } -{ 'include': 'qapi/tpm.json' } -{ 'include': 'qapi/ui.json' } -{ 'include': 'qapi/migration.json' } -{ 'include': 'qapi/transaction.json' } -{ 'include': 'qapi/trace.json' } -{ 'include': 'qapi/introspect.json' } -{ 'include': 'qapi/misc.json' } diff --git a/qapi/misc.json b/qapi/misc.json index dabc987f7a..a1702c9060 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -1729,7 +1729,7 @@ # # Emitted when background dump has completed # -# @result: DumpQueryResult type described in qapi-schema.json. +# @result: final dump status # # @error: human-readable error string that provides # hint on why dump failed. Only presents on failure. The @@ -2944,7 +2944,7 @@ # # Emitted when guest executes ACPI _OST method. # -# @info: ACPIOSTInfo type as described in qapi-schema.json +# @info: OSPM Status Indication # # Since: 2.1 # diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json new file mode 100644 index 0000000000..25bce78352 --- /dev/null +++ b/qapi/qapi-schema.json @@ -0,0 +1,95 @@ +# -*- Mode: Python -*- +## +# = Introduction +# +# This document describes all commands currently supported by QMP. +# +# Most of the time their usage is exactly the same as in the user Monitor, this +# means that any other document which also describe commands (the manpage, +# QEMU's manual, etc) can and should be consulted. +# +# QMP has two types of commands: regular and query commands. Regular commands +# usually change the Virtual Machine's state someway, while query commands just +# return information. The sections below are divided accordingly. +# +# It's important to observe that all communication examples are formatted in +# a reader-friendly way, so that they're easier to understand. However, in real +# protocol usage, they're emitted as a single line. +# +# Also, the following notation is used to denote data flow: +# +# Example: +# +# | -> data issued by the Client +# | <- Server data response +# +# Please, refer to the QMP specification (docs/interop/qmp-spec.txt) for +# detailed information on the Server command and response formats. +# +# = Stability Considerations +# +# The current QMP command set (described in this file) may be useful for a +# number of use cases, however it's limited and several commands have bad +# defined semantics, specially with regard to command completion. +# +# These problems are going to be solved incrementally in the next QEMU releases +# and we're going to establish a deprecation policy for badly defined commands. +# +# If you're planning to adopt QMP, please observe the following: +# +# 1. The deprecation policy will take effect and be documented soon, please +# check the documentation of each used command as soon as a new release of +# QEMU is available +# +# 2. DO NOT rely on anything which is not explicit documented +# +# 3. Errors, in special, are not documented. Applications should NOT check +# for specific errors classes or data (it's strongly recommended to only +# check for the "error" key) +# +## + +{ 'pragma': { 'doc-required': true } } + +# Whitelists to permit QAPI rule violations; think twice before you +# add to them! +{ 'pragma': { + # Commands allowed to return a non-dictionary: + 'returns-whitelist': [ + 'human-monitor-command', + 'qom-get', + 'query-migrate-cache-size', + 'query-tpm-models', + 'query-tpm-types', + 'ringbuf-read' ], + 'name-case-whitelist': [ + 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status + 'CpuInfoMIPS', # PC, visible through query-cpu + 'CpuInfoTricore', # PC, visible through query-cpu + 'QapiErrorClass', # all members, visible through errors + 'UuidInfo', # UUID, visible through query-uuid + 'X86CPURegister32', # all members, visible indirectly through qom-get + 'q_obj_CpuInfo-base' # CPU, visible through query-cpu + ] } } + +# Documentation generated with qapi-gen.py is in source order, with +# included sub-schemas inserted at the first include directive +# (subsequent include directives have no effect). To get a sane and +# stable order, it's best to include each sub-schema just once, or +# include it first right here. + +{ 'include': 'common.json' } +{ 'include': 'sockets.json' } +{ 'include': 'run-state.json' } +{ 'include': 'crypto.json' } +{ 'include': 'block.json' } +{ 'include': 'char.json' } +{ 'include': 'net.json' } +{ 'include': 'rocker.json' } +{ 'include': 'tpm.json' } +{ 'include': 'ui.json' } +{ 'include': 'migration.json' } +{ 'include': 'transaction.json' } +{ 'include': 'trace.json' } +{ 'include': 'introspect.json' } +{ 'include': 'misc.json' } diff --git a/qga/Makefile.objs b/qga/Makefile.objs index 6151378ae4..ed08c5917c 100644 --- a/qga/Makefile.objs +++ b/qga/Makefile.objs @@ -3,6 +3,6 @@ qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o qga-obj-$(CONFIG_WIN32) += vss-win32.o qga-obj-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o -qga-obj-y += qapi-generated/qga-qmp-commands.o +qga-obj-y += qapi-generated/qga-qapi-commands.o qga-vss-dll-obj-$(CONFIG_QGA_VSS) += vss-win32/ diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 967061444a..ac17d0d6cf 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -17,7 +17,7 @@ #include #include #include "qga/guest-agent-core.h" -#include "qga-qmp-commands.h" +#include "qga-qapi-commands.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/queue.h" diff --git a/qga/commands-win32.c b/qga/commands-win32.c index bedae32957..2d48394748 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -34,7 +34,7 @@ #include "qga/guest-agent-core.h" #include "qga/vss-win32.h" -#include "qga-qmp-commands.h" +#include "qga-qapi-commands.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/queue.h" diff --git a/qga/commands.c b/qga/commands.c index 6d710dbb20..a64b34ccab 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qga/guest-agent-core.h" -#include "qga-qmp-commands.h" +#include "qga-qapi-commands.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/base64.h" diff --git a/qga/main.c b/qga/main.c index cb434d8c46..f9c83050c5 100644 --- a/qga/main.c +++ b/qga/main.c @@ -25,7 +25,7 @@ #include "qapi/qmp/qstring.h" #include "qga/guest-agent-core.h" #include "qemu/module.h" -#include "qga-qmp-commands.h" +#include "qga-qapi-commands.h" #include "qapi/qmp/qerror.h" #include "qapi/error.h" #include "qga/channel.h" diff --git a/qom/object.c b/qom/object.c index 81b4f7ac48..f70a75c308 100644 --- a/qom/object.c +++ b/qom/object.c @@ -18,7 +18,7 @@ #include "qapi/visitor.h" #include "qapi/string-input-visitor.h" #include "qapi/string-output-visitor.h" -#include "qapi-builtin-visit.h" +#include "qapi/qapi-builtin-visit.h" #include "qapi/qmp/qerror.h" #include "trace.h" diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 953449171b..21a7e0dbe6 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -232,13 +232,6 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): self._regy = '' self._visited_ret_types = {} - # Temporary HACK: - def _module_basename(self, what, name): - basename = QAPISchemaModularCVisitor._module_basename(self, what, name) - if name == self._main_module: - return re.sub(r'qapi-commands', 'qmp-commands', basename) - return basename - def _begin_module(self, name): self._visited_ret_types[self._genc] = set() commands = self._module_basename('qapi-commands', name) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index e0b88f1896..97e9060b67 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1775,11 +1775,10 @@ def c_enum_const(type_name, const_name, prefix=None): type_name = prefix return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() -# Temporary HACK for '/': if hasattr(str, 'maketrans'): - c_name_trans = str.maketrans('.-/', '___') + c_name_trans = str.maketrans('.-', '__') else: - c_name_trans = string.maketrans('.-/', '___') + c_name_trans = string.maketrans('.-', '__') # Map @name to a valid C identifier. diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 5ad6708491..3dc523cf39 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -157,20 +157,13 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): self._enum_name = c_name(prefix + 'QAPIEvent', protect=False) self._event_names = [] - # Temporary HACK: - def _module_basename(self, what, name): - basename = QAPISchemaModularCVisitor._module_basename(self, what, name) - if name == self._main_module: - return re.sub(r'qapi-events', 'qapi-event', basename) - return basename - def _begin_module(self, name): types = self._module_basename('qapi-types', name) visit = self._module_basename('qapi-visit', name) self._genc.add(mcgen(''' #include "qemu/osdep.h" #include "qemu-common.h" -#include "%(prefix)sqapi-event.h" +#include "%(prefix)sqapi-events.h" #include "%(visit)s.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index f571cc134c..f66c397fb0 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -44,7 +44,7 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): def __init__(self, prefix, unmask): QAPISchemaMonolithicCVisitor.__init__( - self, prefix, 'qmp-introspect', + self, prefix, 'qapi-introspect', ' * QAPI/QMP schema introspection', __doc__) self._unmask = unmask self._schema = None @@ -53,7 +53,7 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): self._name_map = {} self._genc.add(mcgen(''' #include "qemu/osdep.h" -#include "%(prefix)sqmp-introspect.h" +#include "%(prefix)sqapi-introspect.h" ''', prefix=prefix)) diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 2a3c502cf6..64d9c0fb37 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -177,8 +177,8 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" -#include "qapi-builtin-types.h" -#include "qapi-builtin-visit.h" +#include "qapi/qapi-builtin-types.h" +#include "qapi/qapi-builtin-visit.h" ''')) self._genh.preamble_add(mcgen(''' #include "qapi/util.h" @@ -195,7 +195,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): ''', types=types, visit=visit)) self._genh.preamble_add(mcgen(''' -#include "qapi-builtin-types.h" +#include "qapi/qapi-builtin-types.h" ''')) def visit_begin(self, schema): diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index de09966643..5d72d8936c 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -274,11 +274,11 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): #include "qemu/osdep.h" #include "qemu-common.h" #include "qapi/error.h" -#include "qapi-builtin-visit.h" +#include "qapi/qapi-builtin-visit.h" ''')) self._genh.preamble_add(mcgen(''' #include "qapi/visitor.h" -#include "qapi-builtin-types.h" +#include "qapi/qapi-builtin-types.h" ''', prefix=prefix)) @@ -295,7 +295,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): ''', visit=visit, prefix=self._prefix)) self._genh.preamble_add(mcgen(''' -#include "qapi-builtin-visit.h" +#include "qapi/qapi-builtin-visit.h" #include "%(types)s.h" ''', diff --git a/tests/.gitignore b/tests/.gitignore index 2629cfc2f9..18e58b2183 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -60,7 +60,8 @@ test-keyval test-logging test-mul64 test-opts-visitor -test-qapi-event.[ch] +test-qapi-commands.[ch] +test-qapi-events.[ch] test-qapi-types.[ch] test-qapi-util test-qapi-visit.[ch] @@ -71,11 +72,10 @@ test-qga test-qht test-qht-par test-qmp-cmds -test-qmp-commands.[ch] test-qmp-event test-qobject-input-strict test-qobject-input-visitor -test-qmp-introspect.[ch] +test-qapi-introspect.[ch] test-qobject-output-visitor test-rcu-list test-replication diff --git a/tests/Makefile.include b/tests/Makefile.include index 2de46f8acb..fdca062591 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -570,8 +570,8 @@ qapi-schema += unknown-expr-key.json check-qapi-schema-y := $(addprefix tests/qapi-schema/, $(qapi-schema)) GENERATED_FILES += tests/test-qapi-types.h tests/test-qapi-visit.h \ - tests/test-qmp-commands.h tests/test-qapi-event.h \ - tests/test-qmp-introspect.h + tests/test-qapi-commands.h tests/test-qapi-events.h \ + tests/test-qapi-introspect.h test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ tests/check-qlist.o tests/check-qnull.o tests/check-qobject.o \ @@ -596,7 +596,7 @@ QEMU_CFLAGS += -I$(SRC_PATH)/tests test-util-obj-y = libqemuutil.a test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y) test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ - tests/test-qapi-event.o tests/test-qmp-introspect.o \ + tests/test-qapi-events.o tests/test-qapi-introspect.o \ $(test-qom-obj-y) benchmark-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) @@ -660,9 +660,9 @@ tests/test-replication$(EXESUF): tests/test-replication.o $(test-util-obj-y) \ tests/test-qapi-types.c tests/test-qapi-types.h \ tests/test-qapi-visit.c tests/test-qapi-visit.h \ -tests/test-qmp-commands.h tests/test-qmp-commands.c \ -tests/test-qapi-event.c tests/test-qapi-event.h \ -tests/test-qmp-introspect.c tests/test-qmp-introspect.h: \ +tests/test-qapi-commands.h tests/test-qapi-commands.c \ +tests/test-qapi-events.c tests/test-qapi-events.h \ +tests/test-qapi-introspect.c tests/test-qapi-introspect.h: \ tests/test-qapi-gen-timestamp ; tests/test-qapi-gen-timestamp: $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ @@ -683,7 +683,7 @@ tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) tests/test-qobject-output-visitor$(EXESUF): tests/test-qobject-output-visitor.o $(test-qapi-obj-y) tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y) tests/test-qobject-input-visitor$(EXESUF): tests/test-qobject-input-visitor.o $(test-qapi-obj-y) -tests/test-qmp-cmds$(EXESUF): tests/test-qmp-cmds.o tests/test-qmp-commands.o $(test-qapi-obj-y) +tests/test-qmp-cmds$(EXESUF): tests/test-qmp-cmds.o tests/test-qapi-commands.o $(test-qapi-obj-y) tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 24660d0868..5b1cee6912 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -3,12 +3,12 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" -#include "test-qmp-commands.h" #include "qapi/error.h" #include "qemu/module.h" #include "qapi/qobject-input-visitor.h" #include "tests/test-qapi-types.h" #include "tests/test-qapi-visit.h" +#include "test-qapi-commands.h" static QmpCommandList qmp_commands; diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index 5fbe7e551f..31f35b3e66 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -14,13 +14,13 @@ #include "qemu/osdep.h" #include "qemu-common.h" -#include "test-qapi-event.h" #include "qapi/error.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp-event.h" +#include "test-qapi-events.h" typedef struct TestEventData { QDict *expect; diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c index d3a56bd071..79b1a8cb17 100644 --- a/tests/test-qobject-input-visitor.c +++ b/tests/test-qobject-input-visitor.c @@ -24,8 +24,8 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp/qjson.h" -#include "test-qmp-introspect.h" -#include "qmp-introspect.h" +#include "test-qapi-introspect.h" +#include "qapi/qapi-introspect.h" typedef struct TestInputVisitorData { QObject *obj; @@ -1376,7 +1376,7 @@ int main(int argc, char **argv) NULL, test_visitor_in_fail_alternate); input_visitor_test_add("/visitor/input/fail/union-native-list", NULL, test_visitor_in_fail_union_native_list); - input_visitor_test_add("/visitor/input/qmp-introspect", + input_visitor_test_add("/visitor/input/qapi-introspect", NULL, test_visitor_in_qmp_introspect); g_test_run(); diff --git a/tpm.c b/tpm.c index 2db03a09b2..93031723ad 100644 --- a/tpm.c +++ b/tpm.c @@ -181,8 +181,7 @@ int tpm_config_parse(QemuOptsList *opts_list, const char *optarg) } /* - * Walk the list of active TPM backends and collect information about them - * following the schema description in qapi-schema.json. + * Walk the list of active TPM backends and collect information about them. */ TPMInfoList *qmp_query_tpm(Error **errp) { diff --git a/ui/cocoa.m b/ui/cocoa.m index 90d9aa57ea..30888ca8fd 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -32,7 +32,7 @@ #include "ui/input.h" #include "sysemu/sysemu.h" #include "qapi/error.h" -#include "qmp-commands.h" +#include "qapi/qapi-commands.h" #include "sysemu/blockdev.h" #include "qemu-version.h" #include diff --git a/ui/vnc.c b/ui/vnc.c index a25e408cf0..13c28cabb0 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -35,10 +35,10 @@ #include "qemu/timer.h" #include "qemu/acl.h" #include "qemu/config-file.h" +#include "qapi/qapi-events.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" #include "ui/input.h" -#include "qapi-event.h" #include "crypto/hash.h" #include "crypto/tlscredsanon.h" #include "crypto/tlscredsx509.h" -- cgit 1.4.1