diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2019-10-23 13:39:08 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2019-10-23 13:39:08 +0100 |
commit | 69717d0f890e14cbdd668297751f9446d2e2a8fd (patch) | |
tree | 899680cc9b8e66c9cfc9fd09fd0a301f20415ba7 | |
parent | ec97eb6133e204c8c0ee492cfc9c7551b6297aca (diff) | |
parent | 5f76a7aac156ca75680dad5df4a385fd0b58f6b1 (diff) |
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2019-10-22-v3' into staging
QAPI patches for 2019-10-22
# gpg: Signature made Tue 22 Oct 2019 15:56:36 BST
# gpg: using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg: issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg: aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653
* remotes/armbru/tags/pull-qapi-2019-10-22-v3:
qapi: Allow introspecting fix for savevm's cooperation with blockdev
tests/qapi-schema: Cover feature documentation comments
tests: qapi: Test 'features' of commands
qapi: Add feature flags to commands
tests/qapi-schema: Tidy up test output indentation
qapi: Clear scripts/qapi/doc.py executable bits again
qapi: Split up scripts/qapi/common.py
qapi: Move gen_enum(), gen_enum_lookup() back to qapi/types.py
qapi: Speed up frontend tests
qapi: Eliminate accidental global frontend state
qapi: Store pragma state in QAPISourceInfo, not global state
qapi: Don't suppress doc generation without pragma doc-required
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
390 files changed, 3116 insertions, 2961 deletions
@@ -582,13 +582,20 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated qemu-keymap$(EXESUF): LIBS += $(XKBCOMMON_LIBS) qemu-keymap$(EXESUF): QEMU_CFLAGS += $(XKBCOMMON_CFLAGS) -qapi-py = $(SRC_PATH)/scripts/qapi/commands.py \ +qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \ +$(SRC_PATH)/scripts/qapi/commands.py \ +$(SRC_PATH)/scripts/qapi/common.py \ +$(SRC_PATH)/scripts/qapi/doc.py \ +$(SRC_PATH)/scripts/qapi/error.py \ $(SRC_PATH)/scripts/qapi/events.py \ +$(SRC_PATH)/scripts/qapi/expr.py \ +$(SRC_PATH)/scripts/qapi/gen.py \ $(SRC_PATH)/scripts/qapi/introspect.py \ +$(SRC_PATH)/scripts/qapi/parser.py \ +$(SRC_PATH)/scripts/qapi/schema.py \ +$(SRC_PATH)/scripts/qapi/source.py \ $(SRC_PATH)/scripts/qapi/types.py \ $(SRC_PATH)/scripts/qapi/visit.py \ -$(SRC_PATH)/scripts/qapi/common.py \ -$(SRC_PATH)/scripts/qapi/doc.py \ $(SRC_PATH)/scripts/qapi-gen.py qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 64d9e4c6a9..45c93a43cc 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -457,7 +457,8 @@ Syntax: '*gen': false, '*allow-oob': true, '*allow-preconfig': true, - '*if': COND } + '*if': COND, + '*features': FEATURES } Member 'command' names the command. @@ -640,9 +641,10 @@ change in the QMP syntax (usually by allowing values or operations that previously resulted in an error). QMP clients may still need to know whether the extension is available. -For this purpose, a list of features can be specified for a struct type. -This is exposed to the client as a list of string, where each string -signals that this build of QEMU shows a certain behaviour. +For this purpose, a list of features can be specified for a command or +struct type. This is exposed to the client as a list of strings, +where each string signals that this build of QEMU shows a certain +behaviour. Each member of the 'features' array defines a feature. It can either be { 'name': STRING, '*if': COND }, or STRING, which is shorthand for diff --git a/qapi/introspect.json b/qapi/introspect.json index 1843c1cb17..031a954fa9 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -266,13 +266,17 @@ # @allow-oob: whether the command allows out-of-band execution, # defaults to false (Since: 2.12) # +# @features: names of features associated with the command, in no particular +# order. (since 4.2) +# # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # # Since: 2.5 ## { 'struct': 'SchemaInfoCommand', 'data': { 'arg-type': 'str', 'ret-type': 'str', - '*allow-oob': 'bool' } } + '*allow-oob': 'bool', + '*features': [ 'str' ] } } ## # @SchemaInfoEvent: diff --git a/qapi/misc.json b/qapi/misc.json index 6bd11f50e6..33b94e3589 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -1020,6 +1020,12 @@ # # @cpu-index: The CPU to use for commands that require an implicit CPU # +# Features: +# @savevm-monitor-nodes: If present, HMP command savevm only snapshots +# monitor-owned nodes if they have no parents. +# This allows the use of 'savevm' with +# -blockdev. (since 4.2) +# # Returns: the output of the command as a string # # Since: 0.14.0 @@ -1047,7 +1053,8 @@ ## { 'command': 'human-monitor-command', 'data': {'command-line': 'str', '*cpu-index': 'int'}, - 'returns': 'str' } + 'returns': 'str', + 'features': [ 'savevm-monitor-nodes' ] } ## # @change: diff --git a/scripts/qapi-gen.py b/scripts/qapi-gen.py index 3d98ca2e0c..f93f3c7c23 100755 --- a/scripts/qapi-gen.py +++ b/scripts/qapi-gen.py @@ -5,16 +5,18 @@ # See the COPYING file in the top-level directory. from __future__ import print_function + import argparse import re import sys -from qapi.common import QAPIError, QAPISchema -from qapi.types import gen_types -from qapi.visit import gen_visit + from qapi.commands import gen_commands +from qapi.doc import gen_doc from qapi.events import gen_events from qapi.introspect import gen_introspect -from qapi.doc import gen_doc +from qapi.schema import QAPIError, QAPISchema +from qapi.types import gen_types +from qapi.visit import gen_visit def main(argv): diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 7e3dd1068a..ab98e504f3 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -14,6 +14,7 @@ See the COPYING file in the top-level directory. """ from qapi.common import * +from qapi.gen import QAPIGenCCode, QAPISchemaModularCVisitor, ifcontext def gen_command_decl(name, arg_type, boxed, ret_type): @@ -276,7 +277,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); genc.add(gen_registry(self._regy.get_content(), self._prefix)) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig): + success_response, boxed, allow_oob, allow_preconfig, + features): if not gen: return # FIXME: If T is a user-defined type, the user is responsible diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index d6e00c80ea..e00dcafce7 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -11,2053 +11,8 @@ # 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 -from contextlib import contextmanager -import copy -import errno -import os import re import string -import sys -from collections import OrderedDict - -# 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 = [] - - -# -# Parsing the schema into expressions -# - -class QAPISourceInfo(object): - def __init__(self, fname, line, parent): - self.fname = fname - self.line = line - self.parent = parent - self.defn_meta = None - self.defn_name = None - - def set_defn(self, meta, name): - self.defn_meta = meta - self.defn_name = name - - def next_line(self): - info = copy.copy(self) - info.line += 1 - return info - - def loc(self): - if self.fname is None: - return sys.argv[0] - ret = self.fname - if self.line is not None: - ret += ':%d' % self.line - return ret - - def in_defn(self): - if self.defn_name: - return "%s: In %s '%s':\n" % (self.fname, - self.defn_meta, self.defn_name) - return '' - - def include_path(self): - ret = '' - parent = self.parent - while parent: - ret = 'In file included from %s:\n' % parent.loc() + ret - parent = parent.parent - return ret - - def __str__(self): - return self.include_path() + self.in_defn() + self.loc() - - -class QAPIError(Exception): - def __init__(self, info, col, msg): - Exception.__init__(self) - self.info = info - self.col = col - self.msg = msg - - def __str__(self): - loc = str(self.info) - if self.col is not None: - assert self.info.line is not None - loc += ':%s' % self.col - return 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.info, col, msg) - - -class QAPISemError(QAPIError): - def __init__(self, info, msg): - QAPIError.__init__(self, info, None, msg) - - -class QAPIDoc(object): - """ - A documentation comment block, either definition or free-form - - Definition documentation blocks consist of - - * a body section: one line naming the definition, followed by an - overview (any number of lines) - - * argument sections: a description of each argument (for commands - and events) or member (for structs, unions and alternates) - - * features sections: a description of each feature flag - - * additional (non-argument) sections, possibly tagged - - Free-form documentation blocks consist only of a body section. - """ - - 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() - self.features = OrderedDict() - # a list of Section - self.sections = [] - # the current section - self._section = self.body - self._append_line = self._append_body_line - - 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. - - The way that the line is dealt with depends on which part of - the documentation we're parsing right now: - * The body section: ._append_line is ._append_body_line - * An argument section: ._append_line is ._append_args_line - * A features section: ._append_line is ._append_features_line - * An additional section: ._append_line is ._append_various_line - """ - line = line[1:] - if not line: - self._append_freeform(line) - return - - if line[0] != ' ': - raise QAPIParseError(self._parser, "missing space after #") - line = line[1:] - self._append_line(line) - - def end_comment(self): - self._end_section() - - @staticmethod - def _is_section_tag(name): - return name in ('Returns:', 'Since:', - # those are often singular or plural - 'Note:', 'Notes:', - 'Example:', 'Examples:', - 'TODO:') - - def _append_body_line(self, line): - """ - Process a line of documentation text in the body section. - - If this a symbol line and it is the section's first line, this - is a definition documentation block for that symbol. - - If it's a definition documentation block, another symbol line - begins the argument section for the argument named by it, and - a section tag begins an additional section. Start that - section and append the line to it. - - Else, append the line to the current section. - """ - name = line.split(' ', 1)[0] - # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't - # recognized, and get silently treated as ordinary text - if not self.symbol and 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") - elif self.symbol: - # This is a definition documentation block - if name.startswith('@') and name.endswith(':'): - self._append_line = self._append_args_line - self._append_args_line(line) - elif line == 'Features:': - self._append_line = self._append_features_line - elif self._is_section_tag(name): - self._append_line = self._append_various_line - self._append_various_line(line) - else: - self._append_freeform(line.strip()) - else: - # This is a free-form documentation block - self._append_freeform(line.strip()) - - def _append_args_line(self, line): - """ - Process a line of documentation text in an argument section. - - A symbol line begins the next argument section, a section tag - section or a non-indented line after a blank line begins an - additional section. Start that section and append the line to - it. - - Else, append the line to the current section. - - """ - name = line.split(' ', 1)[0] - - if name.startswith('@') and name.endswith(':'): - line = line[len(name)+1:] - self._start_args_section(name[1:-1]) - elif self._is_section_tag(name): - self._append_line = self._append_various_line - self._append_various_line(line) - return - elif (self._section.text.endswith('\n\n') - and line and not line[0].isspace()): - if line == 'Features:': - self._append_line = self._append_features_line - else: - self._start_section() - self._append_line = self._append_various_line - self._append_various_line(line) - return - - self._append_freeform(line.strip()) - - def _append_features_line(self, line): - name = line.split(' ', 1)[0] - - if name.startswith('@') and name.endswith(':'): - line = line[len(name)+1:] - self._start_features_section(name[1:-1]) - elif self._is_section_tag(name): - self._append_line = self._append_various_line - self._append_various_line(line) - return - elif (self._section.text.endswith('\n\n') - and line and not line[0].isspace()): - self._start_section() - self._append_line = self._append_various_line - self._append_various_line(line) - return - - self._append_freeform(line.strip()) - - def _append_various_line(self, line): - """ - Process a line of documentation text in an additional section. - - A symbol line is an error. - - A section tag begins an additional section. Start that - section and append the line to it. - - Else, append the line to the current section. - """ - name = line.split(' ', 1)[0] - - if name.startswith('@') and name.endswith(':'): - raise QAPIParseError(self._parser, - "'%s' can't follow '%s' section" - % (name, self.sections[0].name)) - elif self._is_section_tag(name): - line = line[len(name)+1:] - self._start_section(name[:-1]) - - if (not self._section.name or - not self._section.name.startswith('Example')): - line = line.strip() - - self._append_freeform(line) - - def _start_symbol_section(self, symbols_dict, 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 symbols_dict: - raise QAPIParseError(self._parser, - "'%s' parameter name duplicated" % name) - assert not self.sections - self._end_section() - self._section = QAPIDoc.ArgSection(name) - symbols_dict[name] = self._section - - def _start_args_section(self, name): - self._start_symbol_section(self.args, name) - - def _start_features_section(self, name): - self._start_symbol_section(self.features, name) - - 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): - 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, fname, previously_included=[], incl_info=None): - previously_included.append(os.path.abspath(fname)) - - try: - if sys.version_info[0] >= 3: - fp = open(fname, 'r', encoding='utf-8') - else: - fp = open(fname, 'r') - self.src = fp.read() - except IOError as e: - raise QAPISemError(incl_info or QAPISourceInfo(None, None, None), - "can't read %s file '%s': %s" - % ("include" if incl_info else "schema", - fname, - e.strerror)) - - if self.src == '' or self.src[-1] != '\n': - self.src += '\n' - self.cursor = 0 - self.info = QAPISourceInfo(fname, 1, incl_info) - self.line_pos = 0 - self.exprs = [] - self.docs = [] - self.accept() - cur_doc = None - - while self.tok is not None: - info = self.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") - incl_fname = os.path.join(os.path.dirname(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) - self.docs.extend(exprs_include.docs) - 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 an object") - 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, "definition 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, incl_fname, previously_included): - incl_abs_fname = os.path.abspath(incl_fname) - # catch inclusion cycle - inf = info - while inf: - if incl_abs_fname == os.path.abspath(inf.fname): - 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 None - - return QAPISchemaParser(incl_fname, previously_included, info) - - 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 == "'": - # Note: we accept only printable ASCII - string = '' - esc = False - while True: - ch = self.src[self.cursor] - self.cursor += 1 - if ch == '\n': - raise QAPIParseError(self, "missing terminating \"'\"") - if esc: - # Note: we recognize only \\ because we have - # no use for funny characters in strings - if ch != '\\': - raise QAPIParseError(self, - "unknown escape \\%s" % ch) - esc = False - elif ch == '\\': - esc = True - continue - elif ch == "'": - self.val = string - return - if ord(ch) < 32 or ord(ch) >= 127: - raise QAPIParseError( - self, "funny character in string") - 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.tok == '\n': - if self.cursor == len(self.src): - self.tok = None - return - self.info = self.info.next_line() - self.line_pos = self.cursor - elif not self.tok.isspace(): - # Show up to next structural, whitespace or quote - # character - match = re.match('[^[\\]{}:,\\s\'"]+', - self.src[self.cursor-1:]) - raise QAPIParseError(self, "stray '%s'" % match.group(0)) - - 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 '##'") - - -# -# Check (context-free) schema expression structure -# - -# 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_is_str(name, info, source): - if not isinstance(name, str): - raise QAPISemError(info, "%s requires a string name" % source) - - -def check_name_str(name, info, source, - allow_optional=False, enum_member=False, - permit_upper=False): - global valid_name - membername = name - - if allow_optional and name.startswith('*'): - membername = name[1:] - # 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 has an invalid name" % source) - if not permit_upper and name.lower() != name: - raise QAPISemError( - info, "%s uses uppercase in name" % source) - assert not membername.startswith('*') - - -def check_defn_name_str(name, info, meta): - check_name_str(name, info, meta, permit_upper=True) - if name.endswith('Kind') or name.endswith('List'): - raise QAPISemError( - info, "%s name should not end in '%s'" % (meta, name[-4:])) - - -def check_if(expr, info, source): - - def check_if_str(ifcond, info): - if not isinstance(ifcond, str): - raise QAPISemError( - info, - "'if' condition of %s must be a string or a list of strings" - % source) - if ifcond.strip() == '': - raise QAPISemError( - info, - "'if' condition '%s' of %s makes no sense" - % (ifcond, source)) - - ifcond = expr.get('if') - if ifcond is None: - return - if isinstance(ifcond, list): - if ifcond == []: - raise QAPISemError( - info, "'if' condition [] of %s is useless" % source) - for elt in ifcond: - check_if_str(elt, info) - else: - check_if_str(ifcond, info) - - -def check_type(value, info, source, - allow_array=False, allow_dict=False): - if value is None: - return - - # Array type - 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) - return - - # Type name - if isinstance(value, str): - return - - # Anonymous type - - 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 an object or type name" % source) - - permit_upper = allow_dict in name_case_whitelist - - # value is a dictionary, check that each member is okay - for (key, arg) in value.items(): - key_source = "%s member '%s'" % (source, key) - check_name_str(key, info, key_source, - allow_optional=True, permit_upper=permit_upper) - if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): - raise QAPISemError(info, "%s uses reserved name" % key_source) - check_keys(arg, info, key_source, ['type'], ['if']) - check_if(arg, info, key_source) - normalize_if(arg) - check_type(arg['type'], info, key_source, allow_array=True) - - -def check_command(expr, info): - args = expr.get('data') - rets = expr.get('returns') - boxed = expr.get('boxed', False) - - if boxed and args is None: - raise QAPISemError(info, "'boxed': true requires 'data'") - check_type(args, info, "'data'", allow_dict=not boxed) - check_type(rets, info, "'returns'", allow_array=True) - - -def check_event(expr, info): - args = expr.get('data') - boxed = expr.get('boxed', False) - - if boxed and args is None: - raise QAPISemError(info, "'boxed': true requires 'data'") - check_type(args, info, "'data'", allow_dict=not boxed) - - -def check_union(expr, info): - name = expr['union'] - base = expr.get('base') - discriminator = expr.get('discriminator') - members = expr['data'] - - if discriminator is None: # simple union - if base is not None: - raise QAPISemError(info, "'base' requires 'discriminator'") - else: # flat union - check_type(base, info, "'base'", allow_dict=name) - if not base: - raise QAPISemError(info, "'discriminator' requires 'base'") - check_name_is_str(discriminator, info, "'discriminator'") - - for (key, value) in members.items(): - source = "'data' member '%s'" % key - check_name_str(key, info, source) - check_keys(value, info, source, ['type'], ['if']) - check_if(value, info, source) - normalize_if(value) - check_type(value['type'], info, source, allow_array=not base) - - -def check_alternate(expr, info): - members = expr['data'] - - if len(members) == 0: - raise QAPISemError(info, "'data' must not be empty") - for (key, value) in members.items(): - source = "'data' member '%s'" % key - check_name_str(key, info, source) - check_keys(value, info, source, ['type'], ['if']) - check_if(value, info, source) - normalize_if(value) - check_type(value['type'], info, source) - - -def check_enum(expr, info): - name = expr['enum'] - members = expr['data'] - prefix = expr.get('prefix') - - if not isinstance(members, list): - raise QAPISemError(info, "'data' must be an array") - if prefix is not None and not isinstance(prefix, str): - raise QAPISemError(info, "'prefix' must be a string") - - permit_upper = name in name_case_whitelist - - for member in members: - source = "'data' member" - check_keys(member, info, source, ['name'], ['if']) - check_name_is_str(member['name'], info, source) - source = "%s '%s'" % (source, member['name']) - check_name_str(member['name'], info, source, - enum_member=True, permit_upper=permit_upper) - check_if(member, info, source) - normalize_if(member) - - -def check_struct(expr, info): - name = expr['struct'] - members = expr['data'] - features = expr.get('features') - - check_type(members, info, "'data'", allow_dict=name) - check_type(expr.get('base'), info, "'base'") - - if features: - if not isinstance(features, list): - raise QAPISemError(info, "'features' must be an array") - for f in features: - source = "'features' member" - assert isinstance(f, dict) - check_keys(f, info, source, ['name'], ['if']) - check_name_is_str(f['name'], info, source) - source = "%s '%s'" % (source, f['name']) - check_name_str(f['name'], info, source) - check_if(f, info, source) - normalize_if(f) - - -def check_keys(value, info, source, required, optional): - - def pprint(elems): - return ', '.join("'" + e + "'" for e in sorted(elems)) - - missing = set(required) - set(value) - if missing: - raise QAPISemError( - info, - "%s misses key%s %s" - % (source, 's' if len(missing) > 1 else '', - pprint(missing))) - allowed = set(required + optional) - unknown = set(value) - allowed - if unknown: - raise QAPISemError( - info, - "%s has unknown key%s %s\nValid keys are %s." - % (source, 's' if len(unknown) > 1 else '', - pprint(unknown), pprint(allowed))) - - -def check_flags(expr, info): - for key in ['gen', 'success-response']: - if key in expr and expr[key] is not False: - raise QAPISemError( - info, "flag '%s' may only use false value" % key) - for key in ['boxed', 'allow-oob', 'allow-preconfig']: - if key in expr and expr[key] is not True: - raise QAPISemError( - info, "flag '%s' may only use true value" % key) - - -def normalize_enum(expr): - if isinstance(expr['data'], list): - expr['data'] = [m if isinstance(m, dict) else {'name': m} - for m in expr['data']] - - -def normalize_members(members): - if isinstance(members, OrderedDict): - for key, arg in members.items(): - if isinstance(arg, dict): - continue - members[key] = {'type': arg} - - -def normalize_features(features): - if isinstance(features, list): - features[:] = [f if isinstance(f, dict) else {'name': f} - for f in features] - - -def normalize_if(expr): - ifcond = expr.get('if') - if isinstance(ifcond, str): - expr['if'] = [ifcond] - - -def check_exprs(exprs): - for expr_elem in exprs: - expr = expr_elem['expr'] - info = expr_elem['info'] - doc = expr_elem.get('doc') - - if 'include' in expr: - continue - - if 'enum' in expr: - meta = 'enum' - elif 'union' in expr: - meta = 'union' - elif 'alternate' in expr: - meta = 'alternate' - elif 'struct' in expr: - meta = 'struct' - elif 'command' in expr: - meta = 'command' - elif 'event' in expr: - meta = 'event' - else: - raise QAPISemError(info, "expression is missing metatype") - - name = expr[meta] - check_name_is_str(name, info, "'%s'" % meta) - info.set_defn(meta, name) - check_defn_name_str(name, info, meta) - - if doc: - if doc.symbol != name: - raise QAPISemError( - info, "documentation comment is for '%s'" % doc.symbol) - doc.check_expr(expr) - elif doc_required: - raise QAPISemError(info, - "documentation comment required") - - if meta == 'enum': - check_keys(expr, info, meta, - ['enum', 'data'], ['if', 'prefix']) - normalize_enum(expr) - check_enum(expr, info) - elif meta == 'union': - check_keys(expr, info, meta, - ['union', 'data'], - ['base', 'discriminator', 'if']) - normalize_members(expr.get('base')) - normalize_members(expr['data']) - check_union(expr, info) - elif meta == 'alternate': - check_keys(expr, info, meta, - ['alternate', 'data'], ['if']) - normalize_members(expr['data']) - check_alternate(expr, info) - elif meta == 'struct': - check_keys(expr, info, meta, - ['struct', 'data'], ['base', 'if', 'features']) - normalize_members(expr['data']) - normalize_features(expr.get('features')) - check_struct(expr, info) - elif meta == 'command': - check_keys(expr, info, meta, - ['command'], - ['data', 'returns', 'boxed', 'if', - 'gen', 'success-response', 'allow-oob', - 'allow-preconfig']) - normalize_members(expr.get('data')) - check_command(expr, info) - elif meta == 'event': - check_keys(expr, info, meta, - ['event'], ['data', 'boxed', 'if']) - normalize_members(expr.get('data')) - check_event(expr, info) - else: - assert False, 'unexpected meta type' - - normalize_if(expr) - check_if(expr, info, meta) - check_flags(expr, info) - - return exprs - - -# -# Schema compiler frontend -# TODO catching name collisions in generated code would be nice -# - -class QAPISchemaEntity(object): - meta = None - - def __init__(self, name, info, doc, ifcond=None): - 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 - # triggered the implicit definition (there may be more than one - # such place). - self.info = info - self.doc = doc - self._ifcond = ifcond or [] - self._checked = False - - def c_name(self): - return c_name(self.name) - - def check(self, schema): - assert not self._checked - if self.info: - self._module = os.path.relpath(self.info.fname, - os.path.dirname(schema.fname)) - self._checked = True - - @property - def ifcond(self): - assert self._checked - return self._ifcond - - @property - def module(self): - assert self._checked - return self._module - - def is_implicit(self): - return not self.info - - def visit(self, visitor): - assert self._checked - - def describe(self): - assert self.meta - return "%s '%s'" % (self.meta, self.name) - - -class QAPISchemaVisitor(object): - def visit_begin(self, schema): - pass - - 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 - - def visit_enum_type(self, name, info, ifcond, members, prefix): - pass - - def visit_array_type(self, name, info, ifcond, element_type): - pass - - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): - pass - - def visit_object_type_flat(self, name, info, ifcond, members, variants, - features): - pass - - def visit_alternate_type(self, name, info, ifcond, variants): - pass - - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig): - pass - - def visit_event(self, name, info, ifcond, arg_type, boxed): - pass - - -class QAPISchemaInclude(QAPISchemaEntity): - - def __init__(self, fname, info): - QAPISchemaEntity.__init__(self, None, info, None) - self.fname = fname - - def visit(self, visitor): - QAPISchemaEntity.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. - 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 - - def describe(self): - assert self.meta - return "%s type '%s'" % (self.meta, self.name) - - -class QAPISchemaBuiltinType(QAPISchemaType): - meta = 'built-in' - - 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): - QAPISchemaType.visit(self, visitor) - visitor.visit_builtin_type(self.name, self.info, self.json_type()) - - -class QAPISchemaEnumType(QAPISchemaType): - meta = 'enum' - - def __init__(self, name, info, doc, ifcond, members, prefix): - QAPISchemaType.__init__(self, name, info, doc, ifcond) - for m in members: - assert isinstance(m, QAPISchemaEnumMember) - m.set_defined_in(name) - assert prefix is None or isinstance(prefix, str) - self.members = members - self.prefix = prefix - - def check(self, schema): - QAPISchemaType.check(self, schema) - seen = {} - for m in self.members: - m.check_clash(self.info, seen) - if self.doc: - self.doc.connect_member(m) - - 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 [m.name for m in self.members] - - def json_type(self): - return 'string' - - def visit(self, visitor): - QAPISchemaType.visit(self, visitor) - visitor.visit_enum_type(self.name, self.info, self.ifcond, - self.members, self.prefix) - - -class QAPISchemaArrayType(QAPISchemaType): - meta = 'array' - - def __init__(self, name, info, element_type): - QAPISchemaType.__init__(self, name, info, None, None) - assert isinstance(element_type, str) - self._element_type_name = element_type - self.element_type = None - - def check(self, schema): - QAPISchemaType.check(self, schema) - self.element_type = schema.resolve_type( - self._element_type_name, self.info, - self.info and self.info.defn_meta) - assert not isinstance(self.element_type, QAPISchemaArrayType) - - @property - def ifcond(self): - assert self._checked - return self.element_type.ifcond - - @property - def module(self): - assert self._checked - return self.element_type.module - - 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): - QAPISchemaType.visit(self, visitor) - visitor.visit_array_type(self.name, self.info, self.ifcond, - self.element_type) - - def describe(self): - assert self.meta - return "%s type ['%s']" % (self.meta, self._element_type_name) - - -class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, ifcond, - base, local_members, variants, features): - # 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, ifcond) - self.meta = 'union' if variants else 'struct' - assert base is None or isinstance(base, str) - for m in local_members: - assert isinstance(m, QAPISchemaObjectTypeMember) - m.set_defined_in(name) - if variants is not None: - assert isinstance(variants, QAPISchemaObjectTypeVariants) - variants.set_defined_in(name) - for f in features: - assert isinstance(f, QAPISchemaFeature) - f.set_defined_in(name) - self._base_name = base - self.base = None - self.local_members = local_members - self.variants = variants - self.members = None - self.features = features - - def check(self, schema): - # This calls another type T's .check() exactly when the C - # struct emitted by gen_object() contains that T's C struct - # (pointers don't count). - if self.members is not None: - # A previous .check() completed: nothing to do - return - if self._checked: - # Recursed: C struct contains itself - raise QAPISemError(self.info, - "object %s contains itself" % self.name) - - QAPISchemaType.check(self, schema) - assert self._checked and self.members is None - - seen = OrderedDict() - if self._base_name: - self.base = schema.resolve_type(self._base_name, self.info, - "'base'") - if (not isinstance(self.base, QAPISchemaObjectType) - or self.base.variants): - raise QAPISemError( - self.info, - "'base' requires a struct type, %s isn't" - % self.base.describe()) - 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) - members = seen.values() - - if self.variants: - self.variants.check(schema, seen) - self.variants.check_clash(self.info, seen) - - # Features are in a name space separate from members - seen = {} - for f in self.features: - f.check_clash(self.info, seen) - - if self.doc: - self.doc.check() - - self.members = members # mark completed - - # 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 self._checked - assert not self.variants # not implemented - for m in self.members: - m.check_clash(info, seen) - - @property - def ifcond(self): - assert self._checked - if isinstance(self._ifcond, QAPISchemaType): - # Simple union wrapper type inherits from wrapped type; - # see _make_implicit_object_type() - return self._ifcond.ifcond - return self._ifcond - - 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): - QAPISchemaType.visit(self, visitor) - visitor.visit_object_type(self.name, self.info, self.ifcond, - self.base, self.local_members, self.variants, - self.features) - visitor.visit_object_type_flat(self.name, self.info, self.ifcond, - self.members, self.variants, - self.features) - - -class QAPISchemaMember(object): - """ Represents object members, enum members and features """ - role = 'member' - - def __init__(self, name, info, ifcond=None): - assert isinstance(name, str) - self.name = name - self.info = info - self.ifcond = ifcond or [] - self.defined_in = None - - def set_defined_in(self, name): - assert not self.defined_in - self.defined_in = name - - def check_clash(self, info, seen): - cname = c_name(self.name) - if cname in seen: - raise QAPISemError( - info, - "%s collides with %s" - % (self.describe(info), seen[cname].describe(info))) - seen[cname] = self - - def describe(self, info): - role = self.role - defined_in = self.defined_in - assert defined_in - - if defined_in.startswith('q_obj_'): - # See QAPISchema._make_implicit_object_type() - reverse the - # mapping there to create a nice human-readable description - defined_in = defined_in[6:] - if defined_in.endswith('-arg'): - # Implicit type created for a command's dict 'data' - assert role == 'member' - role = 'parameter' - elif defined_in.endswith('-base'): - # Implicit type created for a flat union's dict 'base' - role = 'base ' + role - else: - # Implicit type created for a simple union's branch - assert defined_in.endswith('-wrapper') - # Unreachable and not implemented - assert False - elif defined_in.endswith('Kind'): - # See QAPISchema._make_implicit_enum_type() - # Implicit enum created for simple union's branches - assert role == 'value' - role = 'branch' - elif defined_in != info.defn_name: - return "%s '%s' of type '%s'" % (role, self.name, defined_in) - return "%s '%s'" % (role, self.name) - - -class QAPISchemaEnumMember(QAPISchemaMember): - role = 'value' - - -class QAPISchemaFeature(QAPISchemaMember): - role = 'feature' - - -class QAPISchemaObjectTypeMember(QAPISchemaMember): - def __init__(self, name, info, typ, optional, ifcond=None): - QAPISchemaMember.__init__(self, name, info, ifcond) - assert isinstance(typ, str) - assert isinstance(optional, bool) - self._type_name = typ - self.type = None - self.optional = optional - - def check(self, schema): - assert self.defined_in - self.type = schema.resolve_type(self._type_name, self.info, - self.describe) - - -class QAPISchemaObjectTypeVariants(object): - def __init__(self, tag_name, info, 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)) - for v in variants: - assert isinstance(v, QAPISchemaObjectTypeVariant) - self._tag_name = tag_name - self.info = info - self.tag_member = tag_member - self.variants = variants - - def set_defined_in(self, name): - for v in self.variants: - v.set_defined_in(name) - - def check(self, schema, seen): - if not self.tag_member: # flat union - self.tag_member = seen.get(c_name(self._tag_name)) - base = "'base'" - # Pointing to the base type when not implicit would be - # nice, but we don't know it here - if not self.tag_member or self._tag_name != self.tag_member.name: - raise QAPISemError( - self.info, - "discriminator '%s' is not a member of %s" - % (self._tag_name, base)) - # Here we do: - base_type = schema.lookup_type(self.tag_member.defined_in) - assert base_type - if not base_type.is_implicit(): - base = "base type '%s'" % self.tag_member.defined_in - if not isinstance(self.tag_member.type, QAPISchemaEnumType): - raise QAPISemError( - self.info, - "discriminator member '%s' of %s must be of enum type" - % (self._tag_name, base)) - if self.tag_member.optional: - raise QAPISemError( - self.info, - "discriminator member '%s' of %s must not be optional" - % (self._tag_name, base)) - if self.tag_member.ifcond: - raise QAPISemError( - self.info, - "discriminator member '%s' of %s must not be conditional" - % (self._tag_name, base)) - else: # simple union - assert isinstance(self.tag_member.type, QAPISchemaEnumType) - assert not self.tag_member.optional - assert self.tag_member.ifcond == [] - if self._tag_name: # flat union - # branches that are not explicitly covered get an empty type - cases = set([v.name for v in self.variants]) - for m in self.tag_member.type.members: - if m.name not in cases: - v = QAPISchemaObjectTypeVariant(m.name, self.info, - 'q_empty', m.ifcond) - v.set_defined_in(self.tag_member.defined_in) - self.variants.append(v) - if not self.variants: - raise QAPISemError(self.info, "union has no branches") - 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: - if v.name not in self.tag_member.type.member_names(): - raise QAPISemError( - self.info, - "branch '%s' is not a value of %s" - % (v.name, self.tag_member.type.describe())) - if (not isinstance(v.type, QAPISchemaObjectType) - or v.type.variants): - raise QAPISemError( - self.info, - "%s cannot use %s" - % (v.describe(self.info), v.type.describe())) - 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 - v.type.check_clash(info, dict(seen)) - - -class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): - role = 'branch' - - def __init__(self, name, info, typ, ifcond=None): - QAPISchemaObjectTypeMember.__init__(self, name, info, typ, - False, ifcond) - - -class QAPISchemaAlternateType(QAPISchemaType): - meta = 'alternate' - - def __init__(self, name, info, doc, ifcond, variants): - QAPISchemaType.__init__(self, name, info, doc, ifcond) - assert isinstance(variants, QAPISchemaObjectTypeVariants) - assert variants.tag_member - variants.set_defined_in(name) - variants.tag_member.set_defined_in(self.name) - self.variants = variants - - def check(self, schema): - QAPISchemaType.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 = {} - types_seen = {} - for v in self.variants.variants: - v.check_clash(self.info, seen) - qtype = v.type.alternate_qtype() - if not qtype: - raise QAPISemError( - self.info, - "%s cannot use %s" - % (v.describe(self.info), v.type.describe())) - conflicting = set([qtype]) - if qtype == 'QTYPE_QSTRING': - if isinstance(v.type, QAPISchemaEnumType): - for m in v.type.members: - if m.name in ['on', 'off']: - conflicting.add('QTYPE_QBOOL') - if re.match(r'[-+0-9.]', m.name): - # 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( - self.info, - "%s can't be distinguished from '%s'" - % (v.describe(self.info), types_seen[qt])) - types_seen[qt] = v.name - 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): - QAPISchemaType.visit(self, visitor) - visitor.visit_alternate_type(self.name, self.info, self.ifcond, - self.variants) - - -class QAPISchemaCommand(QAPISchemaEntity): - meta = 'command' - - def __init__(self, name, info, doc, ifcond, arg_type, ret_type, - gen, success_response, boxed, allow_oob, allow_preconfig): - QAPISchemaEntity.__init__(self, name, info, doc, ifcond) - 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 - self.allow_oob = allow_oob - self.allow_preconfig = allow_preconfig - - def check(self, schema): - QAPISchemaEntity.check(self, schema) - if self._arg_type_name: - self.arg_type = schema.resolve_type( - self._arg_type_name, self.info, "command's 'data'") - if not isinstance(self.arg_type, QAPISchemaObjectType): - raise QAPISemError( - self.info, - "command's 'data' cannot take %s" - % self.arg_type.describe()) - if self.arg_type.variants and not self.boxed: - raise QAPISemError( - self.info, - "command's 'data' can take %s only with 'boxed': true" - % self.arg_type.describe()) - if self._ret_type_name: - self.ret_type = schema.resolve_type( - self._ret_type_name, self.info, "command's 'returns'") - if self.name not in returns_whitelist: - if not (isinstance(self.ret_type, QAPISchemaObjectType) - or (isinstance(self.ret_type, QAPISchemaArrayType) - and isinstance(self.ret_type.element_type, - QAPISchemaObjectType))): - raise QAPISemError( - self.info, - "command's 'returns' cannot take %s" - % self.ret_type.describe()) - - def visit(self, visitor): - QAPISchemaEntity.visit(self, visitor) - visitor.visit_command(self.name, self.info, self.ifcond, - self.arg_type, self.ret_type, - self.gen, self.success_response, - self.boxed, self.allow_oob, - self.allow_preconfig) - - -class QAPISchemaEvent(QAPISchemaEntity): - meta = 'event' - - def __init__(self, name, info, doc, ifcond, arg_type, boxed): - QAPISchemaEntity.__init__(self, name, info, doc, ifcond) - 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): - QAPISchemaEntity.check(self, schema) - if self._arg_type_name: - self.arg_type = schema.resolve_type( - self._arg_type_name, self.info, "event's 'data'") - if not isinstance(self.arg_type, QAPISchemaObjectType): - raise QAPISemError( - self.info, - "event's 'data' cannot take %s" - % self.arg_type.describe()) - if self.arg_type.variants and not self.boxed: - raise QAPISemError( - self.info, - "event's 'data' can take %s only with 'boxed': true" - % self.arg_type.describe()) - - def visit(self, visitor): - QAPISchemaEntity.visit(self, visitor) - visitor.visit_event(self.name, self.info, self.ifcond, - self.arg_type, self.boxed) - - -class QAPISchema(object): - def __init__(self, fname): - self.fname = fname - parser = QAPISchemaParser(fname) - exprs = check_exprs(parser.exprs) - self.docs = parser.docs - self._entity_list = [] - 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 - assert ent.info or self._predefining - self._entity_list.append(ent) - if ent.name is None: - return - # TODO reject names that differ only in '_' vs. '.' vs. '-', - # because they're liable to clash in generated C. - other_ent = self._entity_dict.get(ent.name) - if other_ent: - if other_ent.info: - where = QAPIError(other_ent.info, None, "previous definition") - raise QAPISemError( - ent.info, - "'%s' is already defined\n%s" % (ent.name, where)) - raise QAPISemError( - ent.info, "%s is already defined" % other_ent.describe()) - 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 resolve_type(self, name, info, what): - typ = self.lookup_type(name) - if not typ: - if callable(what): - what = what(info) - raise QAPISemError( - info, "%s uses unknown type '%s'" % (what, name)) - return typ - - 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.fname)) - 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)) - # 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): - 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, [], None, []) - self._def_entity(self.the_empty_object_type) - - qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', - 'qbool'] - qtype_values = self._make_enum_members( - [{'name': n} for n in qtypes], None) - - self._def_entity(QAPISchemaEnumType('QType', None, None, None, - qtype_values, 'QTYPE')) - - def _make_features(self, features, info): - return [QAPISchemaFeature(f['name'], info, f.get('if')) - for f in features] - - def _make_enum_members(self, values, info): - return [QAPISchemaEnumMember(v['name'], info, v.get('if')) - for v in values] - - def _make_implicit_enum_type(self, name, info, ifcond, values): - # See also QAPISchemaObjectTypeMember.describe() - name = name + 'Kind' # reserved by check_defn_name_str() - self._def_entity(QAPISchemaEnumType( - name, info, None, ifcond, self._make_enum_members(values, info), - None)) - return name - - def _make_array_type(self, element_type, info): - name = element_type + 'List' # reserved by check_defn_name_str() - 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, ifcond, - role, members): - if not members: - return None - # See also QAPISchemaObjectTypeMember.describe() - name = 'q_obj_%s-%s' % (name, role) - typ = self.lookup_entity(name, QAPISchemaObjectType) - if typ: - # The implicit object type has multiple users. This can - # happen only for simple unions' implicit wrapper types. - # Its ifcond should be the disjunction of its user's - # ifconds. Not implemented. Instead, we always pass the - # wrapped type's ifcond, which is trivially the same for all - # users. It's also necessary for the wrapper to compile. - # But it's not tight: the disjunction need not imply it. We - # may end up compiling useless wrapper types. - # TODO kill simple unions or implement the disjunction - assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access - else: - self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, - None, members, None, [])) - return name - - def _def_enum_type(self, expr, info, doc): - name = expr['enum'] - data = expr['data'] - prefix = expr.get('prefix') - ifcond = expr.get('if') - self._def_entity(QAPISchemaEnumType( - name, info, doc, ifcond, - self._make_enum_members(data, info), prefix)) - - def _make_member(self, name, typ, ifcond, 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, info, typ, optional, ifcond) - - def _make_members(self, data, info): - return [self._make_member(key, value['type'], value.get('if'), 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'] - ifcond = expr.get('if') - features = expr.get('features', []) - self._def_entity(QAPISchemaObjectType( - name, info, doc, ifcond, base, - self._make_members(data, info), - None, - self._make_features(features, info))) - - def _make_variant(self, case, typ, ifcond, info): - return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) - - def _make_simple_variant(self, case, typ, ifcond, 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, self.lookup_type(typ), - 'wrapper', [self._make_member('data', typ, None, info)]) - return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) - - def _def_union_type(self, expr, info, doc): - name = expr['union'] - data = expr['data'] - base = expr.get('base') - ifcond = expr.get('if') - tag_name = expr.get('discriminator') - tag_member = None - if isinstance(base, dict): - base = self._make_implicit_object_type( - name, info, doc, ifcond, - 'base', self._make_members(base, info)) - if tag_name: - variants = [self._make_variant(key, value['type'], - value.get('if'), info) - for (key, value) in data.items()] - members = [] - else: - variants = [self._make_simple_variant(key, value['type'], - value.get('if'), info) - for (key, value) in data.items()] - enum = [{'name': v.name, 'if': v.ifcond} for v in variants] - typ = self._make_implicit_enum_type(name, info, ifcond, enum) - tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) - members = [tag_member] - self._def_entity( - QAPISchemaObjectType(name, info, doc, ifcond, base, members, - QAPISchemaObjectTypeVariants( - tag_name, info, tag_member, variants), - [])) - - def _def_alternate_type(self, expr, info, doc): - name = expr['alternate'] - data = expr['data'] - ifcond = expr.get('if') - variants = [self._make_variant(key, value['type'], value.get('if'), - info) - for (key, value) in data.items()] - tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) - self._def_entity( - QAPISchemaAlternateType(name, info, doc, ifcond, - QAPISchemaObjectTypeVariants( - None, info, 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) - allow_oob = expr.get('allow-oob', False) - allow_preconfig = expr.get('allow-preconfig', False) - ifcond = expr.get('if') - if isinstance(data, OrderedDict): - data = self._make_implicit_object_type( - name, info, doc, ifcond, '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, ifcond, data, rets, - gen, success_response, - boxed, allow_oob, allow_preconfig)) - - def _def_event(self, expr, info, doc): - name = expr['event'] - data = expr.get('data') - boxed = expr.get('boxed', False) - ifcond = expr.get('if') - if isinstance(data, OrderedDict): - data = self._make_implicit_object_type( - name, info, doc, ifcond, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) - - def _def_exprs(self, exprs): - for expr_elem in 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) - elif 'include' in expr: - self._def_include(expr, info, doc) - else: - assert False - - def check(self): - for ent in self._entity_list: - ent.check(self) - - def visit(self, visitor): - visitor.visit_begin(self) - module = None - visitor.visit_module(module) - 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() - - -# -# 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 @@ -2220,81 +175,6 @@ def gen_endif(ifcond): return ret -def _wrap_ifcond(ifcond, before, after): - if before == after: - return after # suppress empty #if ... #endif - - assert after.startswith(before) - out = before - added = after[len(before):] - if added[0] == '\n': - out += '\n' - added = added[1:] - out += gen_if(ifcond) - out += added - out += gen_endif(ifcond) - return out - - -def gen_enum_lookup(name, members, prefix=None): - ret = mcgen(''' - -const QEnumLookup %(c_name)s_lookup = { - .array = (const char *const[]) { -''', - c_name=c_name(name)) - for m in members: - ret += gen_if(m.ifcond) - index = c_enum_const(name, m.name, prefix) - ret += mcgen(''' - [%(index)s] = "%(name)s", -''', - index=index, name=m.name) - ret += gen_endif(m.ifcond) - - ret += mcgen(''' - }, - .size = %(max_index)s -}; -''', - max_index=c_enum_const(name, '_MAX', prefix)) - return ret - - -def gen_enum(name, members, prefix=None): - # append automatically generated _MAX value - enum_members = members + [QAPISchemaEnumMember('_MAX', None)] - - ret = mcgen(''' - -typedef enum %(c_name)s { -''', - c_name=c_name(name)) - - for m in enum_members: - ret += gen_if(m.ifcond) - ret += mcgen(''' - %(c_enum)s, -''', - c_enum=c_enum_const(name, m.name, prefix)) - ret += gen_endif(m.ifcond) - - 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=None): ret = '' sep = '' @@ -2314,260 +194,3 @@ def build_params(arg_type, boxed, extra=None): if extra: ret += sep + extra return ret if ret else 'void' - - -# -# Accumulate and write output -# - -class QAPIGen(object): - - def __init__(self, fname): - self.fname = fname - self._preamble = '' - self._body = '' - - def preamble_add(self, text): - self._preamble += text - - def add(self, text): - self._body += text - - def get_content(self): - return self._top() + self._preamble + self._body + self._bottom() - - def _top(self): - return '' - - def _bottom(self): - return '' - - def write(self, output_dir): - pathname = os.path.join(output_dir, self.fname) - dir = os.path.dirname(pathname) - if dir: - try: - os.makedirs(dir) - except os.error as e: - if e.errno != errno.EEXIST: - raise - fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) - if sys.version_info[0] >= 3: - f = open(fd, 'r+', encoding='utf-8') - else: - f = os.fdopen(fd, 'r+') - text = self.get_content() - oldtext = f.read(len(text) + 1) - if text != oldtext: - f.seek(0) - f.truncate(0) - f.write(text) - f.close() - - -@contextmanager -def ifcontext(ifcond, *args): - """A 'with' statement context manager to wrap with start_if()/end_if() - - *args: any number of QAPIGenCCode - - Example:: - - with ifcontext(ifcond, self._genh, self._genc): - modify self._genh and self._genc ... - - Is equivalent to calling:: - - self._genh.start_if(ifcond) - self._genc.start_if(ifcond) - modify self._genh and self._genc ... - self._genh.end_if() - self._genc.end_if() - """ - for arg in args: - arg.start_if(ifcond) - yield - for arg in args: - arg.end_if() - - -class QAPIGenCCode(QAPIGen): - - def __init__(self, fname): - QAPIGen.__init__(self, fname) - self._start_if = None - - def start_if(self, ifcond): - assert self._start_if is None - self._start_if = (ifcond, self._body, self._preamble) - - def end_if(self): - assert self._start_if - self._wrap_ifcond() - self._start_if = None - - def _wrap_ifcond(self): - self._body = _wrap_ifcond(self._start_if[0], - self._start_if[1], self._body) - self._preamble = _wrap_ifcond(self._start_if[0], - self._start_if[2], self._preamble) - - def get_content(self): - assert self._start_if is None - return QAPIGen.get_content(self) - - -class QAPIGenC(QAPIGenCCode): - - def __init__(self, fname, blurb, pydoc): - QAPIGenCCode.__init__(self, fname) - self._blurb = blurb - self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, - re.MULTILINE)) - - def _top(self): - 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) - - def _bottom(self): - return mcgen(''' - -/* Dummy declaration to prevent empty .o file */ -char qapi_dummy_%(name)s; -''', - name=c_fname(self.fname)) - - -class QAPIGenH(QAPIGenC): - - def _top(self): - return QAPIGenC._top(self) + guardstart(self.fname) - - def _bottom(self): - return guardend(self.fname) - - -class QAPIGenDoc(QAPIGen): - - def _top(self): - return (QAPIGen._top(self) - + '@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(self._prefix + self._what + '.c', - blurb, pydoc) - self._genh = QAPIGenH(self._prefix + self._what + '.h', - blurb, pydoc) - - def write(self, output_dir): - self._genc.write(output_dir) - self._genh.write(output_dir) - - -class QAPISchemaModularCVisitor(QAPISchemaVisitor): - - def __init__(self, prefix, what, blurb, pydoc): - self._prefix = prefix - self._what = what - self._blurb = blurb - self._pydoc = pydoc - self._genc = None - self._genh = None - self._module = {} - self._main_module = None - - @staticmethod - def _is_user_module(name): - return name and not name.startswith('./') - - @staticmethod - def _is_builtin_module(name): - return not name - - def _module_dirname(self, what, name): - if self._is_user_module(name): - return os.path.dirname(name) - return '' - - def _module_basename(self, what, name): - ret = '' if self._is_builtin_module(name) else self._prefix - if self._is_user_module(name): - basename = os.path.basename(name) - ret += what - if name != self._main_module: - ret += '-' + os.path.splitext(basename)[0] - else: - name = name[2:] if name else 'builtin' - ret += re.sub(r'-', '-' + name + '-', what) - return ret - - def _module_filename(self, what, name): - return os.path.join(self._module_dirname(what, name), - self._module_basename(what, name)) - - def _add_module(self, name, blurb): - basename = self._module_filename(self._what, name) - genc = QAPIGenC(basename + '.c', blurb, self._pydoc) - genh = QAPIGenH(basename + '.h', blurb, self._pydoc) - self._module[name] = (genc, genh) - self._set_module(name) - - def _add_user_module(self, name, blurb): - assert self._is_user_module(name) - if self._main_module is None: - self._main_module = name - self._add_module(name, blurb) - - def _add_system_module(self, name, blurb): - self._add_module(name and './' + name, blurb) - - def _set_module(self, name): - self._genc, self._genh = self._module[name] - - def write(self, output_dir, opt_builtins=False): - for name in self._module: - if self._is_builtin_module(name) and not opt_builtins: - continue - (genc, genh) = self._module[name] - genc.write(output_dir) - genh.write(output_dir) - - def _begin_user_module(self, name): - pass - - def visit_module(self, name): - if name in self._module: - self._set_module(name) - elif self._is_builtin_module(name): - # The built-in module has not been created. No code may - # be generated. - self._genc = None - self._genh = None - else: - self._add_user_module(name, self._blurb) - self._begin_user_module(name) - - def visit_include(self, name, info): - relname = os.path.relpath(self._module_filename(self._what, name), - os.path.dirname(self._genh.fname)) - self._genh.preamble_add(mcgen(''' -#include "%(relname)s.h" -''', - relname=relname)) diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 5fc0fc7e06..6d5726cf6e 100755..100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # QAPI texi generator # # This work is licensed under the terms of the GNU LGPL, version 2+. @@ -7,7 +6,8 @@ from __future__ import print_function import re -import qapi.common +from qapi.gen import QAPIGenDoc, QAPISchemaVisitor + MSG_FMT = """ @deftypefn {type} {{}} {name} @@ -216,10 +216,10 @@ def texi_entity(doc, what, ifcond, base=None, variants=None, + texi_sections(doc, ifcond)) -class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): +class QAPISchemaGenDocVisitor(QAPISchemaVisitor): def __init__(self, prefix): self._prefix = prefix - self._gen = qapi.common.QAPIGenDoc(self._prefix + 'qapi-doc.texi') + self._gen = QAPIGenDoc(self._prefix + 'qapi-doc.texi') self.cur_doc = None def write(self, output_dir): @@ -249,12 +249,14 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): body=texi_entity(doc, 'Members', ifcond))) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig): + success_response, boxed, allow_oob, allow_preconfig, + features): doc = self.cur_doc if boxed: body = texi_body(doc) body += ('\n@b{Arguments:} the members of @code{%s}\n' % arg_type.name) + body += texi_features(doc) body += texi_sections(doc, ifcond) else: body = texi_entity(doc, 'Arguments', ifcond) @@ -283,8 +285,6 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): 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: diff --git a/scripts/qapi/error.py b/scripts/qapi/error.py new file mode 100644 index 0000000000..b9f3751bea --- /dev/null +++ b/scripts/qapi/error.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# QAPI error classes +# +# Copyright (c) 2017-2019 Red Hat Inc. +# +# Authors: +# Markus Armbruster <armbru@redhat.com> +# Marc-André Lureau <marcandre.lureau@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + + +class QAPIError(Exception): + def __init__(self, info, col, msg): + Exception.__init__(self) + self.info = info + self.col = col + self.msg = msg + + def __str__(self): + loc = str(self.info) + if self.col is not None: + assert self.info.line is not None + loc += ':%s' % self.col + return 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.info, col, msg) + + +class QAPISemError(QAPIError): + def __init__(self, info, msg): + QAPIError.__init__(self, info, None, msg) diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 7308e8e589..10fc509fa9 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -13,6 +13,9 @@ See the COPYING file in the top-level directory. """ from qapi.common import * +from qapi.gen import QAPISchemaModularCVisitor, ifcontext +from qapi.schema import QAPISchemaEnumMember +from qapi.types import gen_enum, gen_enum_lookup def build_event_send_proto(name, arg_type, boxed): diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py new file mode 100644 index 0000000000..7c7394f835 --- /dev/null +++ b/scripts/qapi/expr.py @@ -0,0 +1,383 @@ +# -*- coding: utf-8 -*- +# +# Check (context-free) QAPI schema expression structure +# +# Copyright IBM, Corp. 2011 +# Copyright (c) 2013-2019 Red Hat Inc. +# +# Authors: +# Anthony Liguori <aliguori@us.ibm.com> +# Markus Armbruster <armbru@redhat.com> +# Eric Blake <eblake@redhat.com> +# Marc-André Lureau <marcandre.lureau@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +import re +from collections import OrderedDict +from qapi.common import c_name +from qapi.error import QAPISemError + + +# 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_is_str(name, info, source): + if not isinstance(name, str): + raise QAPISemError(info, "%s requires a string name" % source) + + +def check_name_str(name, info, source, + allow_optional=False, enum_member=False, + permit_upper=False): + global valid_name + membername = name + + if allow_optional and name.startswith('*'): + membername = name[1:] + # 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 has an invalid name" % source) + if not permit_upper and name.lower() != name: + raise QAPISemError( + info, "%s uses uppercase in name" % source) + assert not membername.startswith('*') + + +def check_defn_name_str(name, info, meta): + check_name_str(name, info, meta, permit_upper=True) + if name.endswith('Kind') or name.endswith('List'): + raise QAPISemError( + info, "%s name should not end in '%s'" % (meta, name[-4:])) + + +def check_keys(value, info, source, required, optional): + + def pprint(elems): + return ', '.join("'" + e + "'" for e in sorted(elems)) + + missing = set(required) - set(value) + if missing: + raise QAPISemError( + info, + "%s misses key%s %s" + % (source, 's' if len(missing) > 1 else '', + pprint(missing))) + allowed = set(required + optional) + unknown = set(value) - allowed + if unknown: + raise QAPISemError( + info, + "%s has unknown key%s %s\nValid keys are %s." + % (source, 's' if len(unknown) > 1 else '', + pprint(unknown), pprint(allowed))) + + +def check_flags(expr, info): + for key in ['gen', 'success-response']: + if key in expr and expr[key] is not False: + raise QAPISemError( + info, "flag '%s' may only use false value" % key) + for key in ['boxed', 'allow-oob', 'allow-preconfig']: + if key in expr and expr[key] is not True: + raise QAPISemError( + info, "flag '%s' may only use true value" % key) + + +def normalize_if(expr): + ifcond = expr.get('if') + if isinstance(ifcond, str): + expr['if'] = [ifcond] + + +def check_if(expr, info, source): + + def check_if_str(ifcond, info): + if not isinstance(ifcond, str): + raise QAPISemError( + info, + "'if' condition of %s must be a string or a list of strings" + % source) + if ifcond.strip() == '': + raise QAPISemError( + info, + "'if' condition '%s' of %s makes no sense" + % (ifcond, source)) + + ifcond = expr.get('if') + if ifcond is None: + return + if isinstance(ifcond, list): + if ifcond == []: + raise QAPISemError( + info, "'if' condition [] of %s is useless" % source) + for elt in ifcond: + check_if_str(elt, info) + else: + check_if_str(ifcond, info) + + +def normalize_members(members): + if isinstance(members, OrderedDict): + for key, arg in members.items(): + if isinstance(arg, dict): + continue + members[key] = {'type': arg} + + +def check_type(value, info, source, + allow_array=False, allow_dict=False): + if value is None: + return + + # Array type + 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) + return + + # Type name + if isinstance(value, str): + return + + # Anonymous type + + 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 an object or type name" % source) + + permit_upper = allow_dict in info.pragma.name_case_whitelist + + # value is a dictionary, check that each member is okay + for (key, arg) in value.items(): + key_source = "%s member '%s'" % (source, key) + check_name_str(key, info, key_source, + allow_optional=True, permit_upper=permit_upper) + if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): + raise QAPISemError(info, "%s uses reserved name" % key_source) + check_keys(arg, info, key_source, ['type'], ['if']) + check_if(arg, info, key_source) + normalize_if(arg) + check_type(arg['type'], info, key_source, allow_array=True) + + +def normalize_features(features): + if isinstance(features, list): + features[:] = [f if isinstance(f, dict) else {'name': f} + for f in features] + + +def check_features(features, info): + if features is None: + return + if not isinstance(features, list): + raise QAPISemError(info, "'features' must be an array") + for f in features: + source = "'features' member" + assert isinstance(f, dict) + check_keys(f, info, source, ['name'], ['if']) + check_name_is_str(f['name'], info, source) + source = "%s '%s'" % (source, f['name']) + check_name_str(f['name'], info, source) + check_if(f, info, source) + normalize_if(f) + + +def normalize_enum(expr): + if isinstance(expr['data'], list): + expr['data'] = [m if isinstance(m, dict) else {'name': m} + for m in expr['data']] + + +def check_enum(expr, info): + name = expr['enum'] + members = expr['data'] + prefix = expr.get('prefix') + + if not isinstance(members, list): + raise QAPISemError(info, "'data' must be an array") + if prefix is not None and not isinstance(prefix, str): + raise QAPISemError(info, "'prefix' must be a string") + + permit_upper = name in info.pragma.name_case_whitelist + + for member in members: + source = "'data' member" + check_keys(member, info, source, ['name'], ['if']) + check_name_is_str(member['name'], info, source) + source = "%s '%s'" % (source, member['name']) + check_name_str(member['name'], info, source, + enum_member=True, permit_upper=permit_upper) + check_if(member, info, source) + normalize_if(member) + + +def check_struct(expr, info): + name = expr['struct'] + members = expr['data'] + + check_type(members, info, "'data'", allow_dict=name) + check_type(expr.get('base'), info, "'base'") + check_features(expr.get('features'), info) + + +def check_union(expr, info): + name = expr['union'] + base = expr.get('base') + discriminator = expr.get('discriminator') + members = expr['data'] + + if discriminator is None: # simple union + if base is not None: + raise QAPISemError(info, "'base' requires 'discriminator'") + else: # flat union + check_type(base, info, "'base'", allow_dict=name) + if not base: + raise QAPISemError(info, "'discriminator' requires 'base'") + check_name_is_str(discriminator, info, "'discriminator'") + + for (key, value) in members.items(): + source = "'data' member '%s'" % key + check_name_str(key, info, source) + check_keys(value, info, source, ['type'], ['if']) + check_if(value, info, source) + normalize_if(value) + check_type(value['type'], info, source, allow_array=not base) + + +def check_alternate(expr, info): + members = expr['data'] + + if len(members) == 0: + raise QAPISemError(info, "'data' must not be empty") + for (key, value) in members.items(): + source = "'data' member '%s'" % key + check_name_str(key, info, source) + check_keys(value, info, source, ['type'], ['if']) + check_if(value, info, source) + normalize_if(value) + check_type(value['type'], info, source) + + +def check_command(expr, info): + args = expr.get('data') + rets = expr.get('returns') + boxed = expr.get('boxed', False) + + if boxed and args is None: + raise QAPISemError(info, "'boxed': true requires 'data'") + check_type(args, info, "'data'", allow_dict=not boxed) + check_type(rets, info, "'returns'", allow_array=True) + check_features(expr.get('features'), info) + + +def check_event(expr, info): + args = expr.get('data') + boxed = expr.get('boxed', False) + + if boxed and args is None: + raise QAPISemError(info, "'boxed': true requires 'data'") + check_type(args, info, "'data'", allow_dict=not boxed) + + +def check_exprs(exprs): + for expr_elem in exprs: + expr = expr_elem['expr'] + info = expr_elem['info'] + doc = expr_elem.get('doc') + + if 'include' in expr: + continue + + if 'enum' in expr: + meta = 'enum' + elif 'union' in expr: + meta = 'union' + elif 'alternate' in expr: + meta = 'alternate' + elif 'struct' in expr: + meta = 'struct' + elif 'command' in expr: + meta = 'command' + elif 'event' in expr: + meta = 'event' + else: + raise QAPISemError(info, "expression is missing metatype") + + name = expr[meta] + check_name_is_str(name, info, "'%s'" % meta) + info.set_defn(meta, name) + check_defn_name_str(name, info, meta) + + if doc: + if doc.symbol != name: + raise QAPISemError( + info, "documentation comment is for '%s'" % doc.symbol) + doc.check_expr(expr) + elif info.pragma.doc_required: + raise QAPISemError(info, + "documentation comment required") + + if meta == 'enum': + check_keys(expr, info, meta, + ['enum', 'data'], ['if', 'prefix']) + normalize_enum(expr) + check_enum(expr, info) + elif meta == 'union': + check_keys(expr, info, meta, + ['union', 'data'], + ['base', 'discriminator', 'if']) + normalize_members(expr.get('base')) + normalize_members(expr['data']) + check_union(expr, info) + elif meta == 'alternate': + check_keys(expr, info, meta, + ['alternate', 'data'], ['if']) + normalize_members(expr['data']) + check_alternate(expr, info) + elif meta == 'struct': + check_keys(expr, info, meta, + ['struct', 'data'], ['base', 'if', 'features']) + normalize_members(expr['data']) + normalize_features(expr.get('features')) + check_struct(expr, info) + elif meta == 'command': + check_keys(expr, info, meta, + ['command'], + ['data', 'returns', 'boxed', 'if', 'features', + 'gen', 'success-response', 'allow-oob', + 'allow-preconfig']) + normalize_members(expr.get('data')) + normalize_features(expr.get('features')) + check_command(expr, info) + elif meta == 'event': + check_keys(expr, info, meta, + ['event'], ['data', 'boxed', 'if']) + normalize_members(expr.get('data')) + check_event(expr, info) + else: + assert False, 'unexpected meta type' + + normalize_if(expr) + check_if(expr, info, meta) + check_flags(expr, info) + + return exprs diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py new file mode 100644 index 0000000000..112b6d94c5 --- /dev/null +++ b/scripts/qapi/gen.py @@ -0,0 +1,291 @@ +# -*- coding: utf-8 -*- +# +# QAPI code generation +# +# Copyright (c) 2018-2019 Red Hat Inc. +# +# Authors: +# Markus Armbruster <armbru@redhat.com> +# Marc-André Lureau <marcandre.lureau@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + + +import errno +import os +import re +import sys +from contextlib import contextmanager + +from qapi.common import * +from qapi.schema import QAPISchemaVisitor + + +class QAPIGen(object): + + def __init__(self, fname): + self.fname = fname + self._preamble = '' + self._body = '' + + def preamble_add(self, text): + self._preamble += text + + def add(self, text): + self._body += text + + def get_content(self): + return self._top() + self._preamble + self._body + self._bottom() + + def _top(self): + return '' + + def _bottom(self): + return '' + + def write(self, output_dir): + pathname = os.path.join(output_dir, self.fname) + dir = os.path.dirname(pathname) + if dir: + try: + os.makedirs(dir) + except os.error as e: + if e.errno != errno.EEXIST: + raise + fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) + if sys.version_info[0] >= 3: + f = open(fd, 'r+', encoding='utf-8') + else: + f = os.fdopen(fd, 'r+') + text = self.get_content() + oldtext = f.read(len(text) + 1) + if text != oldtext: + f.seek(0) + f.truncate(0) + f.write(text) + f.close() + + +def _wrap_ifcond(ifcond, before, after): + if before == after: + return after # suppress empty #if ... #endif + + assert after.startswith(before) + out = before + added = after[len(before):] + if added[0] == '\n': + out += '\n' + added = added[1:] + out += gen_if(ifcond) + out += added + out += gen_endif(ifcond) + return out + + +class QAPIGenCCode(QAPIGen): + + def __init__(self, fname): + QAPIGen.__init__(self, fname) + self._start_if = None + + def start_if(self, ifcond): + assert self._start_if is None + self._start_if = (ifcond, self._body, self._preamble) + + def end_if(self): + assert self._start_if + self._wrap_ifcond() + self._start_if = None + + def _wrap_ifcond(self): + self._body = _wrap_ifcond(self._start_if[0], + self._start_if[1], self._body) + self._preamble = _wrap_ifcond(self._start_if[0], + self._start_if[2], self._preamble) + + def get_content(self): + assert self._start_if is None + return QAPIGen.get_content(self) + + +class QAPIGenC(QAPIGenCCode): + + def __init__(self, fname, blurb, pydoc): + QAPIGenCCode.__init__(self, fname) + self._blurb = blurb + self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, + re.MULTILINE)) + + def _top(self): + 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) + + def _bottom(self): + return mcgen(''' + +/* Dummy declaration to prevent empty .o file */ +char qapi_dummy_%(name)s; +''', + name=c_fname(self.fname)) + + +class QAPIGenH(QAPIGenC): + + def _top(self): + return QAPIGenC._top(self) + guardstart(self.fname) + + def _bottom(self): + return guardend(self.fname) + + +@contextmanager +def ifcontext(ifcond, *args): + """A 'with' statement context manager to wrap with start_if()/end_if() + + *args: any number of QAPIGenCCode + + Example:: + + with ifcontext(ifcond, self._genh, self._genc): + modify self._genh and self._genc ... + + Is equivalent to calling:: + + self._genh.start_if(ifcond) + self._genc.start_if(ifcond) + modify self._genh and self._genc ... + self._genh.end_if() + self._genc.end_if() + """ + for arg in args: + arg.start_if(ifcond) + yield + for arg in args: + arg.end_if() + + +class QAPIGenDoc(QAPIGen): + + def _top(self): + return (QAPIGen._top(self) + + '@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(self._prefix + self._what + '.c', + blurb, pydoc) + self._genh = QAPIGenH(self._prefix + self._what + '.h', + blurb, pydoc) + + def write(self, output_dir): + self._genc.write(output_dir) + self._genh.write(output_dir) + + +class QAPISchemaModularCVisitor(QAPISchemaVisitor): + + def __init__(self, prefix, what, blurb, pydoc): + self._prefix = prefix + self._what = what + self._blurb = blurb + self._pydoc = pydoc + self._genc = None + self._genh = None + self._module = {} + self._main_module = None + + @staticmethod + def _is_user_module(name): + return name and not name.startswith('./') + + @staticmethod + def _is_builtin_module(name): + return not name + + def _module_dirname(self, what, name): + if self._is_user_module(name): + return os.path.dirname(name) + return '' + + def _module_basename(self, what, name): + ret = '' if self._is_builtin_module(name) else self._prefix + if self._is_user_module(name): + basename = os.path.basename(name) + ret += what + if name != self._main_module: + ret += '-' + os.path.splitext(basename)[0] + else: + name = name[2:] if name else 'builtin' + ret += re.sub(r'-', '-' + name + '-', what) + return ret + + def _module_filename(self, what, name): + return os.path.join(self._module_dirname(what, name), + self._module_basename(what, name)) + + def _add_module(self, name, blurb): + basename = self._module_filename(self._what, name) + genc = QAPIGenC(basename + '.c', blurb, self._pydoc) + genh = QAPIGenH(basename + '.h', blurb, self._pydoc) + self._module[name] = (genc, genh) + self._set_module(name) + + def _add_user_module(self, name, blurb): + assert self._is_user_module(name) + if self._main_module is None: + self._main_module = name + self._add_module(name, blurb) + + def _add_system_module(self, name, blurb): + self._add_module(name and './' + name, blurb) + + def _set_module(self, name): + self._genc, self._genh = self._module[name] + + def write(self, output_dir, opt_builtins=False): + for name in self._module: + if self._is_builtin_module(name) and not opt_builtins: + continue + (genc, genh) = self._module[name] + genc.write(output_dir) + genh.write(output_dir) + + def _begin_user_module(self, name): + pass + + def visit_module(self, name): + if name in self._module: + self._set_module(name) + elif self._is_builtin_module(name): + # The built-in module has not been created. No code may + # be generated. + self._genc = None + self._genh = None + else: + self._add_user_module(name, self._blurb) + self._begin_user_module(name) + + def visit_include(self, name, info): + relname = os.path.relpath(self._module_filename(self._what, name), + os.path.dirname(self._genh.fname)) + self._genh.preamble_add(mcgen(''' +#include "%(relname)s.h" +''', + relname=relname)) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index f62cf0a2e1..b3a463dd8b 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -10,7 +10,12 @@ This work is licensed under the terms of the GNU GPL, version 2. See the COPYING file in the top-level directory. """ +import string + from qapi.common import * +from qapi.gen import QAPISchemaMonolithicCVisitor +from qapi.schema import (QAPISchemaArrayType, QAPISchemaBuiltinType, + QAPISchemaType) def to_qlit(obj, level=0, suppress_first_indent=False): @@ -206,13 +211,18 @@ const QLitObject %(c_name)s = %(c_string)s; for m in variants.variants]}, ifcond) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig): + success_response, boxed, allow_oob, allow_preconfig, + features): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type obj = {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type)} if allow_oob: obj['allow-oob'] = allow_oob + + if features: + obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] + self._gen_qlit(name, 'command', obj, ifcond) def visit_event(self, name, info, ifcond, arg_type, boxed): diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py new file mode 100644 index 0000000000..e800876ad1 --- /dev/null +++ b/scripts/qapi/parser.py @@ -0,0 +1,570 @@ +# -*- coding: utf-8 -*- +# +# QAPI schema parser +# +# Copyright IBM, Corp. 2011 +# Copyright (c) 2013-2019 Red Hat Inc. +# +# Authors: +# Anthony Liguori <aliguori@us.ibm.com> +# Markus Armbruster <armbru@redhat.com> +# Marc-André Lureau <marcandre.lureau@redhat.com> +# Kevin Wolf <kwolf@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +import os +import re +import sys +from collections import OrderedDict + +from qapi.error import QAPIParseError, QAPISemError +from qapi.source import QAPISourceInfo + + +class QAPISchemaParser(object): + + def __init__(self, fname, previously_included=None, incl_info=None): + previously_included = previously_included or set() + previously_included.add(os.path.abspath(fname)) + + try: + if sys.version_info[0] >= 3: + fp = open(fname, 'r', encoding='utf-8') + else: + fp = open(fname, 'r') + self.src = fp.read() + except IOError as e: + raise QAPISemError(incl_info or QAPISourceInfo(None, None, None), + "can't read %s file '%s': %s" + % ("include" if incl_info else "schema", + fname, + e.strerror)) + + if self.src == '' or self.src[-1] != '\n': + self.src += '\n' + self.cursor = 0 + self.info = QAPISourceInfo(fname, 1, incl_info) + self.line_pos = 0 + self.exprs = [] + self.docs = [] + self.accept() + cur_doc = None + + while self.tok is not None: + info = self.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") + incl_fname = os.path.join(os.path.dirname(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) + self.docs.extend(exprs_include.docs) + 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 an object") + 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, "definition 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, incl_fname, previously_included): + incl_abs_fname = os.path.abspath(incl_fname) + # catch inclusion cycle + inf = info + while inf: + if incl_abs_fname == os.path.abspath(inf.fname): + 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 None + + return QAPISchemaParser(incl_fname, previously_included, info) + + def _pragma(self, name, value, info): + if name == 'doc-required': + if not isinstance(value, bool): + raise QAPISemError(info, + "pragma 'doc-required' must be boolean") + info.pragma.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") + info.pragma.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") + info.pragma.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 == "'": + # Note: we accept only printable ASCII + string = '' + esc = False + while True: + ch = self.src[self.cursor] + self.cursor += 1 + if ch == '\n': + raise QAPIParseError(self, "missing terminating \"'\"") + if esc: + # Note: we recognize only \\ because we have + # no use for funny characters in strings + if ch != '\\': + raise QAPIParseError(self, + "unknown escape \\%s" % ch) + esc = False + elif ch == '\\': + esc = True + continue + elif ch == "'": + self.val = string + return + if ord(ch) < 32 or ord(ch) >= 127: + raise QAPIParseError( + self, "funny character in string") + 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.tok == '\n': + if self.cursor == len(self.src): + self.tok = None + return + self.info = self.info.next_line() + self.line_pos = self.cursor + elif not self.tok.isspace(): + # Show up to next structural, whitespace or quote + # character + match = re.match('[^[\\]{}:,\\s\'"]+', + self.src[self.cursor-1:]) + raise QAPIParseError(self, "stray '%s'" % match.group(0)) + + 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 '##'") + + +class QAPIDoc(object): + """ + A documentation comment block, either definition or free-form + + Definition documentation blocks consist of + + * a body section: one line naming the definition, followed by an + overview (any number of lines) + + * argument sections: a description of each argument (for commands + and events) or member (for structs, unions and alternates) + + * features sections: a description of each feature flag + + * additional (non-argument) sections, possibly tagged + + Free-form documentation blocks consist only of a body section. + """ + + 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() + self.features = OrderedDict() + # a list of Section + self.sections = [] + # the current section + self._section = self.body + self._append_line = self._append_body_line + + 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. + + The way that the line is dealt with depends on which part of + the documentation we're parsing right now: + * The body section: ._append_line is ._append_body_line + * An argument section: ._append_line is ._append_args_line + * A features section: ._append_line is ._append_features_line + * An additional section: ._append_line is ._append_various_line + """ + line = line[1:] + if not line: + self._append_freeform(line) + return + + if line[0] != ' ': + raise QAPIParseError(self._parser, "missing space after #") + line = line[1:] + self._append_line(line) + + def end_comment(self): + self._end_section() + + @staticmethod + def _is_section_tag(name): + return name in ('Returns:', 'Since:', + # those are often singular or plural + 'Note:', 'Notes:', + 'Example:', 'Examples:', + 'TODO:') + + def _append_body_line(self, line): + """ + Process a line of documentation text in the body section. + + If this a symbol line and it is the section's first line, this + is a definition documentation block for that symbol. + + If it's a definition documentation block, another symbol line + begins the argument section for the argument named by it, and + a section tag begins an additional section. Start that + section and append the line to it. + + Else, append the line to the current section. + """ + name = line.split(' ', 1)[0] + # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't + # recognized, and get silently treated as ordinary text + if not self.symbol and 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") + elif self.symbol: + # This is a definition documentation block + if name.startswith('@') and name.endswith(':'): + self._append_line = self._append_args_line + self._append_args_line(line) + elif line == 'Features:': + self._append_line = self._append_features_line + elif self._is_section_tag(name): + self._append_line = self._append_various_line + self._append_various_line(line) + else: + self._append_freeform(line.strip()) + else: + # This is a free-form documentation block + self._append_freeform(line.strip()) + + def _append_args_line(self, line): + """ + Process a line of documentation text in an argument section. + + A symbol line begins the next argument section, a section tag + section or a non-indented line after a blank line begins an + additional section. Start that section and append the line to + it. + + Else, append the line to the current section. + + """ + name = line.split(' ', 1)[0] + + if name.startswith('@') and name.endswith(':'): + line = line[len(name)+1:] + self._start_args_section(name[1:-1]) + elif self._is_section_tag(name): + self._append_line = self._append_various_line + self._append_various_line(line) + return + elif (self._section.text.endswith('\n\n') + and line and not line[0].isspace()): + if line == 'Features:': + self._append_line = self._append_features_line + else: + self._start_section() + self._append_line = self._append_various_line + self._append_various_line(line) + return + + self._append_freeform(line.strip()) + + def _append_features_line(self, line): + name = line.split(' ', 1)[0] + + if name.startswith('@') and name.endswith(':'): + line = line[len(name)+1:] + self._start_features_section(name[1:-1]) + elif self._is_section_tag(name): + self._append_line = self._append_various_line + self._append_various_line(line) + return + elif (self._section.text.endswith('\n\n') + and line and not line[0].isspace()): + self._start_section() + self._append_line = self._append_various_line + self._append_various_line(line) + return + + self._append_freeform(line.strip()) + + def _append_various_line(self, line): + """ + Process a line of documentation text in an additional section. + + A symbol line is an error. + + A section tag begins an additional section. Start that + section and append the line to it. + + Else, append the line to the current section. + """ + name = line.split(' ', 1)[0] + + if name.startswith('@') and name.endswith(':'): + raise QAPIParseError(self._parser, + "'%s' can't follow '%s' section" + % (name, self.sections[0].name)) + elif self._is_section_tag(name): + line = line[len(name)+1:] + self._start_section(name[:-1]) + + if (not self._section.name or + not self._section.name.startswith('Example')): + line = line.strip() + + self._append_freeform(line) + + def _start_symbol_section(self, symbols_dict, 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 symbols_dict: + raise QAPIParseError(self._parser, + "'%s' parameter name duplicated" % name) + assert not self.sections + self._end_section() + self._section = QAPIDoc.ArgSection(name) + symbols_dict[name] = self._section + + def _start_args_section(self, name): + self._start_symbol_section(self.args, name) + + def _start_features_section(self, name): + self._start_symbol_section(self.features, name) + + 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): + 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)) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py new file mode 100644 index 0000000000..f7d68a35f4 --- /dev/null +++ b/scripts/qapi/schema.py @@ -0,0 +1,1057 @@ +# -*- coding: utf-8 -*- +# +# QAPI schema internal representation +# +# Copyright (c) 2015-2019 Red Hat Inc. +# +# Authors: +# Markus Armbruster <armbru@redhat.com> +# Eric Blake <eblake@redhat.com> +# Marc-André Lureau <marcandre.lureau@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +# TODO catching name collisions in generated code would be nice + +import os +import re +from collections import OrderedDict + +from qapi.common import c_name, pointer_suffix +from qapi.error import QAPIError, QAPIParseError, QAPISemError +from qapi.expr import check_exprs +from qapi.parser import QAPISchemaParser + + +class QAPISchemaEntity(object): + meta = None + + def __init__(self, name, info, doc, ifcond=None): + 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 + # triggered the implicit definition (there may be more than one + # such place). + self.info = info + self.doc = doc + self._ifcond = ifcond or [] + self._checked = False + + def c_name(self): + return c_name(self.name) + + def check(self, schema): + assert not self._checked + if self.info: + self._module = os.path.relpath(self.info.fname, + os.path.dirname(schema.fname)) + self._checked = True + + @property + def ifcond(self): + assert self._checked + return self._ifcond + + @property + def module(self): + assert self._checked + return self._module + + def is_implicit(self): + return not self.info + + def visit(self, visitor): + assert self._checked + + def describe(self): + assert self.meta + return "%s '%s'" % (self.meta, self.name) + + +class QAPISchemaVisitor(object): + def visit_begin(self, schema): + pass + + 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 + + def visit_enum_type(self, name, info, ifcond, members, prefix): + pass + + def visit_array_type(self, name, info, ifcond, element_type): + pass + + def visit_object_type(self, name, info, ifcond, base, members, variants, + features): + pass + + def visit_object_type_flat(self, name, info, ifcond, members, variants, + features): + pass + + def visit_alternate_type(self, name, info, ifcond, variants): + pass + + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig, + features): + pass + + def visit_event(self, name, info, ifcond, arg_type, boxed): + pass + + +class QAPISchemaInclude(QAPISchemaEntity): + + def __init__(self, fname, info): + QAPISchemaEntity.__init__(self, None, info, None) + self.fname = fname + + def visit(self, visitor): + QAPISchemaEntity.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. + 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 + + def describe(self): + assert self.meta + return "%s type '%s'" % (self.meta, self.name) + + +class QAPISchemaBuiltinType(QAPISchemaType): + meta = 'built-in' + + 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): + QAPISchemaType.visit(self, visitor) + visitor.visit_builtin_type(self.name, self.info, self.json_type()) + + +class QAPISchemaEnumType(QAPISchemaType): + meta = 'enum' + + def __init__(self, name, info, doc, ifcond, members, prefix): + QAPISchemaType.__init__(self, name, info, doc, ifcond) + for m in members: + assert isinstance(m, QAPISchemaEnumMember) + m.set_defined_in(name) + assert prefix is None or isinstance(prefix, str) + self.members = members + self.prefix = prefix + + def check(self, schema): + QAPISchemaType.check(self, schema) + seen = {} + for m in self.members: + m.check_clash(self.info, seen) + if self.doc: + self.doc.connect_member(m) + + 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 [m.name for m in self.members] + + def json_type(self): + return 'string' + + def visit(self, visitor): + QAPISchemaType.visit(self, visitor) + visitor.visit_enum_type(self.name, self.info, self.ifcond, + self.members, self.prefix) + + +class QAPISchemaArrayType(QAPISchemaType): + meta = 'array' + + def __init__(self, name, info, element_type): + QAPISchemaType.__init__(self, name, info, None, None) + assert isinstance(element_type, str) + self._element_type_name = element_type + self.element_type = None + + def check(self, schema): + QAPISchemaType.check(self, schema) + self.element_type = schema.resolve_type( + self._element_type_name, self.info, + self.info and self.info.defn_meta) + assert not isinstance(self.element_type, QAPISchemaArrayType) + + @property + def ifcond(self): + assert self._checked + return self.element_type.ifcond + + @property + def module(self): + assert self._checked + return self.element_type.module + + 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): + QAPISchemaType.visit(self, visitor) + visitor.visit_array_type(self.name, self.info, self.ifcond, + self.element_type) + + def describe(self): + assert self.meta + return "%s type ['%s']" % (self.meta, self._element_type_name) + + +class QAPISchemaObjectType(QAPISchemaType): + def __init__(self, name, info, doc, ifcond, + base, local_members, variants, features): + # 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, ifcond) + self.meta = 'union' if variants else 'struct' + assert base is None or isinstance(base, str) + for m in local_members: + assert isinstance(m, QAPISchemaObjectTypeMember) + m.set_defined_in(name) + if variants is not None: + assert isinstance(variants, QAPISchemaObjectTypeVariants) + variants.set_defined_in(name) + for f in features: + assert isinstance(f, QAPISchemaFeature) + f.set_defined_in(name) + self._base_name = base + self.base = None + self.local_members = local_members + self.variants = variants + self.members = None + self.features = features + + def check(self, schema): + # This calls another type T's .check() exactly when the C + # struct emitted by gen_object() contains that T's C struct + # (pointers don't count). + if self.members is not None: + # A previous .check() completed: nothing to do + return + if self._checked: + # Recursed: C struct contains itself + raise QAPISemError(self.info, + "object %s contains itself" % self.name) + + QAPISchemaType.check(self, schema) + assert self._checked and self.members is None + + seen = OrderedDict() + if self._base_name: + self.base = schema.resolve_type(self._base_name, self.info, + "'base'") + if (not isinstance(self.base, QAPISchemaObjectType) + or self.base.variants): + raise QAPISemError( + self.info, + "'base' requires a struct type, %s isn't" + % self.base.describe()) + 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) + members = seen.values() + + if self.variants: + self.variants.check(schema, seen) + self.variants.check_clash(self.info, seen) + + # Features are in a name space separate from members + seen = {} + for f in self.features: + f.check_clash(self.info, seen) + + if self.doc: + self.doc.check() + + self.members = members # mark completed + + # 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 self._checked + assert not self.variants # not implemented + for m in self.members: + m.check_clash(info, seen) + + @property + def ifcond(self): + assert self._checked + if isinstance(self._ifcond, QAPISchemaType): + # Simple union wrapper type inherits from wrapped type; + # see _make_implicit_object_type() + return self._ifcond.ifcond + return self._ifcond + + 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): + QAPISchemaType.visit(self, visitor) + visitor.visit_object_type(self.name, self.info, self.ifcond, + self.base, self.local_members, self.variants, + self.features) + visitor.visit_object_type_flat(self.name, self.info, self.ifcond, + self.members, self.variants, + self.features) + + +class QAPISchemaMember(object): + """ Represents object members, enum members and features """ + role = 'member' + + def __init__(self, name, info, ifcond=None): + assert isinstance(name, str) + self.name = name + self.info = info + self.ifcond = ifcond or [] + self.defined_in = None + + def set_defined_in(self, name): + assert not self.defined_in + self.defined_in = name + + def check_clash(self, info, seen): + cname = c_name(self.name) + if cname in seen: + raise QAPISemError( + info, + "%s collides with %s" + % (self.describe(info), seen[cname].describe(info))) + seen[cname] = self + + def describe(self, info): + role = self.role + defined_in = self.defined_in + assert defined_in + + if defined_in.startswith('q_obj_'): + # See QAPISchema._make_implicit_object_type() - reverse the + # mapping there to create a nice human-readable description + defined_in = defined_in[6:] + if defined_in.endswith('-arg'): + # Implicit type created for a command's dict 'data' + assert role == 'member' + role = 'parameter' + elif defined_in.endswith('-base'): + # Implicit type created for a flat union's dict 'base' + role = 'base ' + role + else: + # Implicit type created for a simple union's branch + assert defined_in.endswith('-wrapper') + # Unreachable and not implemented + assert False + elif defined_in.endswith('Kind'): + # See QAPISchema._make_implicit_enum_type() + # Implicit enum created for simple union's branches + assert role == 'value' + role = 'branch' + elif defined_in != info.defn_name: + return "%s '%s' of type '%s'" % (role, self.name, defined_in) + return "%s '%s'" % (role, self.name) + + +class QAPISchemaEnumMember(QAPISchemaMember): + role = 'value' + + +class QAPISchemaFeature(QAPISchemaMember): + role = 'feature' + + +class QAPISchemaObjectTypeMember(QAPISchemaMember): + def __init__(self, name, info, typ, optional, ifcond=None): + QAPISchemaMember.__init__(self, name, info, ifcond) + assert isinstance(typ, str) + assert isinstance(optional, bool) + self._type_name = typ + self.type = None + self.optional = optional + + def check(self, schema): + assert self.defined_in + self.type = schema.resolve_type(self._type_name, self.info, + self.describe) + + +class QAPISchemaObjectTypeVariants(object): + def __init__(self, tag_name, info, 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)) + for v in variants: + assert isinstance(v, QAPISchemaObjectTypeVariant) + self._tag_name = tag_name + self.info = info + self.tag_member = tag_member + self.variants = variants + + def set_defined_in(self, name): + for v in self.variants: + v.set_defined_in(name) + + def check(self, schema, seen): + if not self.tag_member: # flat union + self.tag_member = seen.get(c_name(self._tag_name)) + base = "'base'" + # Pointing to the base type when not implicit would be + # nice, but we don't know it here + if not self.tag_member or self._tag_name != self.tag_member.name: + raise QAPISemError( + self.info, + "discriminator '%s' is not a member of %s" + % (self._tag_name, base)) + # Here we do: + base_type = schema.lookup_type(self.tag_member.defined_in) + assert base_type + if not base_type.is_implicit(): + base = "base type '%s'" % self.tag_member.defined_in + if not isinstance(self.tag_member.type, QAPISchemaEnumType): + raise QAPISemError( + self.info, + "discriminator member '%s' of %s must be of enum type" + % (self._tag_name, base)) + if self.tag_member.optional: + raise QAPISemError( + self.info, + "discriminator member '%s' of %s must not be optional" + % (self._tag_name, base)) + if self.tag_member.ifcond: + raise QAPISemError( + self.info, + "discriminator member '%s' of %s must not be conditional" + % (self._tag_name, base)) + else: # simple union + assert isinstance(self.tag_member.type, QAPISchemaEnumType) + assert not self.tag_member.optional + assert self.tag_member.ifcond == [] + if self._tag_name: # flat union + # branches that are not explicitly covered get an empty type + cases = set([v.name for v in self.variants]) + for m in self.tag_member.type.members: + if m.name not in cases: + v = QAPISchemaObjectTypeVariant(m.name, self.info, + 'q_empty', m.ifcond) + v.set_defined_in(self.tag_member.defined_in) + self.variants.append(v) + if not self.variants: + raise QAPISemError(self.info, "union has no branches") + 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: + if v.name not in self.tag_member.type.member_names(): + raise QAPISemError( + self.info, + "branch '%s' is not a value of %s" + % (v.name, self.tag_member.type.describe())) + if (not isinstance(v.type, QAPISchemaObjectType) + or v.type.variants): + raise QAPISemError( + self.info, + "%s cannot use %s" + % (v.describe(self.info), v.type.describe())) + 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 + v.type.check_clash(info, dict(seen)) + + +class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): + role = 'branch' + + def __init__(self, name, info, typ, ifcond=None): + QAPISchemaObjectTypeMember.__init__(self, name, info, typ, + False, ifcond) + + +class QAPISchemaAlternateType(QAPISchemaType): + meta = 'alternate' + + def __init__(self, name, info, doc, ifcond, variants): + QAPISchemaType.__init__(self, name, info, doc, ifcond) + assert isinstance(variants, QAPISchemaObjectTypeVariants) + assert variants.tag_member + variants.set_defined_in(name) + variants.tag_member.set_defined_in(self.name) + self.variants = variants + + def check(self, schema): + QAPISchemaType.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 = {} + types_seen = {} + for v in self.variants.variants: + v.check_clash(self.info, seen) + qtype = v.type.alternate_qtype() + if not qtype: + raise QAPISemError( + self.info, + "%s cannot use %s" + % (v.describe(self.info), v.type.describe())) + conflicting = set([qtype]) + if qtype == 'QTYPE_QSTRING': + if isinstance(v.type, QAPISchemaEnumType): + for m in v.type.members: + if m.name in ['on', 'off']: + conflicting.add('QTYPE_QBOOL') + if re.match(r'[-+0-9.]', m.name): + # 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( + self.info, + "%s can't be distinguished from '%s'" + % (v.describe(self.info), types_seen[qt])) + types_seen[qt] = v.name + 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): + QAPISchemaType.visit(self, visitor) + visitor.visit_alternate_type(self.name, self.info, self.ifcond, + self.variants) + + +class QAPISchemaCommand(QAPISchemaEntity): + meta = 'command' + + def __init__(self, name, info, doc, ifcond, arg_type, ret_type, + gen, success_response, boxed, allow_oob, allow_preconfig, + features): + QAPISchemaEntity.__init__(self, name, info, doc, ifcond) + assert not arg_type or isinstance(arg_type, str) + assert not ret_type or isinstance(ret_type, str) + for f in features: + assert isinstance(f, QAPISchemaFeature) + f.set_defined_in(name) + 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 + self.allow_oob = allow_oob + self.allow_preconfig = allow_preconfig + self.features = features + + def check(self, schema): + QAPISchemaEntity.check(self, schema) + if self._arg_type_name: + self.arg_type = schema.resolve_type( + self._arg_type_name, self.info, "command's 'data'") + if not isinstance(self.arg_type, QAPISchemaObjectType): + raise QAPISemError( + self.info, + "command's 'data' cannot take %s" + % self.arg_type.describe()) + if self.arg_type.variants and not self.boxed: + raise QAPISemError( + self.info, + "command's 'data' can take %s only with 'boxed': true" + % self.arg_type.describe()) + if self._ret_type_name: + self.ret_type = schema.resolve_type( + self._ret_type_name, self.info, "command's 'returns'") + if self.name not in self.info.pragma.returns_whitelist: + if not (isinstance(self.ret_type, QAPISchemaObjectType) + or (isinstance(self.ret_type, QAPISchemaArrayType) + and isinstance(self.ret_type.element_type, + QAPISchemaObjectType))): + raise QAPISemError( + self.info, + "command's 'returns' cannot take %s" + % self.ret_type.describe()) + + # Features are in a name space separate from members + seen = {} + for f in self.features: + f.check_clash(self.info, seen) + + def visit(self, visitor): + QAPISchemaEntity.visit(self, visitor) + visitor.visit_command(self.name, self.info, self.ifcond, + self.arg_type, self.ret_type, + self.gen, self.success_response, + self.boxed, self.allow_oob, + self.allow_preconfig, + self.features) + + +class QAPISchemaEvent(QAPISchemaEntity): + meta = 'event' + + def __init__(self, name, info, doc, ifcond, arg_type, boxed): + QAPISchemaEntity.__init__(self, name, info, doc, ifcond) + 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): + QAPISchemaEntity.check(self, schema) + if self._arg_type_name: + self.arg_type = schema.resolve_type( + self._arg_type_name, self.info, "event's 'data'") + if not isinstance(self.arg_type, QAPISchemaObjectType): + raise QAPISemError( + self.info, + "event's 'data' cannot take %s" + % self.arg_type.describe()) + if self.arg_type.variants and not self.boxed: + raise QAPISemError( + self.info, + "event's 'data' can take %s only with 'boxed': true" + % self.arg_type.describe()) + + def visit(self, visitor): + QAPISchemaEntity.visit(self, visitor) + visitor.visit_event(self.name, self.info, self.ifcond, + self.arg_type, self.boxed) + + +class QAPISchema(object): + def __init__(self, fname): + self.fname = fname + parser = QAPISchemaParser(fname) + exprs = check_exprs(parser.exprs) + self.docs = parser.docs + self._entity_list = [] + 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 + assert ent.info or self._predefining + self._entity_list.append(ent) + if ent.name is None: + return + # TODO reject names that differ only in '_' vs. '.' vs. '-', + # because they're liable to clash in generated C. + other_ent = self._entity_dict.get(ent.name) + if other_ent: + if other_ent.info: + where = QAPIError(other_ent.info, None, "previous definition") + raise QAPISemError( + ent.info, + "'%s' is already defined\n%s" % (ent.name, where)) + raise QAPISemError( + ent.info, "%s is already defined" % other_ent.describe()) + 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 resolve_type(self, name, info, what): + typ = self.lookup_type(name) + if not typ: + if callable(what): + what = what(info) + raise QAPISemError( + info, "%s uses unknown type '%s'" % (what, name)) + return typ + + 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.fname)) + 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)) + # 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): + 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, [], None, []) + self._def_entity(self.the_empty_object_type) + + qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', + 'qbool'] + qtype_values = self._make_enum_members( + [{'name': n} for n in qtypes], None) + + self._def_entity(QAPISchemaEnumType('QType', None, None, None, + qtype_values, 'QTYPE')) + + def _make_features(self, features, info): + return [QAPISchemaFeature(f['name'], info, f.get('if')) + for f in features] + + def _make_enum_members(self, values, info): + return [QAPISchemaEnumMember(v['name'], info, v.get('if')) + for v in values] + + def _make_implicit_enum_type(self, name, info, ifcond, values): + # See also QAPISchemaObjectTypeMember.describe() + name = name + 'Kind' # reserved by check_defn_name_str() + self._def_entity(QAPISchemaEnumType( + name, info, None, ifcond, self._make_enum_members(values, info), + None)) + return name + + def _make_array_type(self, element_type, info): + name = element_type + 'List' # reserved by check_defn_name_str() + 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, ifcond, + role, members): + if not members: + return None + # See also QAPISchemaObjectTypeMember.describe() + name = 'q_obj_%s-%s' % (name, role) + typ = self.lookup_entity(name, QAPISchemaObjectType) + if typ: + # The implicit object type has multiple users. This can + # happen only for simple unions' implicit wrapper types. + # Its ifcond should be the disjunction of its user's + # ifconds. Not implemented. Instead, we always pass the + # wrapped type's ifcond, which is trivially the same for all + # users. It's also necessary for the wrapper to compile. + # But it's not tight: the disjunction need not imply it. We + # may end up compiling useless wrapper types. + # TODO kill simple unions or implement the disjunction + assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access + else: + self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, + None, members, None, [])) + return name + + def _def_enum_type(self, expr, info, doc): + name = expr['enum'] + data = expr['data'] + prefix = expr.get('prefix') + ifcond = expr.get('if') + self._def_entity(QAPISchemaEnumType( + name, info, doc, ifcond, + self._make_enum_members(data, info), prefix)) + + def _make_member(self, name, typ, ifcond, 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, info, typ, optional, ifcond) + + def _make_members(self, data, info): + return [self._make_member(key, value['type'], value.get('if'), 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'] + ifcond = expr.get('if') + features = expr.get('features', []) + self._def_entity(QAPISchemaObjectType( + name, info, doc, ifcond, base, + self._make_members(data, info), + None, + self._make_features(features, info))) + + def _make_variant(self, case, typ, ifcond, info): + return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) + + def _make_simple_variant(self, case, typ, ifcond, 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, self.lookup_type(typ), + 'wrapper', [self._make_member('data', typ, None, info)]) + return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) + + def _def_union_type(self, expr, info, doc): + name = expr['union'] + data = expr['data'] + base = expr.get('base') + ifcond = expr.get('if') + tag_name = expr.get('discriminator') + tag_member = None + if isinstance(base, dict): + base = self._make_implicit_object_type( + name, info, doc, ifcond, + 'base', self._make_members(base, info)) + if tag_name: + variants = [self._make_variant(key, value['type'], + value.get('if'), info) + for (key, value) in data.items()] + members = [] + else: + variants = [self._make_simple_variant(key, value['type'], + value.get('if'), info) + for (key, value) in data.items()] + enum = [{'name': v.name, 'if': v.ifcond} for v in variants] + typ = self._make_implicit_enum_type(name, info, ifcond, enum) + tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) + members = [tag_member] + self._def_entity( + QAPISchemaObjectType(name, info, doc, ifcond, base, members, + QAPISchemaObjectTypeVariants( + tag_name, info, tag_member, variants), + [])) + + def _def_alternate_type(self, expr, info, doc): + name = expr['alternate'] + data = expr['data'] + ifcond = expr.get('if') + variants = [self._make_variant(key, value['type'], value.get('if'), + info) + for (key, value) in data.items()] + tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) + self._def_entity( + QAPISchemaAlternateType(name, info, doc, ifcond, + QAPISchemaObjectTypeVariants( + None, info, 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) + allow_oob = expr.get('allow-oob', False) + allow_preconfig = expr.get('allow-preconfig', False) + ifcond = expr.get('if') + features = expr.get('features', []) + if isinstance(data, OrderedDict): + data = self._make_implicit_object_type( + name, info, doc, ifcond, '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, ifcond, data, rets, + gen, success_response, + boxed, allow_oob, allow_preconfig, + self._make_features(features, info))) + + def _def_event(self, expr, info, doc): + name = expr['event'] + data = expr.get('data') + boxed = expr.get('boxed', False) + ifcond = expr.get('if') + if isinstance(data, OrderedDict): + data = self._make_implicit_object_type( + name, info, doc, ifcond, 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) + + def _def_exprs(self, exprs): + for expr_elem in 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) + elif 'include' in expr: + self._def_include(expr, info, doc) + else: + assert False + + def check(self): + for ent in self._entity_list: + ent.check(self) + + def visit(self, visitor): + visitor.visit_begin(self) + module = None + visitor.visit_module(module) + 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/scripts/qapi/source.py b/scripts/qapi/source.py new file mode 100644 index 0000000000..8956885033 --- /dev/null +++ b/scripts/qapi/source.py @@ -0,0 +1,67 @@ +# +# QAPI frontend source file info +# +# Copyright (c) 2019 Red Hat Inc. +# +# Authors: +# Markus Armbruster <armbru@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +import copy +import sys + + +class QAPISchemaPragma(object): + def __init__(self): + # Are documentation comments required? + self.doc_required = False + # Whitelist of commands allowed to return a non-dictionary + self.returns_whitelist = [] + # Whitelist of entities allowed to violate case conventions + self.name_case_whitelist = [] + + +class QAPISourceInfo(object): + def __init__(self, fname, line, parent): + self.fname = fname + self.line = line + self.parent = parent + self.pragma = parent.pragma if parent else QAPISchemaPragma() + self.defn_meta = None + self.defn_name = None + + def set_defn(self, meta, name): + self.defn_meta = meta + self.defn_name = name + + def next_line(self): + info = copy.copy(self) + info.line += 1 + return info + + def loc(self): + if self.fname is None: + return sys.argv[0] + ret = self.fname + if self.line is not None: + ret += ':%d' % self.line + return ret + + def in_defn(self): + if self.defn_name: + return "%s: In %s '%s':\n" % (self.fname, + self.defn_meta, self.defn_name) + return '' + + def include_path(self): + ret = '' + parent = self.parent + while parent: + ret = 'In file included from %s:\n' % parent.loc() + ret + parent = parent.parent + return ret + + def __str__(self): + return self.include_path() + self.in_defn() + self.loc() diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 3edd9374aa..d8751daa04 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -14,6 +14,8 @@ This work is licensed under the terms of the GNU GPL, version 2. """ from qapi.common import * +from qapi.gen import QAPISchemaModularCVisitor, ifcontext +from qapi.schema import QAPISchemaEnumMember, QAPISchemaObjectType # variants must be emitted before their container; track what has already @@ -21,6 +23,65 @@ from qapi.common import * objects_seen = set() +def gen_enum_lookup(name, members, prefix=None): + ret = mcgen(''' + +const QEnumLookup %(c_name)s_lookup = { + .array = (const char *const[]) { +''', + c_name=c_name(name)) + for m in members: + ret += gen_if(m.ifcond) + index = c_enum_const(name, m.name, prefix) + ret += mcgen(''' + [%(index)s] = "%(name)s", +''', + index=index, name=m.name) + ret += gen_endif(m.ifcond) + + ret += mcgen(''' + }, + .size = %(max_index)s +}; +''', + max_index=c_enum_const(name, '_MAX', prefix)) + return ret + + +def gen_enum(name, members, prefix=None): + # append automatically generated _MAX value + enum_members = members + [QAPISchemaEnumMember('_MAX', None)] + + ret = mcgen(''' + +typedef enum %(c_name)s { +''', + c_name=c_name(name)) + + for m in enum_members: + ret += gen_if(m.ifcond) + ret += mcgen(''' + %(c_enum)s, +''', + c_enum=c_enum_const(name, m.name, prefix)) + ret += gen_endif(m.ifcond) + + 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 gen_fwd_object_or_array(name): return mcgen(''' diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 484ebb66ad..c72f2bc5c0 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -14,6 +14,8 @@ See the COPYING file in the top-level directory. """ from qapi.common import * +from qapi.gen import QAPISchemaModularCVisitor, ifcontext +from qapi.schema import QAPISchemaObjectType def gen_visit_decl(name, scalar=False): diff --git a/tests/Makefile.include b/tests/Makefile.include index 3543451ed3..09e5b410dc 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -31,13 +31,20 @@ ifneq ($(wildcard config-host.mak),) export SRC_PATH # TODO don't duplicate $(SRC_PATH)/Makefile's qapi-py here -qapi-py = $(SRC_PATH)/scripts/qapi/commands.py \ +qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \ +$(SRC_PATH)/scripts/qapi/commands.py \ +$(SRC_PATH)/scripts/qapi/common.py \ +$(SRC_PATH)/scripts/qapi/doc.py \ +$(SRC_PATH)/scripts/qapi/error.py \ $(SRC_PATH)/scripts/qapi/events.py \ +$(SRC_PATH)/scripts/qapi/expr.py \ +$(SRC_PATH)/scripts/qapi/gen.py \ $(SRC_PATH)/scripts/qapi/introspect.py \ +$(SRC_PATH)/scripts/qapi/parser.py \ +$(SRC_PATH)/scripts/qapi/schema.py \ +$(SRC_PATH)/scripts/qapi/source.py \ $(SRC_PATH)/scripts/qapi/types.py \ $(SRC_PATH)/scripts/qapi/visit.py \ -$(SRC_PATH)/scripts/qapi/common.py \ -$(SRC_PATH)/scripts/qapi/doc.py \ $(SRC_PATH)/scripts/qapi-gen.py # Get the list of all supported sysemu targets @@ -609,6 +616,7 @@ tests/test-qapi-gen-timestamp: \ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o tests -p "test-" $<, \ "GEN","$(@:%-timestamp=%)") + @rm -f tests/test-qapi-doc.texi @>$@ tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.json $(qapi-py) @@ -1101,17 +1109,11 @@ check-tests/check-block.sh: tests/check-block.sh qemu-img$(EXESUF) \ $(patsubst %,%/all,$(filter %-softmmu,$(TARGET_DIRS))) @$< -.PHONY: $(patsubst %, check-%, $(check-qapi-schema-y)) -$(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json +.PHONY: check-tests/qapi-schema/frontend +check-tests/qapi-schema/frontend: $(addprefix $(SRC_PATH)/, $(check-qapi-schema-y)) $(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts \ - PYTHONIOENCODING=utf-8 $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py \ - $^ >$*.test.out 2>$*.test.err; \ - echo $$? >$*.test.exit, \ - "TEST","$*.out") - @# Sanitize error messages (make them independent of build directory) - @perl -p -e 's|\Q$(SRC_PATH)\E/||g' $*.test.err | diff -u $(SRC_PATH)/$*.err - - @diff -u $(SRC_PATH)/$*.out $*.test.out - @diff -u $(SRC_PATH)/$*.exit $*.test.exit + PYTHONIOENCODING=utf-8 $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py $^, \ + TEST, check-qapi-schema) .PHONY: check-tests/qapi-schema/doc-good.texi check-tests/qapi-schema/doc-good.texi: tests/qapi-schema/doc-good.test.texi @@ -1169,7 +1171,7 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) # Consolidated targets .PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean -check-qapi-schema: $(patsubst %,check-%, $(check-qapi-schema-y)) check-tests/qapi-schema/doc-good.texi +check-qapi-schema: check-tests/qapi-schema/frontend check-tests/qapi-schema/doc-good.texi check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS)) check-block: $(patsubst %,check-%, $(check-block-y)) check: check-block check-qapi-schema check-unit check-softfloat check-qtest check-decodetree diff --git a/tests/qapi-schema/allow-preconfig-test.err b/tests/qapi-schema/allow-preconfig-test.err index 2a4e6ce663..3cd672bc61 100644 --- a/tests/qapi-schema/allow-preconfig-test.err +++ b/tests/qapi-schema/allow-preconfig-test.err @@ -1,2 +1,2 @@ -tests/qapi-schema/allow-preconfig-test.json: In command 'allow-preconfig-test': -tests/qapi-schema/allow-preconfig-test.json:2: flag 'allow-preconfig' may only use true value +allow-preconfig-test.json: In command 'allow-preconfig-test': +allow-preconfig-test.json:2: flag 'allow-preconfig' may only use true value diff --git a/tests/qapi-schema/allow-preconfig-test.exit b/tests/qapi-schema/allow-preconfig-test.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/allow-preconfig-test.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-any.err b/tests/qapi-schema/alternate-any.err index 03aaf29506..baeb3f66d1 100644 --- a/tests/qapi-schema/alternate-any.err +++ b/tests/qapi-schema/alternate-any.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-any.json: In alternate 'Alt': -tests/qapi-schema/alternate-any.json:2: branch 'one' cannot use built-in type 'any' +alternate-any.json: In alternate 'Alt': +alternate-any.json:2: branch 'one' cannot use built-in type 'any' diff --git a/tests/qapi-schema/alternate-any.exit b/tests/qapi-schema/alternate-any.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-any.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-array.err b/tests/qapi-schema/alternate-array.err index dfbe3ee998..b1aa1f4e8d 100644 --- a/tests/qapi-schema/alternate-array.err +++ b/tests/qapi-schema/alternate-array.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-array.json: In alternate 'Alt': -tests/qapi-schema/alternate-array.json:5: 'data' member 'two' cannot be an array +alternate-array.json: In alternate 'Alt': +alternate-array.json:5: 'data' member 'two' cannot be an array diff --git a/tests/qapi-schema/alternate-array.exit b/tests/qapi-schema/alternate-array.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-array.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err index 04cea97e5c..31ebe56bbf 100644 --- a/tests/qapi-schema/alternate-base.err +++ b/tests/qapi-schema/alternate-base.err @@ -1,3 +1,3 @@ -tests/qapi-schema/alternate-base.json: In alternate 'Alt': -tests/qapi-schema/alternate-base.json:4: alternate has unknown key 'base' +alternate-base.json: In alternate 'Alt': +alternate-base.json:4: alternate has unknown key 'base' Valid keys are 'alternate', 'data', 'if'. diff --git a/tests/qapi-schema/alternate-base.exit b/tests/qapi-schema/alternate-base.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-base.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-branch-if-invalid.err b/tests/qapi-schema/alternate-branch-if-invalid.err index 6c68e5a922..d384929c51 100644 --- a/tests/qapi-schema/alternate-branch-if-invalid.err +++ b/tests/qapi-schema/alternate-branch-if-invalid.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-branch-if-invalid.json: In alternate 'Alt': -tests/qapi-schema/alternate-branch-if-invalid.json:2: 'if' condition ' ' of 'data' member 'branch' makes no sense +alternate-branch-if-invalid.json: In alternate 'Alt': +alternate-branch-if-invalid.json:2: 'if' condition ' ' of 'data' member 'branch' makes no sense diff --git a/tests/qapi-schema/alternate-branch-if-invalid.exit b/tests/qapi-schema/alternate-branch-if-invalid.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-branch-if-invalid.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-clash.err b/tests/qapi-schema/alternate-clash.err index 73a52d69d1..f58b977f7b 100644 --- a/tests/qapi-schema/alternate-clash.err +++ b/tests/qapi-schema/alternate-clash.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-clash.json: In alternate 'Alt1': -tests/qapi-schema/alternate-clash.json:7: branch 'a_b' collides with branch 'a-b' +alternate-clash.json: In alternate 'Alt1': +alternate-clash.json:7: branch 'a_b' collides with branch 'a-b' diff --git a/tests/qapi-schema/alternate-clash.exit b/tests/qapi-schema/alternate-clash.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-clash.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-conflict-bool-string.err b/tests/qapi-schema/alternate-conflict-bool-string.err index f7513b9cbe..59ff5efa87 100644 --- a/tests/qapi-schema/alternate-conflict-bool-string.err +++ b/tests/qapi-schema/alternate-conflict-bool-string.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-conflict-bool-string.json: In alternate 'Alt': -tests/qapi-schema/alternate-conflict-bool-string.json:2: branch 'two' can't be distinguished from 'one' +alternate-conflict-bool-string.json: In alternate 'Alt': +alternate-conflict-bool-string.json:2: branch 'two' can't be distinguished from 'one' diff --git a/tests/qapi-schema/alternate-conflict-bool-string.exit b/tests/qapi-schema/alternate-conflict-bool-string.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-conflict-bool-string.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-conflict-dict.err b/tests/qapi-schema/alternate-conflict-dict.err index e5b42d04c9..d4970284ba 100644 --- a/tests/qapi-schema/alternate-conflict-dict.err +++ b/tests/qapi-schema/alternate-conflict-dict.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-conflict-dict.json: In alternate 'Alt': -tests/qapi-schema/alternate-conflict-dict.json:6: branch 'two' can't be distinguished from 'one' +alternate-conflict-dict.json: In alternate 'Alt': +alternate-conflict-dict.json:6: branch 'two' can't be distinguished from 'one' diff --git a/tests/qapi-schema/alternate-conflict-dict.exit b/tests/qapi-schema/alternate-conflict-dict.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-conflict-dict.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.err b/tests/qapi-schema/alternate-conflict-enum-bool.err index 3d23aeba51..5f35855274 100644 --- a/tests/qapi-schema/alternate-conflict-enum-bool.err +++ b/tests/qapi-schema/alternate-conflict-enum-bool.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-conflict-enum-bool.json: In alternate 'Alt': -tests/qapi-schema/alternate-conflict-enum-bool.json:4: branch 'two' can't be distinguished from 'one' +alternate-conflict-enum-bool.json: In alternate 'Alt': +alternate-conflict-enum-bool.json:4: branch 'two' can't be distinguished from 'one' diff --git a/tests/qapi-schema/alternate-conflict-enum-bool.exit b/tests/qapi-schema/alternate-conflict-enum-bool.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-conflict-enum-bool.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-conflict-enum-int.err b/tests/qapi-schema/alternate-conflict-enum-int.err index b72768caa4..6a6d156664 100644 --- a/tests/qapi-schema/alternate-conflict-enum-int.err +++ b/tests/qapi-schema/alternate-conflict-enum-int.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-conflict-enum-int.json: In alternate 'Alt': -tests/qapi-schema/alternate-conflict-enum-int.json:4: branch 'two' can't be distinguished from 'one' +alternate-conflict-enum-int.json: In alternate 'Alt': +alternate-conflict-enum-int.json:4: branch 'two' can't be distinguished from 'one' diff --git a/tests/qapi-schema/alternate-conflict-enum-int.exit b/tests/qapi-schema/alternate-conflict-enum-int.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-conflict-enum-int.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-conflict-num-string.err b/tests/qapi-schema/alternate-conflict-num-string.err index b8a2bb1829..38c805ea1f 100644 --- a/tests/qapi-schema/alternate-conflict-num-string.err +++ b/tests/qapi-schema/alternate-conflict-num-string.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-conflict-num-string.json: In alternate 'Alt': -tests/qapi-schema/alternate-conflict-num-string.json:2: branch 'two' can't be distinguished from 'one' +alternate-conflict-num-string.json: In alternate 'Alt': +alternate-conflict-num-string.json:2: branch 'two' can't be distinguished from 'one' diff --git a/tests/qapi-schema/alternate-conflict-num-string.exit b/tests/qapi-schema/alternate-conflict-num-string.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-conflict-num-string.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-conflict-string.err b/tests/qapi-schema/alternate-conflict-string.err index 3edec51911..2fa08193db 100644 --- a/tests/qapi-schema/alternate-conflict-string.err +++ b/tests/qapi-schema/alternate-conflict-string.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-conflict-string.json: In alternate 'Alt': -tests/qapi-schema/alternate-conflict-string.json:2: branch 'two' can't be distinguished from 'one' +alternate-conflict-string.json: In alternate 'Alt': +alternate-conflict-string.json:2: branch 'two' can't be distinguished from 'one' diff --git a/tests/qapi-schema/alternate-conflict-string.exit b/tests/qapi-schema/alternate-conflict-string.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-conflict-string.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-empty.err b/tests/qapi-schema/alternate-empty.err index 908c309518..c6f6401d18 100644 --- a/tests/qapi-schema/alternate-empty.err +++ b/tests/qapi-schema/alternate-empty.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-empty.json: In alternate 'Alt': -tests/qapi-schema/alternate-empty.json:2: 'data' must not be empty +alternate-empty.json: In alternate 'Alt': +alternate-empty.json:2: 'data' must not be empty diff --git a/tests/qapi-schema/alternate-empty.exit b/tests/qapi-schema/alternate-empty.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-empty.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-invalid-dict.err b/tests/qapi-schema/alternate-invalid-dict.err index d6a18a294b..e9e5025226 100644 --- a/tests/qapi-schema/alternate-invalid-dict.err +++ b/tests/qapi-schema/alternate-invalid-dict.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-invalid-dict.json: In alternate 'Alt': -tests/qapi-schema/alternate-invalid-dict.json:2: 'data' member 'two' misses key 'type' +alternate-invalid-dict.json: In alternate 'Alt': +alternate-invalid-dict.json:2: 'data' member 'two' misses key 'type' diff --git a/tests/qapi-schema/alternate-invalid-dict.exit b/tests/qapi-schema/alternate-invalid-dict.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-invalid-dict.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-nested.err b/tests/qapi-schema/alternate-nested.err index cd7a076ce5..3ae9cd2f11 100644 --- a/tests/qapi-schema/alternate-nested.err +++ b/tests/qapi-schema/alternate-nested.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-nested.json: In alternate 'Alt2': -tests/qapi-schema/alternate-nested.json:4: branch 'nested' cannot use alternate type 'Alt1' +alternate-nested.json: In alternate 'Alt2': +alternate-nested.json:4: branch 'nested' cannot use alternate type 'Alt1' diff --git a/tests/qapi-schema/alternate-nested.exit b/tests/qapi-schema/alternate-nested.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-nested.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/alternate-unknown.err b/tests/qapi-schema/alternate-unknown.err index df05860bba..17fec1cd17 100644 --- a/tests/qapi-schema/alternate-unknown.err +++ b/tests/qapi-schema/alternate-unknown.err @@ -1,2 +1,2 @@ -tests/qapi-schema/alternate-unknown.json: In alternate 'Alt': -tests/qapi-schema/alternate-unknown.json:2: branch 'unknown' uses unknown type 'MissingType' +alternate-unknown.json: In alternate 'Alt': +alternate-unknown.json:2: branch 'unknown' uses unknown type 'MissingType' diff --git a/tests/qapi-schema/alternate-unknown.exit b/tests/qapi-schema/alternate-unknown.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/alternate-unknown.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-alternate.err b/tests/qapi-schema/args-alternate.err index 852b81b89c..b1530aa610 100644 --- a/tests/qapi-schema/args-alternate.err +++ b/tests/qapi-schema/args-alternate.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-alternate.json: In command 'oops': -tests/qapi-schema/args-alternate.json:3: command's 'data' cannot take alternate type 'Alt' +args-alternate.json: In command 'oops': +args-alternate.json:3: command's 'data' cannot take alternate type 'Alt' diff --git a/tests/qapi-schema/args-alternate.exit b/tests/qapi-schema/args-alternate.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-alternate.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-any.err b/tests/qapi-schema/args-any.err index 04e11df29f..4b60560247 100644 --- a/tests/qapi-schema/args-any.err +++ b/tests/qapi-schema/args-any.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-any.json: In command 'oops': -tests/qapi-schema/args-any.json:2: command's 'data' cannot take built-in type 'any' +args-any.json: In command 'oops': +args-any.json:2: command's 'data' cannot take built-in type 'any' diff --git a/tests/qapi-schema/args-any.exit b/tests/qapi-schema/args-any.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-any.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-array-empty.err b/tests/qapi-schema/args-array-empty.err index c7d367730e..181222296e 100644 --- a/tests/qapi-schema/args-array-empty.err +++ b/tests/qapi-schema/args-array-empty.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-array-empty.json: In command 'oops': -tests/qapi-schema/args-array-empty.json:2: 'data' member 'empty': array type must contain single type name +args-array-empty.json: In command 'oops': +args-array-empty.json:2: 'data' member 'empty': array type must contain single type name diff --git a/tests/qapi-schema/args-array-empty.exit b/tests/qapi-schema/args-array-empty.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-array-empty.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-array-unknown.err b/tests/qapi-schema/args-array-unknown.err index 218fc4bf9a..cbc598e4ff 100644 --- a/tests/qapi-schema/args-array-unknown.err +++ b/tests/qapi-schema/args-array-unknown.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-array-unknown.json: In command 'oops': -tests/qapi-schema/args-array-unknown.json:2: command uses unknown type 'NoSuchType' +args-array-unknown.json: In command 'oops': +args-array-unknown.json:2: command uses unknown type 'NoSuchType' diff --git a/tests/qapi-schema/args-array-unknown.exit b/tests/qapi-schema/args-array-unknown.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-array-unknown.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-bad-boxed.err b/tests/qapi-schema/args-bad-boxed.err index 31d39038fc..361276eb29 100644 --- a/tests/qapi-schema/args-bad-boxed.err +++ b/tests/qapi-schema/args-bad-boxed.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-bad-boxed.json: In command 'foo': -tests/qapi-schema/args-bad-boxed.json:2: flag 'boxed' may only use true value +args-bad-boxed.json: In command 'foo': +args-bad-boxed.json:2: flag 'boxed' may only use true value diff --git a/tests/qapi-schema/args-bad-boxed.exit b/tests/qapi-schema/args-bad-boxed.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-bad-boxed.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-boxed-anon.err b/tests/qapi-schema/args-boxed-anon.err index 5e0c2979b7..a89af75bef 100644 --- a/tests/qapi-schema/args-boxed-anon.err +++ b/tests/qapi-schema/args-boxed-anon.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-boxed-anon.json: In command 'foo': -tests/qapi-schema/args-boxed-anon.json:2: 'data' should be a type name +args-boxed-anon.json: In command 'foo': +args-boxed-anon.json:2: 'data' should be a type name diff --git a/tests/qapi-schema/args-boxed-anon.exit b/tests/qapi-schema/args-boxed-anon.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-boxed-anon.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-boxed-string.err b/tests/qapi-schema/args-boxed-string.err index dc2b00f217..415c1148a9 100644 --- a/tests/qapi-schema/args-boxed-string.err +++ b/tests/qapi-schema/args-boxed-string.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-boxed-string.json: In command 'foo': -tests/qapi-schema/args-boxed-string.json:2: command's 'data' cannot take built-in type 'str' +args-boxed-string.json: In command 'foo': +args-boxed-string.json:2: command's 'data' cannot take built-in type 'str' diff --git a/tests/qapi-schema/args-boxed-string.exit b/tests/qapi-schema/args-boxed-string.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-boxed-string.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-int.err b/tests/qapi-schema/args-int.err index 81b6f86b66..a2331c5543 100644 --- a/tests/qapi-schema/args-int.err +++ b/tests/qapi-schema/args-int.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-int.json: In command 'oops': -tests/qapi-schema/args-int.json:2: command's 'data' cannot take built-in type 'int' +args-int.json: In command 'oops': +args-int.json:2: command's 'data' cannot take built-in type 'int' diff --git a/tests/qapi-schema/args-int.exit b/tests/qapi-schema/args-int.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-int.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-invalid.err b/tests/qapi-schema/args-invalid.err index c4971e1399..1527c4a48d 100644 --- a/tests/qapi-schema/args-invalid.err +++ b/tests/qapi-schema/args-invalid.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-invalid.json: In command 'foo': -tests/qapi-schema/args-invalid.json:1: 'data' should be an object or type name +args-invalid.json: In command 'foo': +args-invalid.json:1: 'data' should be an object or type name diff --git a/tests/qapi-schema/args-invalid.exit b/tests/qapi-schema/args-invalid.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-invalid.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-member-array-bad.err b/tests/qapi-schema/args-member-array-bad.err index f95ac01372..194a3052d2 100644 --- a/tests/qapi-schema/args-member-array-bad.err +++ b/tests/qapi-schema/args-member-array-bad.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-member-array-bad.json: In command 'oops': -tests/qapi-schema/args-member-array-bad.json:2: 'data' member 'member': array type must contain single type name +args-member-array-bad.json: In command 'oops': +args-member-array-bad.json:2: 'data' member 'member': array type must contain single type name diff --git a/tests/qapi-schema/args-member-array-bad.exit b/tests/qapi-schema/args-member-array-bad.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-member-array-bad.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-member-case.err b/tests/qapi-schema/args-member-case.err index 3ecd276040..4f33dbbc38 100644 --- a/tests/qapi-schema/args-member-case.err +++ b/tests/qapi-schema/args-member-case.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-member-case.json: In command 'no-way-this-will-get-whitelisted': -tests/qapi-schema/args-member-case.json:2: 'data' member 'Arg' uses uppercase in name +args-member-case.json: In command 'no-way-this-will-get-whitelisted': +args-member-case.json:2: 'data' member 'Arg' uses uppercase in name diff --git a/tests/qapi-schema/args-member-case.exit b/tests/qapi-schema/args-member-case.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-member-case.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-member-unknown.err b/tests/qapi-schema/args-member-unknown.err index 0626e1209d..96b6e5d289 100644 --- a/tests/qapi-schema/args-member-unknown.err +++ b/tests/qapi-schema/args-member-unknown.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-member-unknown.json: In command 'oops': -tests/qapi-schema/args-member-unknown.json:2: parameter 'member' uses unknown type 'NoSuchType' +args-member-unknown.json: In command 'oops': +args-member-unknown.json:2: parameter 'member' uses unknown type 'NoSuchType' diff --git a/tests/qapi-schema/args-member-unknown.exit b/tests/qapi-schema/args-member-unknown.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-member-unknown.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-name-clash.err b/tests/qapi-schema/args-name-clash.err index c5916a80fb..3e04817bc0 100644 --- a/tests/qapi-schema/args-name-clash.err +++ b/tests/qapi-schema/args-name-clash.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-name-clash.json: In command 'oops': -tests/qapi-schema/args-name-clash.json:4: parameter 'a_b' collides with parameter 'a-b' +args-name-clash.json: In command 'oops': +args-name-clash.json:4: parameter 'a_b' collides with parameter 'a-b' diff --git a/tests/qapi-schema/args-name-clash.exit b/tests/qapi-schema/args-name-clash.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-name-clash.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-union.err b/tests/qapi-schema/args-union.err index 3a77b2863f..4bf4955027 100644 --- a/tests/qapi-schema/args-union.err +++ b/tests/qapi-schema/args-union.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-union.json: In command 'oops': -tests/qapi-schema/args-union.json:3: command's 'data' can take union type 'Uni' only with 'boxed': true +args-union.json: In command 'oops': +args-union.json:3: command's 'data' can take union type 'Uni' only with 'boxed': true diff --git a/tests/qapi-schema/args-union.exit b/tests/qapi-schema/args-union.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-union.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/args-unknown.err b/tests/qapi-schema/args-unknown.err index 6857d6bf48..44bf8706ae 100644 --- a/tests/qapi-schema/args-unknown.err +++ b/tests/qapi-schema/args-unknown.err @@ -1,2 +1,2 @@ -tests/qapi-schema/args-unknown.json: In command 'oops': -tests/qapi-schema/args-unknown.json:2: command's 'data' uses unknown type 'NoSuchType' +args-unknown.json: In command 'oops': +args-unknown.json:2: command's 'data' uses unknown type 'NoSuchType' diff --git a/tests/qapi-schema/args-unknown.exit b/tests/qapi-schema/args-unknown.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/args-unknown.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/bad-base.err b/tests/qapi-schema/bad-base.err index 039678a364..61a1efc2c0 100644 --- a/tests/qapi-schema/bad-base.err +++ b/tests/qapi-schema/bad-base.err @@ -1,2 +1,2 @@ -tests/qapi-schema/bad-base.json: In struct 'MyType': -tests/qapi-schema/bad-base.json:3: 'base' requires a struct type, union type 'Union' isn't +bad-base.json: In struct 'MyType': +bad-base.json:3: 'base' requires a struct type, union type 'Union' isn't diff --git a/tests/qapi-schema/bad-base.exit b/tests/qapi-schema/bad-base.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/bad-base.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/bad-data.err b/tests/qapi-schema/bad-data.err index 5227bdce7e..7991c8898d 100644 --- a/tests/qapi-schema/bad-data.err +++ b/tests/qapi-schema/bad-data.err @@ -1,2 +1,2 @@ -tests/qapi-schema/bad-data.json: In command 'oops': -tests/qapi-schema/bad-data.json:2: 'data' cannot be an array +bad-data.json: In command 'oops': +bad-data.json:2: 'data' cannot be an array diff --git a/tests/qapi-schema/bad-data.exit b/tests/qapi-schema/bad-data.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/bad-data.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/bad-ident.err b/tests/qapi-schema/bad-ident.err index ad38a679c7..263fcd3ecd 100644 --- a/tests/qapi-schema/bad-ident.err +++ b/tests/qapi-schema/bad-ident.err @@ -1,2 +1,2 @@ -tests/qapi-schema/bad-ident.json: In struct '*oops': -tests/qapi-schema/bad-ident.json:2: struct has an invalid name +bad-ident.json: In struct '*oops': +bad-ident.json:2: struct has an invalid name diff --git a/tests/qapi-schema/bad-ident.exit b/tests/qapi-schema/bad-ident.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/bad-ident.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/bad-if-empty-list.err b/tests/qapi-schema/bad-if-empty-list.err index 517519f500..a946376d06 100644 --- a/tests/qapi-schema/bad-if-empty-list.err +++ b/tests/qapi-schema/bad-if-empty-list.err @@ -1,2 +1,2 @@ -tests/qapi-schema/bad-if-empty-list.json: In struct 'TestIfStruct': -tests/qapi-schema/bad-if-empty-list.json:2: 'if' condition [] of struct is useless +bad-if-empty-list.json: In struct 'TestIfStruct': +bad-if-empty-list.json:2: 'if' condition [] of struct is useless diff --git a/tests/qapi-schema/bad-if-empty-list.exit b/tests/qapi-schema/bad-if-empty-list.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/bad-if-empty-list.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/bad-if-empty.err b/tests/qapi-schema/bad-if-empty.err index 5f1767388e..a0f3effefb 100644 --- a/tests/qapi-schema/bad-if-empty.err +++ b/tests/qapi-schema/bad-if-empty.err @@ -1,2 +1,2 @@ -tests/qapi-schema/bad-if-empty.json: In struct 'TestIfStruct': -tests/qapi-schema/bad-if-empty.json:2: 'if' condition '' of struct makes no sense +bad-if-empty.json: In struct 'TestIfStruct': +bad-if-empty.json:2: 'if' condition '' of struct makes no sense diff --git a/tests/qapi-schema/bad-if-empty.exit b/tests/qapi-schema/bad-if-empty.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/bad-if-empty.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/bad-if-list.err b/tests/qapi-schema/bad-if-list.err index e5d72b2f39..c462f11b90 100644 --- a/tests/qapi-schema/bad-if-list.err +++ b/tests/qapi-schema/bad-if-list.err @@ -1,2 +1,2 @@ -tests/qapi-schema/bad-if-list.json: In struct 'TestIfStruct': -tests/qapi-schema/bad-if-list.json:2: 'if' condition ' ' of struct makes no sense +bad-if-list.json: In struct 'TestIfStruct': +bad-if-list.json:2: 'if' condition ' ' of struct makes no sense diff --git a/tests/qapi-schema/bad-if-list.exit b/tests/qapi-schema/bad-if-list.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/bad-if-list.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/bad-if.err b/tests/qapi-schema/bad-if.err index 65d8efd7e4..f83dee65da 100644 --- a/tests/qapi-schema/bad-if.err +++ b/tests/qapi-schema/bad-if.err @@ -1,2 +1,2 @@ -tests/qapi-schema/bad-if.json: In struct 'TestIfStruct': -tests/qapi-schema/bad-if.json:2: 'if' condition of struct must be a string or a list of strings +bad-if.json: In struct 'TestIfStruct': +bad-if.json:2: 'if' condition of struct must be a string or a list of strings diff --git a/tests/qapi-schema/bad-if.exit b/tests/qapi-schema/bad-if.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/bad-if.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/bad-type-bool.err b/tests/qapi-schema/bad-type-bool.err index 984a77c4e3..42ccc18988 100644 --- a/tests/qapi-schema/bad-type-bool.err +++ b/tests/qapi-schema/bad-type-bool.err @@ -1 +1 @@ -tests/qapi-schema/bad-type-bool.json:2: 'struct' requires a string name +bad-type-bool.json:2: 'struct' requires a string name diff --git a/tests/qapi-schema/bad-type-bool.exit b/tests/qapi-schema/bad-type-bool.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/bad-type-bool.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/bad-type-dict.err b/tests/qapi-schema/bad-type-dict.err index e83b8cfb41..3d97cd3f0a 100644 --- a/tests/qapi-schema/bad-type-dict.err +++ b/tests/qapi-schema/bad-type-dict.err @@ -1 +1 @@ -tests/qapi-schema/bad-type-dict.json:2: 'command' requires a string name +bad-type-dict.json:2: 'command' requires a string name diff --git a/tests/qapi-schema/bad-type-dict.exit b/tests/qapi-schema/bad-type-dict.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/bad-type-dict.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/bad-type-int.err b/tests/qapi-schema/bad-type-int.err index 7f5916ea29..90d06bd94d 100644 --- a/tests/qapi-schema/bad-type-int.err +++ b/tests/qapi-schema/bad-type-int.err @@ -1 +1 @@ -tests/qapi-schema/bad-type-int.json:3:13: stray '123' +bad-type-int.json:3:13: stray '123' diff --git a/tests/qapi-schema/bad-type-int.exit b/tests/qapi-schema/bad-type-int.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/bad-type-int.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/base-cycle-direct.err b/tests/qapi-schema/base-cycle-direct.err index 233e4b8952..4ea6c5064e 100644 --- a/tests/qapi-schema/base-cycle-direct.err +++ b/tests/qapi-schema/base-cycle-direct.err @@ -1,2 +1,2 @@ -tests/qapi-schema/base-cycle-direct.json: In struct 'Loopy': -tests/qapi-schema/base-cycle-direct.json:2: object Loopy contains itself +base-cycle-direct.json: In struct 'Loopy': +base-cycle-direct.json:2: object Loopy contains itself diff --git a/tests/qapi-schema/base-cycle-direct.exit b/tests/qapi-schema/base-cycle-direct.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/base-cycle-direct.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/base-cycle-indirect.err b/tests/qapi-schema/base-cycle-indirect.err index 4472f30ba1..9b5e7ec174 100644 --- a/tests/qapi-schema/base-cycle-indirect.err +++ b/tests/qapi-schema/base-cycle-indirect.err @@ -1,2 +1,2 @@ -tests/qapi-schema/base-cycle-indirect.json: In struct 'Base1': -tests/qapi-schema/base-cycle-indirect.json:2: object Base1 contains itself +base-cycle-indirect.json: In struct 'Base1': +base-cycle-indirect.json:2: object Base1 contains itself diff --git a/tests/qapi-schema/base-cycle-indirect.exit b/tests/qapi-schema/base-cycle-indirect.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/base-cycle-indirect.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/command-int.err b/tests/qapi-schema/command-int.err index 3523d50a79..df0e5f5a57 100644 --- a/tests/qapi-schema/command-int.err +++ b/tests/qapi-schema/command-int.err @@ -1,2 +1,2 @@ -tests/qapi-schema/command-int.json: In command 'int': -tests/qapi-schema/command-int.json:2: built-in type 'int' is already defined +command-int.json: In command 'int': +command-int.json:2: built-in type 'int' is already defined diff --git a/tests/qapi-schema/command-int.exit b/tests/qapi-schema/command-int.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/command-int.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/comments.exit b/tests/qapi-schema/comments.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/comments.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/doc-bad-alternate-member.err b/tests/qapi-schema/doc-bad-alternate-member.err index 19a1ffd76e..a1c282f935 100644 --- a/tests/qapi-schema/doc-bad-alternate-member.err +++ b/tests/qapi-schema/doc-bad-alternate-member.err @@ -1 +1 @@ -tests/qapi-schema/doc-bad-alternate-member.json:3: the following documented members are not in the declaration: aa, bb +doc-bad-alternate-member.json:3: the following documented members are not in the declaration: aa, bb diff --git a/tests/qapi-schema/doc-bad-alternate-member.exit b/tests/qapi-schema/doc-bad-alternate-member.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-bad-alternate-member.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-bad-command-arg.err b/tests/qapi-schema/doc-bad-command-arg.err index 6962ebed69..153ea0330a 100644 --- a/tests/qapi-schema/doc-bad-command-arg.err +++ b/tests/qapi-schema/doc-bad-command-arg.err @@ -1 +1 @@ -tests/qapi-schema/doc-bad-command-arg.json:3: the following documented members are not in the declaration: b +doc-bad-command-arg.json:3: the following documented members are not in the declaration: b diff --git a/tests/qapi-schema/doc-bad-command-arg.exit b/tests/qapi-schema/doc-bad-command-arg.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-bad-command-arg.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-bad-section.exit b/tests/qapi-schema/doc-bad-section.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/doc-bad-section.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/doc-bad-symbol.err b/tests/qapi-schema/doc-bad-symbol.err index b23e99d160..166c8dcc18 100644 --- a/tests/qapi-schema/doc-bad-symbol.err +++ b/tests/qapi-schema/doc-bad-symbol.err @@ -1,2 +1,2 @@ -tests/qapi-schema/doc-bad-symbol.json: In command 'foo': -tests/qapi-schema/doc-bad-symbol.json:6: documentation comment is for 'food' +doc-bad-symbol.json: In command 'foo': +doc-bad-symbol.json:6: documentation comment is for 'food' diff --git a/tests/qapi-schema/doc-bad-symbol.exit b/tests/qapi-schema/doc-bad-symbol.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-bad-symbol.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-bad-union-member.err b/tests/qapi-schema/doc-bad-union-member.err index da3cd806e3..8b9d36eab1 100644 --- a/tests/qapi-schema/doc-bad-union-member.err +++ b/tests/qapi-schema/doc-bad-union-member.err @@ -1 +1 @@ -tests/qapi-schema/doc-bad-union-member.json:3: the following documented members are not in the declaration: a, b +doc-bad-union-member.json:3: the following documented members are not in the declaration: a, b diff --git a/tests/qapi-schema/doc-bad-union-member.exit b/tests/qapi-schema/doc-bad-union-member.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-bad-union-member.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-before-include.err b/tests/qapi-schema/doc-before-include.err index e5566f11e9..ae23ea2f57 100644 --- a/tests/qapi-schema/doc-before-include.err +++ b/tests/qapi-schema/doc-before-include.err @@ -1 +1 @@ -tests/qapi-schema/doc-before-include.json:3: documentation for 'foo' is not followed by the definition +doc-before-include.json:3: documentation for 'foo' is not followed by the definition diff --git a/tests/qapi-schema/doc-before-include.exit b/tests/qapi-schema/doc-before-include.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-before-include.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-before-pragma.err b/tests/qapi-schema/doc-before-pragma.err index 8a97ebb578..c776252e07 100644 --- a/tests/qapi-schema/doc-before-pragma.err +++ b/tests/qapi-schema/doc-before-pragma.err @@ -1 +1 @@ -tests/qapi-schema/doc-before-pragma.json:3: documentation for 'foo' is not followed by the definition +doc-before-pragma.json:3: documentation for 'foo' is not followed by the definition diff --git a/tests/qapi-schema/doc-before-pragma.exit b/tests/qapi-schema/doc-before-pragma.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-before-pragma.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-duplicated-arg.err b/tests/qapi-schema/doc-duplicated-arg.err index 1c3f8e0a54..0d0d777a1f 100644 --- a/tests/qapi-schema/doc-duplicated-arg.err +++ b/tests/qapi-schema/doc-duplicated-arg.err @@ -1 +1 @@ -tests/qapi-schema/doc-duplicated-arg.json:6:1: 'a' parameter name duplicated +doc-duplicated-arg.json:6:1: 'a' parameter name duplicated diff --git a/tests/qapi-schema/doc-duplicated-arg.exit b/tests/qapi-schema/doc-duplicated-arg.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-duplicated-arg.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-duplicated-return.err b/tests/qapi-schema/doc-duplicated-return.err index 7631933093..fe97e3db8d 100644 --- a/tests/qapi-schema/doc-duplicated-return.err +++ b/tests/qapi-schema/doc-duplicated-return.err @@ -1 +1 @@ -tests/qapi-schema/doc-duplicated-return.json:7:1: duplicated 'Returns' section +doc-duplicated-return.json:7:1: duplicated 'Returns' section diff --git a/tests/qapi-schema/doc-duplicated-return.exit b/tests/qapi-schema/doc-duplicated-return.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-duplicated-return.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-duplicated-since.err b/tests/qapi-schema/doc-duplicated-since.err index 5ee15ae2a1..abca141a2c 100644 --- a/tests/qapi-schema/doc-duplicated-since.err +++ b/tests/qapi-schema/doc-duplicated-since.err @@ -1 +1 @@ -tests/qapi-schema/doc-duplicated-since.json:7:1: duplicated 'Since' section +doc-duplicated-since.json:7:1: duplicated 'Since' section diff --git a/tests/qapi-schema/doc-duplicated-since.exit b/tests/qapi-schema/doc-duplicated-since.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-duplicated-since.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-empty-arg.err b/tests/qapi-schema/doc-empty-arg.err index 3c78a37ae1..2d0f35f310 100644 --- a/tests/qapi-schema/doc-empty-arg.err +++ b/tests/qapi-schema/doc-empty-arg.err @@ -1 +1 @@ -tests/qapi-schema/doc-empty-arg.json:5:1: invalid parameter name +doc-empty-arg.json:5:1: invalid parameter name diff --git a/tests/qapi-schema/doc-empty-arg.exit b/tests/qapi-schema/doc-empty-arg.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-empty-arg.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-empty-section.err b/tests/qapi-schema/doc-empty-section.err index f6586c566f..ba7ba70125 100644 --- a/tests/qapi-schema/doc-empty-section.err +++ b/tests/qapi-schema/doc-empty-section.err @@ -1 +1 @@ -tests/qapi-schema/doc-empty-section.json:7:1: empty doc section 'Note' +doc-empty-section.json:7:1: empty doc section 'Note' diff --git a/tests/qapi-schema/doc-empty-section.exit b/tests/qapi-schema/doc-empty-section.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-empty-section.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-empty-symbol.err b/tests/qapi-schema/doc-empty-symbol.err index 2dcddca7f6..81b90e882a 100644 --- a/tests/qapi-schema/doc-empty-symbol.err +++ b/tests/qapi-schema/doc-empty-symbol.err @@ -1 +1 @@ -tests/qapi-schema/doc-empty-symbol.json:4:1: invalid name +doc-empty-symbol.json:4:1: invalid name diff --git a/tests/qapi-schema/doc-empty-symbol.exit b/tests/qapi-schema/doc-empty-symbol.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-empty-symbol.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-good.exit b/tests/qapi-schema/doc-good.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/doc-good.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index f7fb48af38..7dc21e58a3 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -71,8 +71,12 @@ # A paragraph # # Another paragraph (but no @var: line) +# +# Features: +# @variant1-feat: a feature ## { 'struct': 'Variant1', + 'features': [ 'variant1-feat' ], 'data': { 'var1': { 'type': 'str', 'if': 'defined(IFSTR)' } } } ## @@ -104,6 +108,10 @@ # # @arg2: the second # argument +# +# Features: +# @cmd-feat1: a feature +# @cmd-feat2: another feature # Note: @arg3 is undocumented # Returns: @Object # TODO: frobnicate @@ -123,11 +131,15 @@ ## { 'command': 'cmd', 'data': { 'arg1': 'int', '*arg2': 'str', 'arg3': 'bool' }, - 'returns': 'Object' } + 'returns': 'Object', + 'features': [ 'cmd-feat1', 'cmd-feat2' ] } ## # @cmd-boxed: # If you're bored enough to read this, go see a video of boxed cats +# Features: +# @cmd-feat1: a feature +# @cmd-feat2: another feature # Example: # # -> in @@ -135,4 +147,5 @@ # <- out ## { 'command': 'cmd-boxed', 'boxed': true, - 'data': 'Object' } + 'data': 'Object', + 'features': [ 'cmd-feat1', 'cmd-feat2' ] } diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index d3bca343eb..f78fdef6a9 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -20,6 +20,7 @@ object Base object Variant1 member var1: str optional=False if ['defined(IFSTR)'] + feature variant1-feat object Variant2 object Object base Base @@ -46,9 +47,13 @@ object q_obj_cmd-arg member arg2: str optional=True member arg3: bool optional=False command cmd q_obj_cmd-arg -> Object - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False + feature cmd-feat1 + feature cmd-feat2 command cmd-boxed Object -> None - gen=True success_response=True boxed=True oob=False preconfig=False + gen=True success_response=True boxed=True oob=False preconfig=False + feature cmd-feat1 + feature cmd-feat2 doc freeform body= = Section diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi index 2526abc6d9..2ce8b883c9 100644 --- a/tests/qapi-schema/doc-good.texi +++ b/tests/qapi-schema/doc-good.texi @@ -122,6 +122,12 @@ Not documented @*@b{If:} @code{defined(IFSTR)} @end table +@b{Features:} +@table @asis +@item @code{variant1-feat} +a feature +@end table + @end deftp @@ -182,6 +188,14 @@ argument Not documented @end table +@b{Features:} +@table @asis +@item @code{cmd-feat1} +a feature +@item @code{cmd-feat2} +another feature +@end table + @b{Note:} @code{arg3} is undocumented @@ -227,6 +241,14 @@ If you're bored enough to read this, go see a video of boxed cats @b{Arguments:} the members of @code{Object} +@b{Features:} +@table @asis +@item @code{cmd-feat1} +a feature +@item @code{cmd-feat2} +another feature +@end table + @b{Example:} @example -> in diff --git a/tests/qapi-schema/doc-interleaved-section.err b/tests/qapi-schema/doc-interleaved-section.err index d373eabc55..715d58cd31 100644 --- a/tests/qapi-schema/doc-interleaved-section.err +++ b/tests/qapi-schema/doc-interleaved-section.err @@ -1 +1 @@ -tests/qapi-schema/doc-interleaved-section.json:15:1: '@foobar:' can't follow 'Note' section +doc-interleaved-section.json:15:1: '@foobar:' can't follow 'Note' section diff --git a/tests/qapi-schema/doc-interleaved-section.exit b/tests/qapi-schema/doc-interleaved-section.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-interleaved-section.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-invalid-end.err b/tests/qapi-schema/doc-invalid-end.err index 6345aa6a0f..919e4d317e 100644 --- a/tests/qapi-schema/doc-invalid-end.err +++ b/tests/qapi-schema/doc-invalid-end.err @@ -1 +1 @@ -tests/qapi-schema/doc-invalid-end.json:5:2: documentation comment must end with '##' +doc-invalid-end.json:5:2: documentation comment must end with '##' diff --git a/tests/qapi-schema/doc-invalid-end.exit b/tests/qapi-schema/doc-invalid-end.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-invalid-end.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-invalid-end2.err b/tests/qapi-schema/doc-invalid-end2.err index 13ead36fd2..d88a15cc8a 100644 --- a/tests/qapi-schema/doc-invalid-end2.err +++ b/tests/qapi-schema/doc-invalid-end2.err @@ -1 +1 @@ -tests/qapi-schema/doc-invalid-end2.json:5:1: junk after '##' at end of documentation comment +doc-invalid-end2.json:5:1: junk after '##' at end of documentation comment diff --git a/tests/qapi-schema/doc-invalid-end2.exit b/tests/qapi-schema/doc-invalid-end2.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-invalid-end2.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-invalid-return.err b/tests/qapi-schema/doc-invalid-return.err index 5aaba33bb4..2ad89c5941 100644 --- a/tests/qapi-schema/doc-invalid-return.err +++ b/tests/qapi-schema/doc-invalid-return.err @@ -1 +1 @@ -tests/qapi-schema/doc-invalid-return.json:3: 'Returns:' is only valid for commands +doc-invalid-return.json:3: 'Returns:' is only valid for commands diff --git a/tests/qapi-schema/doc-invalid-return.exit b/tests/qapi-schema/doc-invalid-return.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-invalid-return.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-invalid-section.err b/tests/qapi-schema/doc-invalid-section.err index bda93b44fd..d2d40e5704 100644 --- a/tests/qapi-schema/doc-invalid-section.err +++ b/tests/qapi-schema/doc-invalid-section.err @@ -1 +1 @@ -tests/qapi-schema/doc-invalid-section.json:5:1: '@note:' not allowed in free-form documentation +doc-invalid-section.json:5:1: '@note:' not allowed in free-form documentation diff --git a/tests/qapi-schema/doc-invalid-section.exit b/tests/qapi-schema/doc-invalid-section.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-invalid-section.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-invalid-start.err b/tests/qapi-schema/doc-invalid-start.err index dcaa9699d6..276c033600 100644 --- a/tests/qapi-schema/doc-invalid-start.err +++ b/tests/qapi-schema/doc-invalid-start.err @@ -1 +1 @@ -tests/qapi-schema/doc-invalid-start.json:3:1: junk after '##' at start of documentation comment +doc-invalid-start.json:3:1: junk after '##' at start of documentation comment diff --git a/tests/qapi-schema/doc-invalid-start.exit b/tests/qapi-schema/doc-invalid-start.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-invalid-start.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-missing-colon.err b/tests/qapi-schema/doc-missing-colon.err index 6fb5a380bd..cbcea00715 100644 --- a/tests/qapi-schema/doc-missing-colon.err +++ b/tests/qapi-schema/doc-missing-colon.err @@ -1 +1 @@ -tests/qapi-schema/doc-missing-colon.json:4:1: line should end with ':' +doc-missing-colon.json:4:1: line should end with ':' diff --git a/tests/qapi-schema/doc-missing-colon.exit b/tests/qapi-schema/doc-missing-colon.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-missing-colon.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-missing-expr.err b/tests/qapi-schema/doc-missing-expr.err index 622a37cc6c..c9b32a96fa 100644 --- a/tests/qapi-schema/doc-missing-expr.err +++ b/tests/qapi-schema/doc-missing-expr.err @@ -1 +1 @@ -tests/qapi-schema/doc-missing-expr.json:3: documentation for 'bar' is not followed by the definition +doc-missing-expr.json:3: documentation for 'bar' is not followed by the definition diff --git a/tests/qapi-schema/doc-missing-expr.exit b/tests/qapi-schema/doc-missing-expr.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-missing-expr.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-missing-space.err b/tests/qapi-schema/doc-missing-space.err index 1187ba12c4..350031d1d6 100644 --- a/tests/qapi-schema/doc-missing-space.err +++ b/tests/qapi-schema/doc-missing-space.err @@ -1 +1 @@ -tests/qapi-schema/doc-missing-space.json:5:1: missing space after # +doc-missing-space.json:5:1: missing space after # diff --git a/tests/qapi-schema/doc-missing-space.exit b/tests/qapi-schema/doc-missing-space.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-missing-space.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-missing.err b/tests/qapi-schema/doc-missing.err index 7fbf54ff65..b89d925bcc 100644 --- a/tests/qapi-schema/doc-missing.err +++ b/tests/qapi-schema/doc-missing.err @@ -1,2 +1,2 @@ -tests/qapi-schema/doc-missing.json: In command 'undocumented': -tests/qapi-schema/doc-missing.json:5: documentation comment required +doc-missing.json: In command 'undocumented': +doc-missing.json:5: documentation comment required diff --git a/tests/qapi-schema/doc-missing.exit b/tests/qapi-schema/doc-missing.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-missing.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/doc-no-symbol.err b/tests/qapi-schema/doc-no-symbol.err index 9a3057730c..1b4fa14a22 100644 --- a/tests/qapi-schema/doc-no-symbol.err +++ b/tests/qapi-schema/doc-no-symbol.err @@ -1 +1 @@ -tests/qapi-schema/doc-no-symbol.json:3: definition documentation required +doc-no-symbol.json:3: definition documentation required diff --git a/tests/qapi-schema/doc-no-symbol.exit b/tests/qapi-schema/doc-no-symbol.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/doc-no-symbol.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/double-type.err b/tests/qapi-schema/double-type.err index 23f88aae99..71fc4dbb52 100644 --- a/tests/qapi-schema/double-type.err +++ b/tests/qapi-schema/double-type.err @@ -1,3 +1,3 @@ -tests/qapi-schema/double-type.json: In struct 'bar': -tests/qapi-schema/double-type.json:2: struct has unknown key 'command' +double-type.json: In struct 'bar': +double-type.json:2: struct has unknown key 'command' Valid keys are 'base', 'data', 'features', 'if', 'struct'. diff --git a/tests/qapi-schema/double-type.exit b/tests/qapi-schema/double-type.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/double-type.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/duplicate-key.err b/tests/qapi-schema/duplicate-key.err index 7f34a34eb6..7ea8e95e8c 100644 --- a/tests/qapi-schema/duplicate-key.err +++ b/tests/qapi-schema/duplicate-key.err @@ -1 +1 @@ -tests/qapi-schema/duplicate-key.json:3:10: duplicate key 'key' +duplicate-key.json:3:10: duplicate key 'key' diff --git a/tests/qapi-schema/duplicate-key.exit b/tests/qapi-schema/duplicate-key.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/duplicate-key.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/empty.exit b/tests/qapi-schema/empty.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/empty.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/enum-bad-member.err b/tests/qapi-schema/enum-bad-member.err index 2b1b4f98d0..f75d8c56ff 100644 --- a/tests/qapi-schema/enum-bad-member.err +++ b/tests/qapi-schema/enum-bad-member.err @@ -1,2 +1,2 @@ -tests/qapi-schema/enum-bad-member.json: In enum 'MyEnum': -tests/qapi-schema/enum-bad-member.json:2: 'data' member requires a string name +enum-bad-member.json: In enum 'MyEnum': +enum-bad-member.json:2: 'data' member requires a string name diff --git a/tests/qapi-schema/enum-bad-member.exit b/tests/qapi-schema/enum-bad-member.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/enum-bad-member.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/enum-bad-name.err b/tests/qapi-schema/enum-bad-name.err index 3273a9808a..d26044b8e3 100644 --- a/tests/qapi-schema/enum-bad-name.err +++ b/tests/qapi-schema/enum-bad-name.err @@ -1,2 +1,2 @@ -tests/qapi-schema/enum-bad-name.json: In enum 'MyEnum': -tests/qapi-schema/enum-bad-name.json:3: 'data' member 'not\possible' has an invalid name +enum-bad-name.json: In enum 'MyEnum': +enum-bad-name.json:3: 'data' member 'not\possible' has an invalid name diff --git a/tests/qapi-schema/enum-bad-name.exit b/tests/qapi-schema/enum-bad-name.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/enum-bad-name.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/enum-bad-prefix.err b/tests/qapi-schema/enum-bad-prefix.err index 933e33aa18..7a2b35787e 100644 --- a/tests/qapi-schema/enum-bad-prefix.err +++ b/tests/qapi-schema/enum-bad-prefix.err @@ -1,2 +1,2 @@ -tests/qapi-schema/enum-bad-prefix.json: In enum 'MyEnum': -tests/qapi-schema/enum-bad-prefix.json:2: 'prefix' must be a string +enum-bad-prefix.json: In enum 'MyEnum': +enum-bad-prefix.json:2: 'prefix' must be a string diff --git a/tests/qapi-schema/enum-bad-prefix.exit b/tests/qapi-schema/enum-bad-prefix.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/enum-bad-prefix.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/enum-clash-member.err b/tests/qapi-schema/enum-clash-member.err index 84e02db82c..5986571427 100644 --- a/tests/qapi-schema/enum-clash-member.err +++ b/tests/qapi-schema/enum-clash-member.err @@ -1,2 +1,2 @@ -tests/qapi-schema/enum-clash-member.json: In enum 'MyEnum': -tests/qapi-schema/enum-clash-member.json:2: value 'one_two' collides with value 'one-two' +enum-clash-member.json: In enum 'MyEnum': +enum-clash-member.json:2: value 'one_two' collides with value 'one-two' diff --git a/tests/qapi-schema/enum-clash-member.exit b/tests/qapi-schema/enum-clash-member.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/enum-clash-member.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/enum-dict-member-unknown.err b/tests/qapi-schema/enum-dict-member-unknown.err index 5df0236343..f8617ea179 100644 --- a/tests/qapi-schema/enum-dict-member-unknown.err +++ b/tests/qapi-schema/enum-dict-member-unknown.err @@ -1,3 +1,3 @@ -tests/qapi-schema/enum-dict-member-unknown.json: In enum 'MyEnum': -tests/qapi-schema/enum-dict-member-unknown.json:2: 'data' member has unknown key 'bad-key' +enum-dict-member-unknown.json: In enum 'MyEnum': +enum-dict-member-unknown.json:2: 'data' member has unknown key 'bad-key' Valid keys are 'if', 'name'. diff --git a/tests/qapi-schema/enum-dict-member-unknown.exit b/tests/qapi-schema/enum-dict-member-unknown.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/enum-dict-member-unknown.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/enum-if-invalid.err b/tests/qapi-schema/enum-if-invalid.err index 30c1f0e91c..0556dc967b 100644 --- a/tests/qapi-schema/enum-if-invalid.err +++ b/tests/qapi-schema/enum-if-invalid.err @@ -1,2 +1,2 @@ -tests/qapi-schema/enum-if-invalid.json: In enum 'TestIfEnum': -tests/qapi-schema/enum-if-invalid.json:2: 'if' condition of 'data' member 'bar' must be a string or a list of strings +enum-if-invalid.json: In enum 'TestIfEnum': +enum-if-invalid.json:2: 'if' condition of 'data' member 'bar' must be a string or a list of strings diff --git a/tests/qapi-schema/enum-if-invalid.exit b/tests/qapi-schema/enum-if-invalid.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/enum-if-invalid.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/enum-int-member.err b/tests/qapi-schema/enum-int-member.err index 27f06e334c..3a15294963 100644 --- a/tests/qapi-schema/enum-int-member.err +++ b/tests/qapi-schema/enum-int-member.err @@ -1 +1 @@ -tests/qapi-schema/enum-int-member.json:3:31: stray '1' +enum-int-member.json:3:31: stray '1' diff --git a/tests/qapi-schema/enum-int-member.exit b/tests/qapi-schema/enum-int-member.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/enum-int-member.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/enum-member-case.err b/tests/qapi-schema/enum-member-case.err index e6b080c6e5..8b3aefe37a 100644 --- a/tests/qapi-schema/enum-member-case.err +++ b/tests/qapi-schema/enum-member-case.err @@ -1,2 +1,2 @@ -tests/qapi-schema/enum-member-case.json: In enum 'NoWayThisWillGetWhitelisted': -tests/qapi-schema/enum-member-case.json:4: 'data' member 'Value' uses uppercase in name +enum-member-case.json: In enum 'NoWayThisWillGetWhitelisted': +enum-member-case.json:4: 'data' member 'Value' uses uppercase in name diff --git a/tests/qapi-schema/enum-member-case.exit b/tests/qapi-schema/enum-member-case.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/enum-member-case.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/enum-missing-data.err b/tests/qapi-schema/enum-missing-data.err index 4809b01f34..ec4bdae037 100644 --- a/tests/qapi-schema/enum-missing-data.err +++ b/tests/qapi-schema/enum-missing-data.err @@ -1,2 +1,2 @@ -tests/qapi-schema/enum-missing-data.json: In enum 'MyEnum': -tests/qapi-schema/enum-missing-data.json:2: enum misses key 'data' +enum-missing-data.json: In enum 'MyEnum': +enum-missing-data.json:2: enum misses key 'data' diff --git a/tests/qapi-schema/enum-missing-data.exit b/tests/qapi-schema/enum-missing-data.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/enum-missing-data.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/enum-wrong-data.err b/tests/qapi-schema/enum-wrong-data.err index ad5f0ce46f..a9f34481ba 100644 --- a/tests/qapi-schema/enum-wrong-data.err +++ b/tests/qapi-schema/enum-wrong-data.err @@ -1,2 +1,2 @@ -tests/qapi-schema/enum-wrong-data.json: In enum 'MyEnum': -tests/qapi-schema/enum-wrong-data.json:2: 'data' must be an array +enum-wrong-data.json: In enum 'MyEnum': +enum-wrong-data.json:2: 'data' must be an array diff --git a/tests/qapi-schema/enum-wrong-data.exit b/tests/qapi-schema/enum-wrong-data.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/enum-wrong-data.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/escape-outside-string.err b/tests/qapi-schema/escape-outside-string.err index 06f5f2ed55..c49fe0c285 100644 --- a/tests/qapi-schema/escape-outside-string.err +++ b/tests/qapi-schema/escape-outside-string.err @@ -1 +1 @@ -tests/qapi-schema/escape-outside-string.json:3:27: stray '\' +escape-outside-string.json:3:27: stray '\' diff --git a/tests/qapi-schema/event-boxed-empty.err b/tests/qapi-schema/event-boxed-empty.err index 931c10b036..b3872cdab5 100644 --- a/tests/qapi-schema/event-boxed-empty.err +++ b/tests/qapi-schema/event-boxed-empty.err @@ -1,2 +1,2 @@ -tests/qapi-schema/event-boxed-empty.json: In event 'FOO': -tests/qapi-schema/event-boxed-empty.json:2: 'boxed': true requires 'data' +event-boxed-empty.json: In event 'FOO': +event-boxed-empty.json:2: 'boxed': true requires 'data' diff --git a/tests/qapi-schema/event-boxed-empty.exit b/tests/qapi-schema/event-boxed-empty.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/event-boxed-empty.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/event-case.exit b/tests/qapi-schema/event-case.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/event-case.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out index ec8a1406e4..42ae519656 100644 --- a/tests/qapi-schema/event-case.out +++ b/tests/qapi-schema/event-case.out @@ -11,4 +11,4 @@ enum QType member qbool module event-case.json event oops None - boxed=False + boxed=False diff --git a/tests/qapi-schema/event-member-invalid-dict.err b/tests/qapi-schema/event-member-invalid-dict.err index 8406c43df7..c7a6a24305 100644 --- a/tests/qapi-schema/event-member-invalid-dict.err +++ b/tests/qapi-schema/event-member-invalid-dict.err @@ -1,2 +1,2 @@ -tests/qapi-schema/event-member-invalid-dict.json: In event 'EVENT_A': -tests/qapi-schema/event-member-invalid-dict.json:1: 'data' member 'a' misses key 'type' +event-member-invalid-dict.json: In event 'EVENT_A': +event-member-invalid-dict.json:1: 'data' member 'a' misses key 'type' diff --git a/tests/qapi-schema/event-member-invalid-dict.exit b/tests/qapi-schema/event-member-invalid-dict.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/event-member-invalid-dict.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/event-nest-struct.err b/tests/qapi-schema/event-nest-struct.err index 1a3254a73c..8c5f6ed311 100644 --- a/tests/qapi-schema/event-nest-struct.err +++ b/tests/qapi-schema/event-nest-struct.err @@ -1,2 +1,2 @@ -tests/qapi-schema/event-nest-struct.json: In event 'EVENT_A': -tests/qapi-schema/event-nest-struct.json:1: 'data' member 'a' should be a type name +event-nest-struct.json: In event 'EVENT_A': +event-nest-struct.json:1: 'data' member 'a' should be a type name diff --git a/tests/qapi-schema/event-nest-struct.exit b/tests/qapi-schema/event-nest-struct.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/event-nest-struct.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/features-bad-type.err b/tests/qapi-schema/features-bad-type.err index 30deb8b624..3c63591632 100644 --- a/tests/qapi-schema/features-bad-type.err +++ b/tests/qapi-schema/features-bad-type.err @@ -1,2 +1,2 @@ -tests/qapi-schema/features-bad-type.json: In struct 'FeatureStruct0': -tests/qapi-schema/features-bad-type.json:1: 'features' member requires a string name +features-bad-type.json: In struct 'FeatureStruct0': +features-bad-type.json:1: 'features' member requires a string name diff --git a/tests/qapi-schema/features-bad-type.exit b/tests/qapi-schema/features-bad-type.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/features-bad-type.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/features-duplicate-name.err b/tests/qapi-schema/features-duplicate-name.err index a99bbde737..0adbee6b0a 100644 --- a/tests/qapi-schema/features-duplicate-name.err +++ b/tests/qapi-schema/features-duplicate-name.err @@ -1,2 +1,2 @@ -tests/qapi-schema/features-duplicate-name.json: In struct 'FeatureStruct0': -tests/qapi-schema/features-duplicate-name.json:1: feature 'foo' collides with feature 'foo' +features-duplicate-name.json: In struct 'FeatureStruct0': +features-duplicate-name.json:1: feature 'foo' collides with feature 'foo' diff --git a/tests/qapi-schema/features-duplicate-name.exit b/tests/qapi-schema/features-duplicate-name.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/features-duplicate-name.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/features-if-invalid.err b/tests/qapi-schema/features-if-invalid.err index ffb39378af..f63b89535e 100644 --- a/tests/qapi-schema/features-if-invalid.err +++ b/tests/qapi-schema/features-if-invalid.err @@ -1,2 +1,2 @@ -tests/qapi-schema/features-if-invalid.json: In struct 'Stru': -tests/qapi-schema/features-if-invalid.json:2: 'if' condition of 'features' member 'f' must be a string or a list of strings +features-if-invalid.json: In struct 'Stru': +features-if-invalid.json:2: 'if' condition of 'features' member 'f' must be a string or a list of strings diff --git a/tests/qapi-schema/features-if-invalid.exit b/tests/qapi-schema/features-if-invalid.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/features-if-invalid.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/features-missing-name.err b/tests/qapi-schema/features-missing-name.err index b8db328acc..ce02412d2e 100644 --- a/tests/qapi-schema/features-missing-name.err +++ b/tests/qapi-schema/features-missing-name.err @@ -1,2 +1,2 @@ -tests/qapi-schema/features-missing-name.json: In struct 'FeatureStruct0': -tests/qapi-schema/features-missing-name.json:1: 'features' member misses key 'name' +features-missing-name.json: In struct 'FeatureStruct0': +features-missing-name.json:1: 'features' member misses key 'name' diff --git a/tests/qapi-schema/features-missing-name.exit b/tests/qapi-schema/features-missing-name.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/features-missing-name.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/features-name-bad-type.err b/tests/qapi-schema/features-name-bad-type.err index 86db2c0ea2..4afcd5fdc3 100644 --- a/tests/qapi-schema/features-name-bad-type.err +++ b/tests/qapi-schema/features-name-bad-type.err @@ -1,2 +1,2 @@ -tests/qapi-schema/features-name-bad-type.json: In struct 'FeatureStruct0': -tests/qapi-schema/features-name-bad-type.json:1: 'features' member requires a string name +features-name-bad-type.json: In struct 'FeatureStruct0': +features-name-bad-type.json:1: 'features' member requires a string name diff --git a/tests/qapi-schema/features-name-bad-type.exit b/tests/qapi-schema/features-name-bad-type.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/features-name-bad-type.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/features-no-list.err b/tests/qapi-schema/features-no-list.err index e493f85057..5cfaa3f216 100644 --- a/tests/qapi-schema/features-no-list.err +++ b/tests/qapi-schema/features-no-list.err @@ -1,2 +1,2 @@ -tests/qapi-schema/features-no-list.json: In struct 'FeatureStruct0': -tests/qapi-schema/features-no-list.json:1: 'features' must be an array +features-no-list.json: In struct 'FeatureStruct0': +features-no-list.json:1: 'features' must be an array diff --git a/tests/qapi-schema/features-no-list.exit b/tests/qapi-schema/features-no-list.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/features-no-list.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/features-unknown-key.err b/tests/qapi-schema/features-unknown-key.err index 22f5dcf4b0..13e359a216 100644 --- a/tests/qapi-schema/features-unknown-key.err +++ b/tests/qapi-schema/features-unknown-key.err @@ -1,3 +1,3 @@ -tests/qapi-schema/features-unknown-key.json: In struct 'FeatureStruct0': -tests/qapi-schema/features-unknown-key.json:1: 'features' member has unknown key 'colour' +features-unknown-key.json: In struct 'FeatureStruct0': +features-unknown-key.json:1: 'features' member has unknown key 'colour' Valid keys are 'if', 'name'. diff --git a/tests/qapi-schema/features-unknown-key.exit b/tests/qapi-schema/features-unknown-key.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/features-unknown-key.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-array-branch.err b/tests/qapi-schema/flat-union-array-branch.err index de07a7b32a..20a8ef1406 100644 --- a/tests/qapi-schema/flat-union-array-branch.err +++ b/tests/qapi-schema/flat-union-array-branch.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-array-branch.json: In union 'TestUnion': -tests/qapi-schema/flat-union-array-branch.json:8: 'data' member 'value1' cannot be an array +flat-union-array-branch.json: In union 'TestUnion': +flat-union-array-branch.json:8: 'data' member 'value1' cannot be an array diff --git a/tests/qapi-schema/flat-union-array-branch.exit b/tests/qapi-schema/flat-union-array-branch.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-array-branch.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-bad-base.err b/tests/qapi-schema/flat-union-bad-base.err index 5da7602c20..e0a205a58c 100644 --- a/tests/qapi-schema/flat-union-bad-base.err +++ b/tests/qapi-schema/flat-union-bad-base.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-bad-base.json: In union 'TestUnion': -tests/qapi-schema/flat-union-bad-base.json:8: member 'string' of type 'TestTypeA' collides with base member 'string' +flat-union-bad-base.json: In union 'TestUnion': +flat-union-bad-base.json:8: member 'string' of type 'TestTypeA' collides with base member 'string' diff --git a/tests/qapi-schema/flat-union-bad-base.exit b/tests/qapi-schema/flat-union-bad-base.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-bad-base.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-bad-discriminator.err b/tests/qapi-schema/flat-union-bad-discriminator.err index c1b4209ffd..b705439bd9 100644 --- a/tests/qapi-schema/flat-union-bad-discriminator.err +++ b/tests/qapi-schema/flat-union-bad-discriminator.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-bad-discriminator.json: In union 'TestUnion': -tests/qapi-schema/flat-union-bad-discriminator.json:11: 'discriminator' requires a string name +flat-union-bad-discriminator.json: In union 'TestUnion': +flat-union-bad-discriminator.json:11: 'discriminator' requires a string name diff --git a/tests/qapi-schema/flat-union-bad-discriminator.exit b/tests/qapi-schema/flat-union-bad-discriminator.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-bad-discriminator.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-base-any.err b/tests/qapi-schema/flat-union-base-any.err index 7ab3402396..c2d4de6a5d 100644 --- a/tests/qapi-schema/flat-union-base-any.err +++ b/tests/qapi-schema/flat-union-base-any.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-base-any.json: In union 'TestUnion': -tests/qapi-schema/flat-union-base-any.json:8: 'base' requires a struct type, built-in type 'any' isn't +flat-union-base-any.json: In union 'TestUnion': +flat-union-base-any.json:8: 'base' requires a struct type, built-in type 'any' isn't diff --git a/tests/qapi-schema/flat-union-base-any.exit b/tests/qapi-schema/flat-union-base-any.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-base-any.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-base-union.err b/tests/qapi-schema/flat-union-base-union.err index 5db7b1e404..3b0087220e 100644 --- a/tests/qapi-schema/flat-union-base-union.err +++ b/tests/qapi-schema/flat-union-base-union.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-base-union.json: In union 'TestUnion': -tests/qapi-schema/flat-union-base-union.json:14: 'base' requires a struct type, union type 'UnionBase' isn't +flat-union-base-union.json: In union 'TestUnion': +flat-union-base-union.json:14: 'base' requires a struct type, union type 'UnionBase' isn't diff --git a/tests/qapi-schema/flat-union-base-union.exit b/tests/qapi-schema/flat-union-base-union.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-base-union.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-clash-member.err b/tests/qapi-schema/flat-union-clash-member.err index 40f10681f8..07551e6ef5 100644 --- a/tests/qapi-schema/flat-union-clash-member.err +++ b/tests/qapi-schema/flat-union-clash-member.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-clash-member.json: In union 'TestUnion': -tests/qapi-schema/flat-union-clash-member.json:11: member 'name' of type 'Branch1' collides with member 'name' of type 'Base' +flat-union-clash-member.json: In union 'TestUnion': +flat-union-clash-member.json:11: member 'name' of type 'Branch1' collides with member 'name' of type 'Base' diff --git a/tests/qapi-schema/flat-union-clash-member.exit b/tests/qapi-schema/flat-union-clash-member.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-clash-member.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-discriminator-bad-name.err b/tests/qapi-schema/flat-union-discriminator-bad-name.err index 2a0deb6a0e..28be49c31a 100644 --- a/tests/qapi-schema/flat-union-discriminator-bad-name.err +++ b/tests/qapi-schema/flat-union-discriminator-bad-name.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-discriminator-bad-name.json: In union 'MyUnion': -tests/qapi-schema/flat-union-discriminator-bad-name.json:6: discriminator '*switch' is not a member of 'base' +flat-union-discriminator-bad-name.json: In union 'MyUnion': +flat-union-discriminator-bad-name.json:6: discriminator '*switch' is not a member of 'base' diff --git a/tests/qapi-schema/flat-union-discriminator-bad-name.exit b/tests/qapi-schema/flat-union-discriminator-bad-name.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-discriminator-bad-name.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-empty.err b/tests/qapi-schema/flat-union-empty.err index 91a5b57f5d..89b0f25cb0 100644 --- a/tests/qapi-schema/flat-union-empty.err +++ b/tests/qapi-schema/flat-union-empty.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-empty.json: In union 'Union': -tests/qapi-schema/flat-union-empty.json:4: union has no branches +flat-union-empty.json: In union 'Union': +flat-union-empty.json:4: union has no branches diff --git a/tests/qapi-schema/flat-union-empty.exit b/tests/qapi-schema/flat-union-empty.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-empty.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.err b/tests/qapi-schema/flat-union-inline-invalid-dict.err index d353bdd338..53e5416707 100644 --- a/tests/qapi-schema/flat-union-inline-invalid-dict.err +++ b/tests/qapi-schema/flat-union-inline-invalid-dict.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-inline-invalid-dict.json: In union 'TestUnion': -tests/qapi-schema/flat-union-inline-invalid-dict.json:7: 'data' member 'value1' misses key 'type' +flat-union-inline-invalid-dict.json: In union 'TestUnion': +flat-union-inline-invalid-dict.json:7: 'data' member 'value1' misses key 'type' diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.exit b/tests/qapi-schema/flat-union-inline-invalid-dict.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-inline-invalid-dict.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-inline.err b/tests/qapi-schema/flat-union-inline.err index 95b1e8c1b7..538283f5db 100644 --- a/tests/qapi-schema/flat-union-inline.err +++ b/tests/qapi-schema/flat-union-inline.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-inline.json: In union 'TestUnion': -tests/qapi-schema/flat-union-inline.json:7: 'data' member 'value1' should be a type name +flat-union-inline.json: In union 'TestUnion': +flat-union-inline.json:7: 'data' member 'value1' should be a type name diff --git a/tests/qapi-schema/flat-union-inline.exit b/tests/qapi-schema/flat-union-inline.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-inline.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-int-branch.err b/tests/qapi-schema/flat-union-int-branch.err index 416b696c8f..ae7f800603 100644 --- a/tests/qapi-schema/flat-union-int-branch.err +++ b/tests/qapi-schema/flat-union-int-branch.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-int-branch.json: In union 'TestUnion': -tests/qapi-schema/flat-union-int-branch.json:8: branch 'value1' cannot use built-in type 'int' +flat-union-int-branch.json: In union 'TestUnion': +flat-union-int-branch.json:8: branch 'value1' cannot use built-in type 'int' diff --git a/tests/qapi-schema/flat-union-int-branch.exit b/tests/qapi-schema/flat-union-int-branch.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-int-branch.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.err b/tests/qapi-schema/flat-union-invalid-branch-key.err index 6997b3387d..5576a25f9b 100644 --- a/tests/qapi-schema/flat-union-invalid-branch-key.err +++ b/tests/qapi-schema/flat-union-invalid-branch-key.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-invalid-branch-key.json: In union 'TestUnion': -tests/qapi-schema/flat-union-invalid-branch-key.json:13: branch 'value_wrong' is not a value of enum type 'TestEnum' +flat-union-invalid-branch-key.json: In union 'TestUnion': +flat-union-invalid-branch-key.json:13: branch 'value_wrong' is not a value of enum type 'TestEnum' diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.exit b/tests/qapi-schema/flat-union-invalid-branch-key.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-invalid-branch-key.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.err b/tests/qapi-schema/flat-union-invalid-discriminator.err index 3f80de3044..99bca2ddab 100644 --- a/tests/qapi-schema/flat-union-invalid-discriminator.err +++ b/tests/qapi-schema/flat-union-invalid-discriminator.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-invalid-discriminator.json: In union 'TestUnion': -tests/qapi-schema/flat-union-invalid-discriminator.json:10: discriminator 'enum_wrong' is not a member of 'base' +flat-union-invalid-discriminator.json: In union 'TestUnion': +flat-union-invalid-discriminator.json:10: discriminator 'enum_wrong' is not a member of 'base' diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.exit b/tests/qapi-schema/flat-union-invalid-discriminator.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-invalid-discriminator.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.err b/tests/qapi-schema/flat-union-invalid-if-discriminator.err index d2b35be9ae..350f28da9d 100644 --- a/tests/qapi-schema/flat-union-invalid-if-discriminator.err +++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-invalid-if-discriminator.json: In union 'TestUnion': -tests/qapi-schema/flat-union-invalid-if-discriminator.json:10: discriminator member 'enum1' of 'base' must not be conditional +flat-union-invalid-if-discriminator.json: In union 'TestUnion': +flat-union-invalid-if-discriminator.json:10: discriminator member 'enum1' of 'base' must not be conditional diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.exit b/tests/qapi-schema/flat-union-invalid-if-discriminator.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-invalid-if-discriminator.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-no-base.err b/tests/qapi-schema/flat-union-no-base.err index a16f3231f1..9bd595bcfb 100644 --- a/tests/qapi-schema/flat-union-no-base.err +++ b/tests/qapi-schema/flat-union-no-base.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-no-base.json: In union 'TestUnion': -tests/qapi-schema/flat-union-no-base.json:9: 'discriminator' requires 'base' +flat-union-no-base.json: In union 'TestUnion': +flat-union-no-base.json:9: 'discriminator' requires 'base' diff --git a/tests/qapi-schema/flat-union-no-base.exit b/tests/qapi-schema/flat-union-no-base.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-no-base.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-optional-discriminator.err b/tests/qapi-schema/flat-union-optional-discriminator.err index 49fbf5b04d..3d60a1b496 100644 --- a/tests/qapi-schema/flat-union-optional-discriminator.err +++ b/tests/qapi-schema/flat-union-optional-discriminator.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-optional-discriminator.json: In union 'MyUnion': -tests/qapi-schema/flat-union-optional-discriminator.json:6: discriminator member 'switch' of base type 'Base' must not be optional +flat-union-optional-discriminator.json: In union 'MyUnion': +flat-union-optional-discriminator.json:6: discriminator member 'switch' of base type 'Base' must not be optional diff --git a/tests/qapi-schema/flat-union-optional-discriminator.exit b/tests/qapi-schema/flat-union-optional-discriminator.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-optional-discriminator.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-string-discriminator.err b/tests/qapi-schema/flat-union-string-discriminator.err index fb499188aa..ff42c9728b 100644 --- a/tests/qapi-schema/flat-union-string-discriminator.err +++ b/tests/qapi-schema/flat-union-string-discriminator.err @@ -1,2 +1,2 @@ -tests/qapi-schema/flat-union-string-discriminator.json: In union 'TestUnion': -tests/qapi-schema/flat-union-string-discriminator.json:13: discriminator member 'kind' of base type 'TestBase' must be of enum type +flat-union-string-discriminator.json: In union 'TestUnion': +flat-union-string-discriminator.json:13: discriminator member 'kind' of base type 'TestBase' must be of enum type diff --git a/tests/qapi-schema/flat-union-string-discriminator.exit b/tests/qapi-schema/flat-union-string-discriminator.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-string-discriminator.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/funny-char.err b/tests/qapi-schema/funny-char.err index 132fac93ea..1c8abae2e0 100644 --- a/tests/qapi-schema/funny-char.err +++ b/tests/qapi-schema/funny-char.err @@ -1 +1 @@ -tests/qapi-schema/funny-char.json:2:36: stray ';' +funny-char.json:2:36: stray ';' diff --git a/tests/qapi-schema/funny-char.exit b/tests/qapi-schema/funny-char.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/funny-char.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/funny-word.err b/tests/qapi-schema/funny-word.err index d9286c8962..a1d9ade970 100644 --- a/tests/qapi-schema/funny-word.err +++ b/tests/qapi-schema/funny-word.err @@ -1 +1 @@ -tests/qapi-schema/funny-word.json:1:3: stray 'command' +funny-word.json:1:3: stray 'command' diff --git a/tests/qapi-schema/funny-word.exit b/tests/qapi-schema/funny-word.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/funny-word.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/ident-with-escape.err b/tests/qapi-schema/ident-with-escape.err index 1117283c81..3f6c1e423c 100644 --- a/tests/qapi-schema/ident-with-escape.err +++ b/tests/qapi-schema/ident-with-escape.err @@ -1 +1 @@ -tests/qapi-schema/ident-with-escape.json:3:3: unknown escape \u +ident-with-escape.json:3:3: unknown escape \u diff --git a/tests/qapi-schema/ident-with-escape.exit b/tests/qapi-schema/ident-with-escape.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/ident-with-escape.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/include-before-err.err b/tests/qapi-schema/include-before-err.err index 098314bc49..16da03026f 100644 --- a/tests/qapi-schema/include-before-err.err +++ b/tests/qapi-schema/include-before-err.err @@ -1 +1 @@ -tests/qapi-schema/include-before-err.json:2:13: expected ':' +include-before-err.json:2:13: expected ':' diff --git a/tests/qapi-schema/include-before-err.exit b/tests/qapi-schema/include-before-err.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/include-before-err.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/include-cycle.err b/tests/qapi-schema/include-cycle.err index 52028669b5..4c99f5642b 100644 --- a/tests/qapi-schema/include-cycle.err +++ b/tests/qapi-schema/include-cycle.err @@ -1,3 +1,3 @@ -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 +In file included from include-cycle.json:1: +In file included from include-cycle-b.json:1: +include-cycle-c.json:1: inclusion loop for include-cycle.json diff --git a/tests/qapi-schema/include-cycle.exit b/tests/qapi-schema/include-cycle.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/include-cycle.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/include-extra-junk.err b/tests/qapi-schema/include-extra-junk.err index 854cec3ce3..1f7d2e1437 100644 --- a/tests/qapi-schema/include-extra-junk.err +++ b/tests/qapi-schema/include-extra-junk.err @@ -1 +1 @@ -tests/qapi-schema/include-extra-junk.json:3: invalid 'include' directive +include-extra-junk.json:3: invalid 'include' directive diff --git a/tests/qapi-schema/include-extra-junk.exit b/tests/qapi-schema/include-extra-junk.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/include-extra-junk.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/include-nested-err.err b/tests/qapi-schema/include-nested-err.err index 11928b4f7f..9eac9eaeea 100644 --- a/tests/qapi-schema/include-nested-err.err +++ b/tests/qapi-schema/include-nested-err.err @@ -1,2 +1,2 @@ -In file included from tests/qapi-schema/include-nested-err.json:1: -tests/qapi-schema/missing-colon.json:1:10: expected ':' +In file included from include-nested-err.json:1: +missing-colon.json:1:10: expected ':' diff --git a/tests/qapi-schema/include-nested-err.exit b/tests/qapi-schema/include-nested-err.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/include-nested-err.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/include-no-file.err b/tests/qapi-schema/include-no-file.err index 0a6c6bb4a9..3115a889fe 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: can't read include file 'tests/qapi-schema/include-no-file-sub.json': No such file or directory +include-no-file.json:1: can't read include file 'include-no-file-sub.json': No such file or directory diff --git a/tests/qapi-schema/include-no-file.exit b/tests/qapi-schema/include-no-file.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/include-no-file.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/include-non-file.err b/tests/qapi-schema/include-non-file.err index 65dd3c7c2c..ec8a331654 100644 --- a/tests/qapi-schema/include-non-file.err +++ b/tests/qapi-schema/include-non-file.err @@ -1 +1 @@ -tests/qapi-schema/include-non-file.json:1: value of 'include' must be a string +include-non-file.json:1: value of 'include' must be a string diff --git a/tests/qapi-schema/include-non-file.exit b/tests/qapi-schema/include-non-file.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/include-non-file.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/include-repetition.exit b/tests/qapi-schema/include-repetition.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/include-repetition.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/include-self-cycle.err b/tests/qapi-schema/include-self-cycle.err index c84795d1dc..284b0d6f0e 100644 --- a/tests/qapi-schema/include-self-cycle.err +++ b/tests/qapi-schema/include-self-cycle.err @@ -1 +1 @@ -tests/qapi-schema/include-self-cycle.json:1: inclusion loop for include-self-cycle.json +include-self-cycle.json:1: inclusion loop for include-self-cycle.json diff --git a/tests/qapi-schema/include-self-cycle.exit b/tests/qapi-schema/include-self-cycle.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/include-self-cycle.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/include-simple.exit b/tests/qapi-schema/include-simple.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/include-simple.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/indented-expr.exit b/tests/qapi-schema/indented-expr.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/indented-expr.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index bffdf6756d..04356775cd 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -11,6 +11,6 @@ enum QType member qbool module indented-expr.json command eins None -> None - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False command zwei None -> None - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/leading-comma-list.err b/tests/qapi-schema/leading-comma-list.err index cddf471f71..76eed2b5b3 100644 --- a/tests/qapi-schema/leading-comma-list.err +++ b/tests/qapi-schema/leading-comma-list.err @@ -1 +1 @@ -tests/qapi-schema/leading-comma-list.json:2:13: expected '{', '[', ']', string, boolean or 'null' +leading-comma-list.json:2:13: expected '{', '[', ']', string, boolean or 'null' diff --git a/tests/qapi-schema/leading-comma-list.exit b/tests/qapi-schema/leading-comma-list.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/leading-comma-list.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/leading-comma-object.err b/tests/qapi-schema/leading-comma-object.err index 2f3b193274..25f8b6ffd6 100644 --- a/tests/qapi-schema/leading-comma-object.err +++ b/tests/qapi-schema/leading-comma-object.err @@ -1 +1 @@ -tests/qapi-schema/leading-comma-object.json:1:3: expected string or '}' +leading-comma-object.json:1:3: expected string or '}' diff --git a/tests/qapi-schema/leading-comma-object.exit b/tests/qapi-schema/leading-comma-object.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/leading-comma-object.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/missing-colon.err b/tests/qapi-schema/missing-colon.err index e642c7c549..d5fe235cb9 100644 --- a/tests/qapi-schema/missing-colon.err +++ b/tests/qapi-schema/missing-colon.err @@ -1 +1 @@ -tests/qapi-schema/missing-colon.json:1:10: expected ':' +missing-colon.json:1:10: expected ':' diff --git a/tests/qapi-schema/missing-colon.exit b/tests/qapi-schema/missing-colon.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/missing-colon.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/missing-comma-list.err b/tests/qapi-schema/missing-comma-list.err index 5359499430..d0a790c2b3 100644 --- a/tests/qapi-schema/missing-comma-list.err +++ b/tests/qapi-schema/missing-comma-list.err @@ -1 +1 @@ -tests/qapi-schema/missing-comma-list.json:2:20: expected ',' or ']' +missing-comma-list.json:2:20: expected ',' or ']' diff --git a/tests/qapi-schema/missing-comma-list.exit b/tests/qapi-schema/missing-comma-list.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/missing-comma-list.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/missing-comma-object.err b/tests/qapi-schema/missing-comma-object.err index c9b02b0760..aa8efbc2f3 100644 --- a/tests/qapi-schema/missing-comma-object.err +++ b/tests/qapi-schema/missing-comma-object.err @@ -1 +1 @@ -tests/qapi-schema/missing-comma-object.json:2:3: expected ',' or '}' +missing-comma-object.json:2:3: expected ',' or '}' diff --git a/tests/qapi-schema/missing-comma-object.exit b/tests/qapi-schema/missing-comma-object.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/missing-comma-object.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/missing-type.err b/tests/qapi-schema/missing-type.err index 19b7c495e7..5755386a18 100644 --- a/tests/qapi-schema/missing-type.err +++ b/tests/qapi-schema/missing-type.err @@ -1 +1 @@ -tests/qapi-schema/missing-type.json:2: expression is missing metatype +missing-type.json:2: expression is missing metatype diff --git a/tests/qapi-schema/missing-type.exit b/tests/qapi-schema/missing-type.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/missing-type.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.err b/tests/qapi-schema/nested-struct-data-invalid-dict.err index ed42d6323e..c044b2b17a 100644 --- a/tests/qapi-schema/nested-struct-data-invalid-dict.err +++ b/tests/qapi-schema/nested-struct-data-invalid-dict.err @@ -1,2 +1,2 @@ -tests/qapi-schema/nested-struct-data-invalid-dict.json: In command 'foo': -tests/qapi-schema/nested-struct-data-invalid-dict.json:2: 'data' member 'a' misses key 'type' +nested-struct-data-invalid-dict.json: In command 'foo': +nested-struct-data-invalid-dict.json:2: 'data' member 'a' misses key 'type' diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.exit b/tests/qapi-schema/nested-struct-data-invalid-dict.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/nested-struct-data-invalid-dict.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/nested-struct-data.err b/tests/qapi-schema/nested-struct-data.err index b0ec410eb7..c7258a0182 100644 --- a/tests/qapi-schema/nested-struct-data.err +++ b/tests/qapi-schema/nested-struct-data.err @@ -1,2 +1,2 @@ -tests/qapi-schema/nested-struct-data.json: In command 'foo': -tests/qapi-schema/nested-struct-data.json:2: 'data' member 'a' should be a type name +nested-struct-data.json: In command 'foo': +nested-struct-data.json:2: 'data' member 'a' should be a type name diff --git a/tests/qapi-schema/nested-struct-data.exit b/tests/qapi-schema/nested-struct-data.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/nested-struct-data.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/non-objects.err b/tests/qapi-schema/non-objects.err index 9237af6939..3a4ea36966 100644 --- a/tests/qapi-schema/non-objects.err +++ b/tests/qapi-schema/non-objects.err @@ -1 +1 @@ -tests/qapi-schema/non-objects.json:1:1: expected '{' +non-objects.json:1:1: expected '{' diff --git a/tests/qapi-schema/non-objects.exit b/tests/qapi-schema/non-objects.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/non-objects.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/oob-test.err b/tests/qapi-schema/oob-test.err index 3c2ba6e0fd..7b9a50b3d5 100644 --- a/tests/qapi-schema/oob-test.err +++ b/tests/qapi-schema/oob-test.err @@ -1,2 +1,2 @@ -tests/qapi-schema/oob-test.json: In command 'oob-command-1': -tests/qapi-schema/oob-test.json:2: flag 'allow-oob' may only use true value +oob-test.json: In command 'oob-command-1': +oob-test.json:2: flag 'allow-oob' may only use true value diff --git a/tests/qapi-schema/oob-test.exit b/tests/qapi-schema/oob-test.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/oob-test.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/pragma-doc-required-crap.err b/tests/qapi-schema/pragma-doc-required-crap.err index bcd981ada8..717062cb14 100644 --- a/tests/qapi-schema/pragma-doc-required-crap.err +++ b/tests/qapi-schema/pragma-doc-required-crap.err @@ -1 +1 @@ -tests/qapi-schema/pragma-doc-required-crap.json:3: pragma 'doc-required' must be boolean +pragma-doc-required-crap.json:3: pragma 'doc-required' must be boolean diff --git a/tests/qapi-schema/pragma-doc-required-crap.exit b/tests/qapi-schema/pragma-doc-required-crap.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/pragma-doc-required-crap.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/pragma-extra-junk.err b/tests/qapi-schema/pragma-extra-junk.err index 3ae48d3668..ba5cc23f56 100644 --- a/tests/qapi-schema/pragma-extra-junk.err +++ b/tests/qapi-schema/pragma-extra-junk.err @@ -1 +1 @@ -tests/qapi-schema/pragma-extra-junk.json:3: invalid 'pragma' directive +pragma-extra-junk.json:3: invalid 'pragma' directive diff --git a/tests/qapi-schema/pragma-extra-junk.exit b/tests/qapi-schema/pragma-extra-junk.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/pragma-extra-junk.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/pragma-name-case-whitelist-crap.err b/tests/qapi-schema/pragma-name-case-whitelist-crap.err index 81f829ff36..fbea90d6c5 100644 --- a/tests/qapi-schema/pragma-name-case-whitelist-crap.err +++ b/tests/qapi-schema/pragma-name-case-whitelist-crap.err @@ -1 +1 @@ -tests/qapi-schema/pragma-name-case-whitelist-crap.json:3: pragma name-case-whitelist must be a list of strings +pragma-name-case-whitelist-crap.json:3: pragma name-case-whitelist must be a list of strings diff --git a/tests/qapi-schema/pragma-name-case-whitelist-crap.exit b/tests/qapi-schema/pragma-name-case-whitelist-crap.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/pragma-name-case-whitelist-crap.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/pragma-non-dict.err b/tests/qapi-schema/pragma-non-dict.err index 8221724b0a..802f1806c5 100644 --- a/tests/qapi-schema/pragma-non-dict.err +++ b/tests/qapi-schema/pragma-non-dict.err @@ -1 +1 @@ -tests/qapi-schema/pragma-non-dict.json:3: value of 'pragma' must be an object +pragma-non-dict.json:3: value of 'pragma' must be an object diff --git a/tests/qapi-schema/pragma-non-dict.exit b/tests/qapi-schema/pragma-non-dict.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/pragma-non-dict.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/pragma-returns-whitelist-crap.err b/tests/qapi-schema/pragma-returns-whitelist-crap.err index c0cae5de18..69784259df 100644 --- a/tests/qapi-schema/pragma-returns-whitelist-crap.err +++ b/tests/qapi-schema/pragma-returns-whitelist-crap.err @@ -1 +1 @@ -tests/qapi-schema/pragma-returns-whitelist-crap.json:3: pragma returns-whitelist must be a list of strings +pragma-returns-whitelist-crap.json:3: pragma returns-whitelist must be a list of strings diff --git a/tests/qapi-schema/pragma-returns-whitelist-crap.exit b/tests/qapi-schema/pragma-returns-whitelist-crap.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/pragma-returns-whitelist-crap.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/pragma-unknown.err b/tests/qapi-schema/pragma-unknown.err index f1335f0a0a..d99a772503 100644 --- a/tests/qapi-schema/pragma-unknown.err +++ b/tests/qapi-schema/pragma-unknown.err @@ -1 +1 @@ -tests/qapi-schema/pragma-unknown.json:1: unknown pragma 'no-such-pragma' +pragma-unknown.json:1: unknown pragma 'no-such-pragma' diff --git a/tests/qapi-schema/pragma-unknown.exit b/tests/qapi-schema/pragma-unknown.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/pragma-unknown.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/qapi-schema-test.exit b/tests/qapi-schema/qapi-schema-test.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/qapi-schema-test.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 75c42eb0e3..9abf175fe0 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -290,3 +290,21 @@ 'cfs1': 'CondFeatureStruct1', 'cfs2': 'CondFeatureStruct2', 'cfs3': 'CondFeatureStruct3' } } + +# test 'features' for command + +{ 'command': 'test-command-features0', + 'features': [] } +{ 'command': 'test-command-features1', + 'features': [ 'feature1' ] } +{ 'command': 'test-command-features3', + 'features': [ 'feature1', 'feature2' ] } + +{ 'command': 'test-command-cond-features1', + 'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'} ] } +{ 'command': 'test-command-cond-features2', + 'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'}, + { 'name': 'feature2', 'if': 'defined(TEST_IF_FEATURE_2)'} ] } +{ 'command': 'test-command-cond-features3', + 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', + 'defined(TEST_IF_COND_2)'] } ] } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 98031da96f..3660e75a48 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -33,7 +33,7 @@ object Union case value3: q_empty case value4: q_empty command user_def_cmd0 Empty2 -> Empty2 - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False enum QEnumTwo prefix QENUM_TWO member value1 @@ -205,35 +205,35 @@ object SecondArrayRef member s: StatusList optional=False module qapi-schema-test.json command user_def_cmd None -> None - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=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 oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=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 oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False command cmd-success-response None -> None - gen=True success_response=False boxed=False oob=False preconfig=False + gen=True success_response=False boxed=False oob=False preconfig=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 oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=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 oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False command boxed-struct UserDefZero -> None - gen=True success_response=True boxed=True oob=False preconfig=False + gen=True success_response=True boxed=True oob=False preconfig=False command boxed-union UserDefListUnion -> None - gen=True success_response=True boxed=True oob=False preconfig=False + gen=True success_response=True boxed=True oob=False preconfig=False command boxed-empty Empty1 -> None - gen=True success_response=True boxed=True oob=False preconfig=False + gen=True success_response=True boxed=True oob=False preconfig=False command test-flags-command None -> None - gen=True success_response=True boxed=False oob=True preconfig=True + gen=True success_response=True boxed=False oob=True preconfig=True object UserDefOptions member i64: intList optional=True member u64: uint64List optional=True @@ -245,28 +245,28 @@ object EventStructOne member string: str optional=False member enum2: EnumOne optional=True event EVENT_A None - boxed=False + boxed=False event EVENT_B None - boxed=False + 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 + 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 + boxed=False event EVENT_E UserDefZero - boxed=True + boxed=True event EVENT_F UserDefFlatUnion - boxed=True + boxed=True event EVENT_G Empty1 - boxed=True + boxed=True enum __org.qemu_x-Enum member __org.qemu_x-value object __org.qemu_x-Base @@ -297,7 +297,7 @@ alternate __org.qemu_x-Alt tag type case __org.qemu_x-branch: __org.qemu_x-Base event __ORG.QEMU_X-EVENT __org.qemu_x-Struct - boxed=False + boxed=False array __org.qemu_x-EnumList __org.qemu_x-Enum array __org.qemu_x-StructList __org.qemu_x-Struct object q_obj___org.qemu_x-command-arg @@ -306,7 +306,7 @@ object q_obj___org.qemu_x-command-arg member c: __org.qemu_x-Union2 optional=False member d: __org.qemu_x-Alt optional=False command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False object TestIfStruct member foo: int optional=False member bar: int optional=False @@ -335,7 +335,7 @@ object q_obj_TestIfUnionCmd-arg member union_cmd_arg: TestIfUnion optional=False if ['defined(TEST_IF_UNION)'] command TestIfUnionCmd q_obj_TestIfUnionCmd-arg -> None - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False if ['defined(TEST_IF_UNION)'] alternate TestIfAlternate tag type @@ -347,7 +347,7 @@ object q_obj_TestIfAlternateCmd-arg member alt_cmd_arg: TestIfAlternate optional=False if ['defined(TEST_IF_ALT)'] command TestIfAlternateCmd q_obj_TestIfAlternateCmd-arg -> None - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False if ['defined(TEST_IF_ALT)'] object q_obj_TestIfCmd-arg member foo: TestIfStruct optional=False @@ -355,10 +355,10 @@ object q_obj_TestIfCmd-arg if ['defined(TEST_IF_CMD_BAR)'] if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] command TestCmdReturnDefThree None -> UserDefThree - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False array TestIfEnumList TestIfEnum if ['defined(TEST_IF_ENUM)'] object q_obj_TestIfEvent-arg @@ -367,7 +367,7 @@ object q_obj_TestIfEvent-arg if ['defined(TEST_IF_EVT_BAR)'] if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'] event TestIfEvent q_obj_TestIfEvent-arg - boxed=False + boxed=False if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'] object FeatureStruct0 member foo: int optional=False @@ -411,4 +411,27 @@ object q_obj_test-features-arg member cfs2: CondFeatureStruct2 optional=False member cfs3: CondFeatureStruct3 optional=False command test-features q_obj_test-features-arg -> None - gen=True success_response=True boxed=False oob=False preconfig=False + gen=True success_response=True boxed=False oob=False preconfig=False +command test-command-features0 None -> None + gen=True success_response=True boxed=False oob=False preconfig=False +command test-command-features1 None -> None + gen=True success_response=True boxed=False oob=False preconfig=False + feature feature1 +command test-command-features3 None -> None + gen=True success_response=True boxed=False oob=False preconfig=False + feature feature1 + feature feature2 +command test-command-cond-features1 None -> None + gen=True success_response=True boxed=False oob=False preconfig=False + feature feature1 + if ['defined(TEST_IF_FEATURE_1)'] +command test-command-cond-features2 None -> None + gen=True success_response=True boxed=False oob=False preconfig=False + feature feature1 + if ['defined(TEST_IF_FEATURE_1)'] + feature feature2 + if ['defined(TEST_IF_FEATURE_2)'] +command test-command-cond-features3 None -> None + gen=True success_response=True boxed=False oob=False preconfig=False + feature feature1 + if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] diff --git a/tests/qapi-schema/quoted-structural-chars.err b/tests/qapi-schema/quoted-structural-chars.err index d8460a63a7..07d1561d1f 100644 --- a/tests/qapi-schema/quoted-structural-chars.err +++ b/tests/qapi-schema/quoted-structural-chars.err @@ -1 +1 @@ -tests/qapi-schema/quoted-structural-chars.json:1:1: expected '{' +quoted-structural-chars.json:1:1: expected '{' diff --git a/tests/qapi-schema/quoted-structural-chars.exit b/tests/qapi-schema/quoted-structural-chars.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/quoted-structural-chars.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/redefined-builtin.err b/tests/qapi-schema/redefined-builtin.err index 47c8933759..58c7e42ffc 100644 --- a/tests/qapi-schema/redefined-builtin.err +++ b/tests/qapi-schema/redefined-builtin.err @@ -1,2 +1,2 @@ -tests/qapi-schema/redefined-builtin.json: In struct 'size': -tests/qapi-schema/redefined-builtin.json:2: built-in type 'size' is already defined +redefined-builtin.json: In struct 'size': +redefined-builtin.json:2: built-in type 'size' is already defined diff --git a/tests/qapi-schema/redefined-builtin.exit b/tests/qapi-schema/redefined-builtin.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/redefined-builtin.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/redefined-command.err b/tests/qapi-schema/redefined-command.err index 54e366bbf3..888eea87ca 100644 --- a/tests/qapi-schema/redefined-command.err +++ b/tests/qapi-schema/redefined-command.err @@ -1,4 +1,4 @@ -tests/qapi-schema/redefined-command.json: In command 'foo': -tests/qapi-schema/redefined-command.json:3: 'foo' is already defined -tests/qapi-schema/redefined-command.json: In command 'foo': -tests/qapi-schema/redefined-command.json:2: previous definition +redefined-command.json: In command 'foo': +redefined-command.json:3: 'foo' is already defined +redefined-command.json: In command 'foo': +redefined-command.json:2: previous definition diff --git a/tests/qapi-schema/redefined-command.exit b/tests/qapi-schema/redefined-command.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/redefined-command.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/redefined-event.err b/tests/qapi-schema/redefined-event.err index 606c6e4497..b1a6d99d2f 100644 --- a/tests/qapi-schema/redefined-event.err +++ b/tests/qapi-schema/redefined-event.err @@ -1,4 +1,4 @@ -tests/qapi-schema/redefined-event.json: In event 'EVENT_A': -tests/qapi-schema/redefined-event.json:3: 'EVENT_A' is already defined -tests/qapi-schema/redefined-event.json: In event 'EVENT_A': -tests/qapi-schema/redefined-event.json:2: previous definition +redefined-event.json: In event 'EVENT_A': +redefined-event.json:3: 'EVENT_A' is already defined +redefined-event.json: In event 'EVENT_A': +redefined-event.json:2: previous definition diff --git a/tests/qapi-schema/redefined-event.exit b/tests/qapi-schema/redefined-event.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/redefined-event.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/redefined-type.err b/tests/qapi-schema/redefined-type.err index 77786f98ae..b7103fc15f 100644 --- a/tests/qapi-schema/redefined-type.err +++ b/tests/qapi-schema/redefined-type.err @@ -1,4 +1,4 @@ -tests/qapi-schema/redefined-type.json: In enum 'foo': -tests/qapi-schema/redefined-type.json:3: 'foo' is already defined -tests/qapi-schema/redefined-type.json: In struct 'foo': -tests/qapi-schema/redefined-type.json:2: previous definition +redefined-type.json: In enum 'foo': +redefined-type.json:3: 'foo' is already defined +redefined-type.json: In struct 'foo': +redefined-type.json:2: previous definition diff --git a/tests/qapi-schema/redefined-type.exit b/tests/qapi-schema/redefined-type.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/redefined-type.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/reserved-command-q.err b/tests/qapi-schema/reserved-command-q.err index 7f65cda02d..6fecbaa046 100644 --- a/tests/qapi-schema/reserved-command-q.err +++ b/tests/qapi-schema/reserved-command-q.err @@ -1,2 +1,2 @@ -tests/qapi-schema/reserved-command-q.json: In command 'q-unix': -tests/qapi-schema/reserved-command-q.json:5: command has an invalid name +reserved-command-q.json: In command 'q-unix': +reserved-command-q.json:5: command has an invalid name diff --git a/tests/qapi-schema/reserved-command-q.exit b/tests/qapi-schema/reserved-command-q.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/reserved-command-q.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/reserved-enum-q.err b/tests/qapi-schema/reserved-enum-q.err index e202f9ff7b..fc3c757874 100644 --- a/tests/qapi-schema/reserved-enum-q.err +++ b/tests/qapi-schema/reserved-enum-q.err @@ -1,2 +1,2 @@ -tests/qapi-schema/reserved-enum-q.json: In enum 'Foo': -tests/qapi-schema/reserved-enum-q.json:4: 'data' member 'q-Unix' has an invalid name +reserved-enum-q.json: In enum 'Foo': +reserved-enum-q.json:4: 'data' member 'q-Unix' has an invalid name diff --git a/tests/qapi-schema/reserved-enum-q.exit b/tests/qapi-schema/reserved-enum-q.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/reserved-enum-q.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/reserved-member-has.err b/tests/qapi-schema/reserved-member-has.err index c7ad721ad1..436e1749f2 100644 --- a/tests/qapi-schema/reserved-member-has.err +++ b/tests/qapi-schema/reserved-member-has.err @@ -1,2 +1,2 @@ -tests/qapi-schema/reserved-member-has.json: In command 'oops': -tests/qapi-schema/reserved-member-has.json:5: 'data' member 'has-a' uses reserved name +reserved-member-has.json: In command 'oops': +reserved-member-has.json:5: 'data' member 'has-a' uses reserved name diff --git a/tests/qapi-schema/reserved-member-has.exit b/tests/qapi-schema/reserved-member-has.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/reserved-member-has.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/reserved-member-q.err b/tests/qapi-schema/reserved-member-q.err index 04078604fa..9ac8654a8b 100644 --- a/tests/qapi-schema/reserved-member-q.err +++ b/tests/qapi-schema/reserved-member-q.err @@ -1,2 +1,2 @@ -tests/qapi-schema/reserved-member-q.json: In struct 'Foo': -tests/qapi-schema/reserved-member-q.json:4: 'data' member 'q-unix' has an invalid name +reserved-member-q.json: In struct 'Foo': +reserved-member-q.json:4: 'data' member 'q-unix' has an invalid name diff --git a/tests/qapi-schema/reserved-member-q.exit b/tests/qapi-schema/reserved-member-q.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/reserved-member-q.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/reserved-member-u.err b/tests/qapi-schema/reserved-member-u.err index 2e92c11ba5..231d552494 100644 --- a/tests/qapi-schema/reserved-member-u.err +++ b/tests/qapi-schema/reserved-member-u.err @@ -1,2 +1,2 @@ -tests/qapi-schema/reserved-member-u.json: In struct 'Oops': -tests/qapi-schema/reserved-member-u.json:7: 'data' member 'u' uses reserved name +reserved-member-u.json: In struct 'Oops': +reserved-member-u.json:7: 'data' member 'u' uses reserved name diff --git a/tests/qapi-schema/reserved-member-u.exit b/tests/qapi-schema/reserved-member-u.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/reserved-member-u.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/reserved-member-underscore.err b/tests/qapi-schema/reserved-member-underscore.err index da62b48222..df3ab8a11a 100644 --- a/tests/qapi-schema/reserved-member-underscore.err +++ b/tests/qapi-schema/reserved-member-underscore.err @@ -1,2 +1,2 @@ -tests/qapi-schema/reserved-member-underscore.json: In struct 'Oops': -tests/qapi-schema/reserved-member-underscore.json:4: 'data' member '_oops' has an invalid name +reserved-member-underscore.json: In struct 'Oops': +reserved-member-underscore.json:4: 'data' member '_oops' has an invalid name diff --git a/tests/qapi-schema/reserved-member-underscore.exit b/tests/qapi-schema/reserved-member-underscore.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/reserved-member-underscore.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/reserved-type-kind.err b/tests/qapi-schema/reserved-type-kind.err index f8112cf92e..d8fb769f9d 100644 --- a/tests/qapi-schema/reserved-type-kind.err +++ b/tests/qapi-schema/reserved-type-kind.err @@ -1,2 +1,2 @@ -tests/qapi-schema/reserved-type-kind.json: In enum 'UnionKind': -tests/qapi-schema/reserved-type-kind.json:2: enum name should not end in 'Kind' +reserved-type-kind.json: In enum 'UnionKind': +reserved-type-kind.json:2: enum name should not end in 'Kind' diff --git a/tests/qapi-schema/reserved-type-kind.exit b/tests/qapi-schema/reserved-type-kind.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/reserved-type-kind.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/reserved-type-list.err b/tests/qapi-schema/reserved-type-list.err index c6eee0585c..e09f5352f8 100644 --- a/tests/qapi-schema/reserved-type-list.err +++ b/tests/qapi-schema/reserved-type-list.err @@ -1,2 +1,2 @@ -tests/qapi-schema/reserved-type-list.json: In struct 'FooList': -tests/qapi-schema/reserved-type-list.json:5: struct name should not end in 'List' +reserved-type-list.json: In struct 'FooList': +reserved-type-list.json:5: struct name should not end in 'List' diff --git a/tests/qapi-schema/reserved-type-list.exit b/tests/qapi-schema/reserved-type-list.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/reserved-type-list.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/returns-alternate.err b/tests/qapi-schema/returns-alternate.err index c1caf98c47..b6421a8917 100644 --- a/tests/qapi-schema/returns-alternate.err +++ b/tests/qapi-schema/returns-alternate.err @@ -1,2 +1,2 @@ -tests/qapi-schema/returns-alternate.json: In command 'oops': -tests/qapi-schema/returns-alternate.json:3: command's 'returns' cannot take alternate type 'Alt' +returns-alternate.json: In command 'oops': +returns-alternate.json:3: command's 'returns' cannot take alternate type 'Alt' diff --git a/tests/qapi-schema/returns-alternate.exit b/tests/qapi-schema/returns-alternate.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/returns-alternate.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/returns-array-bad.err b/tests/qapi-schema/returns-array-bad.err index 1b86777d8f..52712d139d 100644 --- a/tests/qapi-schema/returns-array-bad.err +++ b/tests/qapi-schema/returns-array-bad.err @@ -1,2 +1,2 @@ -tests/qapi-schema/returns-array-bad.json: In command 'oops': -tests/qapi-schema/returns-array-bad.json:2: 'returns': array type must contain single type name +returns-array-bad.json: In command 'oops': +returns-array-bad.json:2: 'returns': array type must contain single type name diff --git a/tests/qapi-schema/returns-array-bad.exit b/tests/qapi-schema/returns-array-bad.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/returns-array-bad.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/returns-dict.err b/tests/qapi-schema/returns-dict.err index 52e4f3ad71..9b2d90c010 100644 --- a/tests/qapi-schema/returns-dict.err +++ b/tests/qapi-schema/returns-dict.err @@ -1,2 +1,2 @@ -tests/qapi-schema/returns-dict.json: In command 'oops': -tests/qapi-schema/returns-dict.json:2: 'returns' should be a type name +returns-dict.json: In command 'oops': +returns-dict.json:2: 'returns' should be a type name diff --git a/tests/qapi-schema/returns-dict.exit b/tests/qapi-schema/returns-dict.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/returns-dict.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/returns-unknown.err b/tests/qapi-schema/returns-unknown.err index f0a989a175..bf59086d0c 100644 --- a/tests/qapi-schema/returns-unknown.err +++ b/tests/qapi-schema/returns-unknown.err @@ -1,2 +1,2 @@ -tests/qapi-schema/returns-unknown.json: In command 'oops': -tests/qapi-schema/returns-unknown.json:2: command's 'returns' uses unknown type 'NoSuchType' +returns-unknown.json: In command 'oops': +returns-unknown.json:2: command's 'returns' uses unknown type 'NoSuchType' diff --git a/tests/qapi-schema/returns-unknown.exit b/tests/qapi-schema/returns-unknown.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/returns-unknown.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/returns-whitelist.err b/tests/qapi-schema/returns-whitelist.err index 5b0285220f..c6e46b9b86 100644 --- a/tests/qapi-schema/returns-whitelist.err +++ b/tests/qapi-schema/returns-whitelist.err @@ -1,2 +1,2 @@ -tests/qapi-schema/returns-whitelist.json: In command 'no-way-this-will-get-whitelisted': -tests/qapi-schema/returns-whitelist.json:14: command's 'returns' cannot take array type ['int'] +returns-whitelist.json: In command 'no-way-this-will-get-whitelisted': +returns-whitelist.json:14: command's 'returns' cannot take array type ['int'] diff --git a/tests/qapi-schema/returns-whitelist.exit b/tests/qapi-schema/returns-whitelist.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/returns-whitelist.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/string-code-point-127.err b/tests/qapi-schema/string-code-point-127.err index b4fa2610a9..eb0d1c71a6 100644 --- a/tests/qapi-schema/string-code-point-127.err +++ b/tests/qapi-schema/string-code-point-127.err @@ -1 +1 @@ -tests/qapi-schema/string-code-point-127.json:2:14: funny character in string +string-code-point-127.json:2:14: funny character in string diff --git a/tests/qapi-schema/string-code-point-127.exit b/tests/qapi-schema/string-code-point-127.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/string-code-point-127.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/string-code-point-31.err b/tests/qapi-schema/string-code-point-31.err index 0bb5ce37b8..ae969d6e10 100644 --- a/tests/qapi-schema/string-code-point-31.err +++ b/tests/qapi-schema/string-code-point-31.err @@ -1 +1 @@ -tests/qapi-schema/string-code-point-31.json:2:14: funny character in string +string-code-point-31.json:2:14: funny character in string diff --git a/tests/qapi-schema/string-code-point-31.exit b/tests/qapi-schema/string-code-point-31.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/string-code-point-31.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/struct-base-clash-deep.err b/tests/qapi-schema/struct-base-clash-deep.err index 2b12b3c07f..79879681d9 100644 --- a/tests/qapi-schema/struct-base-clash-deep.err +++ b/tests/qapi-schema/struct-base-clash-deep.err @@ -1,2 +1,2 @@ -tests/qapi-schema/struct-base-clash-deep.json: In struct 'Sub': -tests/qapi-schema/struct-base-clash-deep.json:10: member 'name' collides with member 'name' of type 'Base' +struct-base-clash-deep.json: In struct 'Sub': +struct-base-clash-deep.json:10: member 'name' collides with member 'name' of type 'Base' diff --git a/tests/qapi-schema/struct-base-clash-deep.exit b/tests/qapi-schema/struct-base-clash-deep.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/struct-base-clash-deep.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/struct-base-clash.err b/tests/qapi-schema/struct-base-clash.err index 8c3ee1c435..46473947e6 100644 --- a/tests/qapi-schema/struct-base-clash.err +++ b/tests/qapi-schema/struct-base-clash.err @@ -1,2 +1,2 @@ -tests/qapi-schema/struct-base-clash.json: In struct 'Sub': -tests/qapi-schema/struct-base-clash.json:5: member 'name' collides with member 'name' of type 'Base' +struct-base-clash.json: In struct 'Sub': +struct-base-clash.json:5: member 'name' collides with member 'name' of type 'Base' diff --git a/tests/qapi-schema/struct-base-clash.exit b/tests/qapi-schema/struct-base-clash.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/struct-base-clash.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/struct-data-invalid.err b/tests/qapi-schema/struct-data-invalid.err index aa868bf974..5ed4bec573 100644 --- a/tests/qapi-schema/struct-data-invalid.err +++ b/tests/qapi-schema/struct-data-invalid.err @@ -1,2 +1,2 @@ -tests/qapi-schema/struct-data-invalid.json: In struct 'foo': -tests/qapi-schema/struct-data-invalid.json:1: 'data' should be an object or type name +struct-data-invalid.json: In struct 'foo': +struct-data-invalid.json:1: 'data' should be an object or type name diff --git a/tests/qapi-schema/struct-data-invalid.exit b/tests/qapi-schema/struct-data-invalid.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/struct-data-invalid.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/struct-member-if-invalid.err b/tests/qapi-schema/struct-member-if-invalid.err index 4c5983674b..42e7fdae3c 100644 --- a/tests/qapi-schema/struct-member-if-invalid.err +++ b/tests/qapi-schema/struct-member-if-invalid.err @@ -1,2 +1,2 @@ -tests/qapi-schema/struct-member-if-invalid.json: In struct 'Stru': -tests/qapi-schema/struct-member-if-invalid.json:2: 'if' condition of 'data' member 'member' must be a string or a list of strings +struct-member-if-invalid.json: In struct 'Stru': +struct-member-if-invalid.json:2: 'if' condition of 'data' member 'member' must be a string or a list of strings diff --git a/tests/qapi-schema/struct-member-if-invalid.exit b/tests/qapi-schema/struct-member-if-invalid.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/struct-member-if-invalid.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/struct-member-invalid-dict.err b/tests/qapi-schema/struct-member-invalid-dict.err index 46ec991c28..0621aecfbd 100644 --- a/tests/qapi-schema/struct-member-invalid-dict.err +++ b/tests/qapi-schema/struct-member-invalid-dict.err @@ -1,2 +1,2 @@ -tests/qapi-schema/struct-member-invalid-dict.json: In struct 'foo': -tests/qapi-schema/struct-member-invalid-dict.json:2: 'data' member '*a' misses key 'type' +struct-member-invalid-dict.json: In struct 'foo': +struct-member-invalid-dict.json:2: 'data' member '*a' misses key 'type' diff --git a/tests/qapi-schema/struct-member-invalid-dict.exit b/tests/qapi-schema/struct-member-invalid-dict.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/struct-member-invalid-dict.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/struct-member-invalid.err b/tests/qapi-schema/struct-member-invalid.err index 92d4973832..9a2c934538 100644 --- a/tests/qapi-schema/struct-member-invalid.err +++ b/tests/qapi-schema/struct-member-invalid.err @@ -1,2 +1,2 @@ -tests/qapi-schema/struct-member-invalid.json: In struct 'foo': -tests/qapi-schema/struct-member-invalid.json:1: 'data' member 'a' should be a type name +struct-member-invalid.json: In struct 'foo': +struct-member-invalid.json:1: 'data' member 'a' should be a type name diff --git a/tests/qapi-schema/struct-member-invalid.exit b/tests/qapi-schema/struct-member-invalid.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/struct-member-invalid.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index b0f770b9bd..2bd9fd8742 100644..100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # # QAPI parser test harness # @@ -11,8 +12,19 @@ # from __future__ import print_function + +import argparse +import difflib +import os import sys -from qapi.common import QAPIError, QAPISchema, QAPISchemaVisitor + +from qapi.error import QAPIError +from qapi.schema import QAPISchema, QAPISchemaVisitor + +if sys.version_info[0] < 3: + from cStringIO import StringIO +else: + from io import StringIO class QAPISchemaTestVisitor(QAPISchemaVisitor): @@ -49,10 +61,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(m.ifcond, 8) self._print_variants(variants) self._print_if(ifcond) - if features: - for f in features: - print(' feature %s' % f.name) - self._print_if(f.ifcond, 8) + self._print_features(features) def visit_alternate_type(self, name, info, ifcond, variants): print('alternate %s' % name) @@ -60,17 +69,19 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(ifcond) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig): + success_response, boxed, allow_oob, allow_preconfig, + features): print('command %s %s -> %s' % (name, arg_type and arg_type.name, ret_type and ret_type.name)) - print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' + print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' % (gen, success_response, boxed, allow_oob, allow_preconfig)) self._print_if(ifcond) + self._print_features(features) def visit_event(self, name, info, ifcond, arg_type, boxed): print('event %s %s' % (name, arg_type and arg_type.name)) - print(' boxed=%s' % boxed) + print(' boxed=%s' % boxed) self._print_if(ifcond) @staticmethod @@ -86,22 +97,110 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): if ifcond: print('%sif %s' % (' ' * indent, ifcond)) - -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: - if doc.symbol: - print('doc symbol=%s' % doc.symbol) + @classmethod + def _print_features(cls, features): + if features: + for f in features: + print(' feature %s' % f.name) + cls._print_if(f.ifcond, 8) + + +def test_frontend(fname): + schema = QAPISchema(fname) + schema.visit(QAPISchemaTestVisitor()) + + for doc in schema.docs: + if doc.symbol: + print('doc symbol=%s' % doc.symbol) + else: + print('doc freeform') + print(' body=\n%s' % doc.body.text) + for arg, section in doc.args.items(): + print(' arg=%s\n%s' % (arg, section.text)) + for section in doc.sections: + print(' section=%s\n%s' % (section.name, section.text)) + + +def test_and_diff(test_name, dir_name, update): + sys.stdout = StringIO() + try: + test_frontend(os.path.join(dir_name, test_name + '.json')) + except QAPIError as err: + if err.info.fname is None: + print("%s" % err, file=sys.stderr) + return 2 + errstr = str(err) + '\n' + if dir_name: + errstr = errstr.replace(dir_name + '/', '') + actual_err = errstr.splitlines(True) else: - print('doc freeform') - print(' body=\n%s' % doc.body.text) - for arg, section in doc.args.items(): - print(' arg=%s\n%s' % (arg, section.text)) - for section in doc.sections: - print(' section=%s\n%s' % (section.name, section.text)) + actual_err = [] + finally: + actual_out = sys.stdout.getvalue().splitlines(True) + sys.stdout.close() + sys.stdout = sys.__stdout__ + + mode = 'r+' if update else 'r' + try: + outfp = open(os.path.join(dir_name, test_name + '.out'), mode) + errfp = open(os.path.join(dir_name, test_name + '.err'), mode) + expected_out = outfp.readlines() + expected_err = errfp.readlines() + except IOError as err: + print("%s: can't open '%s': %s" + % (sys.argv[0], err.filename, err.strerror), + file=sys.stderr) + return 2 + + if actual_out == expected_out and actual_err == expected_err: + return 0 + + print("%s %s" % (test_name, 'UPDATE' if update else 'FAIL'), + file=sys.stderr) + out_diff = difflib.unified_diff(expected_out, actual_out, outfp.name) + err_diff = difflib.unified_diff(expected_err, actual_err, errfp.name) + sys.stdout.writelines(out_diff) + sys.stdout.writelines(err_diff) + + if not update: + return 1 + + try: + outfp.truncate(0) + outfp.seek(0) + outfp.writelines(actual_out) + errfp.truncate(0) + errfp.seek(0) + errfp.writelines(actual_err) + except IOError as err: + print("%s: can't write '%s': %s" + % (sys.argv[0], err.filename, err.strerror), + file=sys.stderr) + return 2 + + return 0 + + +def main(argv): + parser = argparse.ArgumentParser( + description='QAPI schema tester') + parser.add_argument('-d', '--dir', action='store', default='', + help="directory containing tests") + parser.add_argument('-u', '--update', action='store_true', + help="update expected test results") + parser.add_argument('tests', nargs='*', metavar='TEST', action='store') + args = parser.parse_args() + + status = 0 + for t in args.tests: + (dir_name, base_name) = os.path.split(t) + dir_name = dir_name or args.dir + test_name = os.path.splitext(base_name)[0] + status |= test_and_diff(test_name, dir_name, args.update) + + exit(status) + + +if __name__ == '__main__': + main(sys.argv) + exit(0) diff --git a/tests/qapi-schema/trailing-comma-list.err b/tests/qapi-schema/trailing-comma-list.err index 167d688beb..ad2f2d7c97 100644 --- a/tests/qapi-schema/trailing-comma-list.err +++ b/tests/qapi-schema/trailing-comma-list.err @@ -1 +1 @@ -tests/qapi-schema/trailing-comma-list.json:2:36: expected '{', '[', string, boolean or 'null' +trailing-comma-list.json:2:36: expected '{', '[', string, boolean or 'null' diff --git a/tests/qapi-schema/trailing-comma-list.exit b/tests/qapi-schema/trailing-comma-list.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/trailing-comma-list.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/trailing-comma-object.err b/tests/qapi-schema/trailing-comma-object.err index 186df0fa45..29aafc69cd 100644 --- a/tests/qapi-schema/trailing-comma-object.err +++ b/tests/qapi-schema/trailing-comma-object.err @@ -1 +1 @@ -tests/qapi-schema/trailing-comma-object.json:2:38: expected string +trailing-comma-object.json:2:38: expected string diff --git a/tests/qapi-schema/trailing-comma-object.exit b/tests/qapi-schema/trailing-comma-object.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/trailing-comma-object.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/type-bypass-bad-gen.err b/tests/qapi-schema/type-bypass-bad-gen.err index 1077651896..2ca95cd86d 100644 --- a/tests/qapi-schema/type-bypass-bad-gen.err +++ b/tests/qapi-schema/type-bypass-bad-gen.err @@ -1,2 +1,2 @@ -tests/qapi-schema/type-bypass-bad-gen.json: In command 'foo': -tests/qapi-schema/type-bypass-bad-gen.json:2: flag 'gen' may only use false value +type-bypass-bad-gen.json: In command 'foo': +type-bypass-bad-gen.json:2: flag 'gen' may only use false value diff --git a/tests/qapi-schema/type-bypass-bad-gen.exit b/tests/qapi-schema/type-bypass-bad-gen.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/type-bypass-bad-gen.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/unclosed-list.err b/tests/qapi-schema/unclosed-list.err index 6648a8e30b..aa2c430b22 100644 --- a/tests/qapi-schema/unclosed-list.err +++ b/tests/qapi-schema/unclosed-list.err @@ -1 +1 @@ -tests/qapi-schema/unclosed-list.json:1:20: expected ',' or ']' +unclosed-list.json:1:20: expected ',' or ']' diff --git a/tests/qapi-schema/unclosed-list.exit b/tests/qapi-schema/unclosed-list.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/unclosed-list.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/unclosed-object.err b/tests/qapi-schema/unclosed-object.err index 54d221e3a9..c08499b341 100644 --- a/tests/qapi-schema/unclosed-object.err +++ b/tests/qapi-schema/unclosed-object.err @@ -1 +1 @@ -tests/qapi-schema/unclosed-object.json:1:21: expected ',' or '}' +unclosed-object.json:1:21: expected ',' or '}' diff --git a/tests/qapi-schema/unclosed-object.exit b/tests/qapi-schema/unclosed-object.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/unclosed-object.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/unclosed-string.err b/tests/qapi-schema/unclosed-string.err index 9439698fe0..175e192b24 100644 --- a/tests/qapi-schema/unclosed-string.err +++ b/tests/qapi-schema/unclosed-string.err @@ -1 +1 @@ -tests/qapi-schema/unclosed-string.json:1:11: missing terminating "'" +unclosed-string.json:1:11: missing terminating "'" diff --git a/tests/qapi-schema/unclosed-string.exit b/tests/qapi-schema/unclosed-string.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/unclosed-string.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/union-base-empty.err b/tests/qapi-schema/union-base-empty.err index b76542d47a..3630b341a6 100644 --- a/tests/qapi-schema/union-base-empty.err +++ b/tests/qapi-schema/union-base-empty.err @@ -1,2 +1,2 @@ -tests/qapi-schema/union-base-empty.json: In union 'TestUnion': -tests/qapi-schema/union-base-empty.json:5: discriminator 'type' is not a member of 'base' +union-base-empty.json: In union 'TestUnion': +union-base-empty.json:5: discriminator 'type' is not a member of 'base' diff --git a/tests/qapi-schema/union-base-empty.exit b/tests/qapi-schema/union-base-empty.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/union-base-empty.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/union-base-no-discriminator.err b/tests/qapi-schema/union-base-no-discriminator.err index f4c16a2c14..9cd5d11b0b 100644 --- a/tests/qapi-schema/union-base-no-discriminator.err +++ b/tests/qapi-schema/union-base-no-discriminator.err @@ -1,2 +1,2 @@ -tests/qapi-schema/union-base-no-discriminator.json: In union 'TestUnion': -tests/qapi-schema/union-base-no-discriminator.json:11: 'base' requires 'discriminator' +union-base-no-discriminator.json: In union 'TestUnion': +union-base-no-discriminator.json:11: 'base' requires 'discriminator' diff --git a/tests/qapi-schema/union-base-no-discriminator.exit b/tests/qapi-schema/union-base-no-discriminator.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/union-base-no-discriminator.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/union-branch-case.err b/tests/qapi-schema/union-branch-case.err index a0684ae637..b1e9417303 100644 --- a/tests/qapi-schema/union-branch-case.err +++ b/tests/qapi-schema/union-branch-case.err @@ -1,2 +1,2 @@ -tests/qapi-schema/union-branch-case.json: In union 'Uni': -tests/qapi-schema/union-branch-case.json:2: 'data' member 'Branch' uses uppercase in name +union-branch-case.json: In union 'Uni': +union-branch-case.json:2: 'data' member 'Branch' uses uppercase in name diff --git a/tests/qapi-schema/union-branch-case.exit b/tests/qapi-schema/union-branch-case.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/union-branch-case.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/union-branch-if-invalid.err b/tests/qapi-schema/union-branch-if-invalid.err index 14819bf8b8..dd4518233e 100644 --- a/tests/qapi-schema/union-branch-if-invalid.err +++ b/tests/qapi-schema/union-branch-if-invalid.err @@ -1,2 +1,2 @@ -tests/qapi-schema/union-branch-if-invalid.json: In union 'Uni': -tests/qapi-schema/union-branch-if-invalid.json:4: 'if' condition '' of 'data' member 'branch1' makes no sense +union-branch-if-invalid.json: In union 'Uni': +union-branch-if-invalid.json:4: 'if' condition '' of 'data' member 'branch1' makes no sense diff --git a/tests/qapi-schema/union-branch-if-invalid.exit b/tests/qapi-schema/union-branch-if-invalid.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/union-branch-if-invalid.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/union-branch-invalid-dict.err b/tests/qapi-schema/union-branch-invalid-dict.err index 2967cd6260..8137c5a767 100644 --- a/tests/qapi-schema/union-branch-invalid-dict.err +++ b/tests/qapi-schema/union-branch-invalid-dict.err @@ -1,2 +1,2 @@ -tests/qapi-schema/union-branch-invalid-dict.json: In union 'UnionInvalidBranch': -tests/qapi-schema/union-branch-invalid-dict.json:2: 'data' member 'integer' misses key 'type' +union-branch-invalid-dict.json: In union 'UnionInvalidBranch': +union-branch-invalid-dict.json:2: 'data' member 'integer' misses key 'type' diff --git a/tests/qapi-schema/union-branch-invalid-dict.exit b/tests/qapi-schema/union-branch-invalid-dict.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/union-branch-invalid-dict.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/union-clash-branches.err b/tests/qapi-schema/union-clash-branches.err index 931399f076..73bbc2cabd 100644 --- a/tests/qapi-schema/union-clash-branches.err +++ b/tests/qapi-schema/union-clash-branches.err @@ -1,2 +1,2 @@ -tests/qapi-schema/union-clash-branches.json: In union 'TestUnion': -tests/qapi-schema/union-clash-branches.json:4: branch 'a_b' collides with branch 'a-b' +union-clash-branches.json: In union 'TestUnion': +union-clash-branches.json:4: branch 'a_b' collides with branch 'a-b' diff --git a/tests/qapi-schema/union-clash-branches.exit b/tests/qapi-schema/union-clash-branches.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/union-clash-branches.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/union-empty.err b/tests/qapi-schema/union-empty.err index 35c0d62eb0..59788c94ce 100644 --- a/tests/qapi-schema/union-empty.err +++ b/tests/qapi-schema/union-empty.err @@ -1,2 +1,2 @@ -tests/qapi-schema/union-empty.json: In union 'Union': -tests/qapi-schema/union-empty.json:2: union has no branches +union-empty.json: In union 'Union': +union-empty.json:2: union has no branches diff --git a/tests/qapi-schema/union-empty.exit b/tests/qapi-schema/union-empty.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/union-empty.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/union-invalid-base.err b/tests/qapi-schema/union-invalid-base.err index 10fecf0b56..a804028438 100644 --- a/tests/qapi-schema/union-invalid-base.err +++ b/tests/qapi-schema/union-invalid-base.err @@ -1,2 +1,2 @@ -tests/qapi-schema/union-invalid-base.json: In union 'TestUnion': -tests/qapi-schema/union-invalid-base.json:8: 'base' requires a struct type, built-in type 'int' isn't +union-invalid-base.json: In union 'TestUnion': +union-invalid-base.json:8: 'base' requires a struct type, built-in type 'int' isn't diff --git a/tests/qapi-schema/union-invalid-base.exit b/tests/qapi-schema/union-invalid-base.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/union-invalid-base.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/union-optional-branch.err b/tests/qapi-schema/union-optional-branch.err index 9f24274923..b33f111de4 100644 --- a/tests/qapi-schema/union-optional-branch.err +++ b/tests/qapi-schema/union-optional-branch.err @@ -1,2 +1,2 @@ -tests/qapi-schema/union-optional-branch.json: In union 'Union': -tests/qapi-schema/union-optional-branch.json:2: 'data' member '*a' has an invalid name +union-optional-branch.json: In union 'Union': +union-optional-branch.json:2: 'data' member '*a' has an invalid name diff --git a/tests/qapi-schema/union-optional-branch.exit b/tests/qapi-schema/union-optional-branch.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/union-optional-branch.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/union-unknown.err b/tests/qapi-schema/union-unknown.err index a7f340ddca..7aba9f94da 100644 --- a/tests/qapi-schema/union-unknown.err +++ b/tests/qapi-schema/union-unknown.err @@ -1,2 +1,2 @@ -tests/qapi-schema/union-unknown.json: In union 'Union': -tests/qapi-schema/union-unknown.json:2: union uses unknown type 'MissingType' +union-unknown.json: In union 'Union': +union-unknown.json:2: union uses unknown type 'MissingType' diff --git a/tests/qapi-schema/union-unknown.exit b/tests/qapi-schema/union-unknown.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/union-unknown.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/unknown-escape.err b/tests/qapi-schema/unknown-escape.err index e24bbaf046..759a5990d8 100644 --- a/tests/qapi-schema/unknown-escape.err +++ b/tests/qapi-schema/unknown-escape.err @@ -1 +1 @@ -tests/qapi-schema/unknown-escape.json:3:21: unknown escape \x +unknown-escape.json:3:21: unknown escape \x diff --git a/tests/qapi-schema/unknown-escape.exit b/tests/qapi-schema/unknown-escape.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/unknown-escape.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/unknown-expr-key.err b/tests/qapi-schema/unknown-expr-key.err index be9f99c4ef..c5f395bf79 100644 --- a/tests/qapi-schema/unknown-expr-key.err +++ b/tests/qapi-schema/unknown-expr-key.err @@ -1,3 +1,3 @@ -tests/qapi-schema/unknown-expr-key.json: In struct 'bar': -tests/qapi-schema/unknown-expr-key.json:2: struct has unknown keys 'bogus', 'phony' +unknown-expr-key.json: In struct 'bar': +unknown-expr-key.json:2: struct has unknown keys 'bogus', 'phony' Valid keys are 'base', 'data', 'features', 'if', 'struct'. diff --git a/tests/qapi-schema/unknown-expr-key.exit b/tests/qapi-schema/unknown-expr-key.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/unknown-expr-key.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 36fdf5b115..27b0afe55a 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -51,6 +51,30 @@ void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, { } +void qmp_test_command_features0(Error **errp) +{ +} + +void qmp_test_command_features1(Error **errp) +{ +} + +void qmp_test_command_features3(Error **errp) +{ +} + +void qmp_test_command_cond_features1(Error **errp) +{ +} + +void qmp_test_command_cond_features2(Error **errp) +{ +} + +void qmp_test_command_cond_features3(Error **errp) +{ +} + UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, bool has_udb1, UserDefOne *ud1b, Error **errp) |