mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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>
This commit is contained in:
@@ -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 \
|
||||
$(SRC_PATH)/scripts/qapi/events.py \
|
||||
$(SRC_PATH)/scripts/qapi/introspect.py \
|
||||
$(SRC_PATH)/scripts/qapi/types.py \
|
||||
$(SRC_PATH)/scripts/qapi/visit.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-gen.py
|
||||
|
||||
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
+8
-1
@@ -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:
|
||||
|
||||
+6
-4
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Executable → Regular
+7
-7
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
@@ -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))
|
||||
@@ -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):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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()
|
||||
@@ -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('''
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
+18
-16
@@ -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 \
|
||||
$(SRC_PATH)/scripts/qapi/events.py \
|
||||
$(SRC_PATH)/scripts/qapi/introspect.py \
|
||||
$(SRC_PATH)/scripts/qapi/types.py \
|
||||
$(SRC_PATH)/scripts/qapi/visit.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-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
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user