QAPI patches
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQIcBAABAgAGBQJVgrdwAAoJEDhwtADrkYZT4SIP/3SuCDVbKyhcwjRWk7Ooyqm6
gddoSHDJ6RZhonUncZ7xLML2snCqUsmuLA9WCf0MS+mJJlKDcAIemuhJ9L/MFwxT
9hz9NIJc28LVxkszlspEfUwr1+wf+qF38XpLtapKvXEcw/foUPjwHEW7YrKWJ7rP
J2p39y35sLuZMkKPJ5i6LY8GZXhy387uSIAsIoYc2fVAijCCGDJ6fczxQ/n+HbXp
gRQzFjbUcMu95nMxjDHD17Oq1xC2ktz0UTTx2A744PlXXLoMUzqJD44qY71W3PpZ
83YiFVuiKQUcNoefLEECGNXBzmgdyyQ8vDymlQtlFWwqhMRaGXCNeGT70byEp4jp
LdH5GcGHq95A12EGIpH+Bxr2/o+MU0BxMBg7QcEjmem47IZNkz1fwQgJcSui8OtG
zG/oezWQvY0rUPHKR2rG24RgOPzzoAKbDmIae9IfaUOa2YNetKFV0l46n8jsr/Y5
C9pH+1XDalR0vDrj5WNqH8sEiR8p6FPx3EGKTgtRuurCDuuB8VZU/3Q+O1qSV0i6
k1W9iUjmC/vyMt5KaDXrnII9jK8XWft7LWaT+2P0y/z0emSWfL9IwjXWPhYuEbCr
KIfdNeD9pV/ituqK+cBABvjI+9J6v8eQZPIHm9HzgmD11aufXzaiwcqvOYN+kdux
T4yw3naErAnPj+LC9uNz
=OPwL
-----END PGP SIGNATURE-----
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2015-06-18' into staging
QAPI patches
# gpg: Signature made Thu Jun 18 13:20:00 2015 BST using RSA key ID EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg: aka "Markus Armbruster <armbru@pond.sub.org>"
* remotes/armbru/tags/pull-qapi-2015-06-18:
qapi-types: Bury code dead since commit 6b5abc7
qapi-types: Split generate_fwd_builtin() off generate_fwd_struct()
qapi-types: Drop unused members parameters
qapi-types: Don't filter out expressions with 'gen'
qapi: Catch and reject flat union branch of array type
tests/qapi-schema: New flat union array branch test case
qapi: Better separate the different kinds of helpers
qapi: Move exprs checking from parse_schema() to check_exprs()
qapi: Fix to reject stray 't', 'f' and 'n'
qapi: Simplify inclusion cycle detection
qapi: Fix file name in error messages for included files
qapi: Improve a couple of confusing variable names
qapi: Eliminate superfluous QAPISchema attribute input_dir
qapi: Drop bogus command from docs
MAINTAINERS: Fix up QAPI and QAPI schema file patterns
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
8ffe756da0
@ -952,7 +952,10 @@ M: Markus Armbruster <armbru@redhat.com>
|
|||||||
M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: qapi/
|
F: qapi/
|
||||||
|
X: qapi/*.json
|
||||||
F: tests/qapi-schema/
|
F: tests/qapi-schema/
|
||||||
|
F: scripts/qapi*
|
||||||
|
F: docs/qapi*
|
||||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||||
|
|
||||||
QAPI Schema
|
QAPI Schema
|
||||||
@ -960,6 +963,7 @@ M: Eric Blake <eblake@redhat.com>
|
|||||||
M: Markus Armbruster <armbru@redhat.com>
|
M: Markus Armbruster <armbru@redhat.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: qapi-schema.json
|
F: qapi-schema.json
|
||||||
|
F: qapi/*.json
|
||||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||||
|
|
||||||
QObject
|
QObject
|
||||||
|
@ -680,8 +680,6 @@ Example:
|
|||||||
out:
|
out:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
$ python scripts/qapi-commands.py --output-dir="qapi-generated" \
|
|
||||||
--prefix="example-" example-schema.json
|
|
||||||
$ cat qapi-generated/example-qapi-visit.h
|
$ cat qapi-generated/example-qapi-visit.h
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
@ -12,9 +12,8 @@
|
|||||||
from ordereddict import OrderedDict
|
from ordereddict import OrderedDict
|
||||||
from qapi import *
|
from qapi import *
|
||||||
|
|
||||||
def generate_fwd_struct(name, members, builtin_type=False):
|
def generate_fwd_builtin(name):
|
||||||
if builtin_type:
|
return mcgen('''
|
||||||
return mcgen('''
|
|
||||||
|
|
||||||
typedef struct %(name)sList
|
typedef struct %(name)sList
|
||||||
{
|
{
|
||||||
@ -25,9 +24,10 @@ typedef struct %(name)sList
|
|||||||
struct %(name)sList *next;
|
struct %(name)sList *next;
|
||||||
} %(name)sList;
|
} %(name)sList;
|
||||||
''',
|
''',
|
||||||
type=c_type(name),
|
type=c_type(name),
|
||||||
name=name)
|
name=name)
|
||||||
|
|
||||||
|
def generate_fwd_struct(name):
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
typedef struct %(name)s %(name)s;
|
typedef struct %(name)s %(name)s;
|
||||||
@ -43,7 +43,7 @@ typedef struct %(name)sList
|
|||||||
''',
|
''',
|
||||||
name=c_name(name))
|
name=c_name(name))
|
||||||
|
|
||||||
def generate_fwd_enum_struct(name, members):
|
def generate_fwd_enum_struct(name):
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
typedef struct %(name)sList
|
typedef struct %(name)sList
|
||||||
{
|
{
|
||||||
@ -75,7 +75,6 @@ def generate_struct_fields(members):
|
|||||||
def generate_struct(expr):
|
def generate_struct(expr):
|
||||||
|
|
||||||
structname = expr.get('struct', "")
|
structname = expr.get('struct', "")
|
||||||
fieldname = expr.get('field', "")
|
|
||||||
members = expr['data']
|
members = expr['data']
|
||||||
base = expr.get('base')
|
base = expr.get('base')
|
||||||
|
|
||||||
@ -98,12 +97,9 @@ struct %(name)s
|
|||||||
char qapi_dummy_field_for_empty_struct;
|
char qapi_dummy_field_for_empty_struct;
|
||||||
''')
|
''')
|
||||||
|
|
||||||
if len(fieldname):
|
|
||||||
fieldname = " " + fieldname
|
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
}%(field)s;
|
};
|
||||||
''',
|
''')
|
||||||
field=fieldname)
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@ -329,30 +325,29 @@ fdecl.write(mcgen('''
|
|||||||
'''))
|
'''))
|
||||||
|
|
||||||
exprs = parse_schema(input_file)
|
exprs = parse_schema(input_file)
|
||||||
exprs = filter(lambda expr: not expr.has_key('gen'), exprs)
|
|
||||||
|
|
||||||
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
|
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
|
||||||
for typename in builtin_types.keys():
|
for typename in builtin_types.keys():
|
||||||
fdecl.write(generate_fwd_struct(typename, None, builtin_type=True))
|
fdecl.write(generate_fwd_builtin(typename))
|
||||||
fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
|
fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
|
||||||
|
|
||||||
for expr in exprs:
|
for expr in exprs:
|
||||||
ret = "\n"
|
ret = "\n"
|
||||||
if expr.has_key('struct'):
|
if expr.has_key('struct'):
|
||||||
ret += generate_fwd_struct(expr['struct'], expr['data'])
|
ret += generate_fwd_struct(expr['struct'])
|
||||||
elif expr.has_key('enum'):
|
elif expr.has_key('enum'):
|
||||||
ret += generate_enum(expr['enum'], expr['data']) + "\n"
|
ret += generate_enum(expr['enum'], expr['data']) + "\n"
|
||||||
ret += generate_fwd_enum_struct(expr['enum'], expr['data'])
|
ret += generate_fwd_enum_struct(expr['enum'])
|
||||||
fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
|
fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
|
||||||
elif expr.has_key('union'):
|
elif expr.has_key('union'):
|
||||||
ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
|
ret += generate_fwd_struct(expr['union']) + "\n"
|
||||||
enum_define = discriminator_find_enum_define(expr)
|
enum_define = discriminator_find_enum_define(expr)
|
||||||
if not enum_define:
|
if not enum_define:
|
||||||
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
|
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
|
||||||
fdef.write(generate_enum_lookup('%sKind' % expr['union'],
|
fdef.write(generate_enum_lookup('%sKind' % expr['union'],
|
||||||
expr['data'].keys()))
|
expr['data'].keys()))
|
||||||
elif expr.has_key('alternate'):
|
elif expr.has_key('alternate'):
|
||||||
ret += generate_fwd_struct(expr['alternate'], expr['data']) + "\n"
|
ret += generate_fwd_struct(expr['alternate']) + "\n"
|
||||||
ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys())
|
ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys())
|
||||||
fdef.write(generate_enum_lookup('%sKind' % expr['alternate'],
|
fdef.write(generate_enum_lookup('%sKind' % expr['alternate'],
|
||||||
expr['data'].keys()))
|
expr['data'].keys()))
|
||||||
|
348
scripts/qapi.py
348
scripts/qapi.py
@ -65,6 +65,10 @@ union_types = []
|
|||||||
events = []
|
events = []
|
||||||
all_names = {}
|
all_names = {}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Parsing the schema into expressions
|
||||||
|
#
|
||||||
|
|
||||||
def error_path(parent):
|
def error_path(parent):
|
||||||
res = ""
|
res = ""
|
||||||
while parent:
|
while parent:
|
||||||
@ -75,7 +79,7 @@ def error_path(parent):
|
|||||||
|
|
||||||
class QAPISchemaError(Exception):
|
class QAPISchemaError(Exception):
|
||||||
def __init__(self, schema, msg):
|
def __init__(self, schema, msg):
|
||||||
self.input_file = schema.input_file
|
self.fname = schema.fname
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
self.col = 1
|
self.col = 1
|
||||||
self.line = schema.line
|
self.line = schema.line
|
||||||
@ -84,11 +88,11 @@ class QAPISchemaError(Exception):
|
|||||||
self.col = (self.col + 7) % 8 + 1
|
self.col = (self.col + 7) % 8 + 1
|
||||||
else:
|
else:
|
||||||
self.col += 1
|
self.col += 1
|
||||||
self.info = schema.parent_info
|
self.info = schema.incl_info
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return error_path(self.info) + \
|
return error_path(self.info) + \
|
||||||
"%s:%d:%d: %s" % (self.input_file, self.line, self.col, self.msg)
|
"%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
|
||||||
|
|
||||||
class QAPIExprError(Exception):
|
class QAPIExprError(Exception):
|
||||||
def __init__(self, expr_info, msg):
|
def __init__(self, expr_info, msg):
|
||||||
@ -101,19 +105,12 @@ class QAPIExprError(Exception):
|
|||||||
|
|
||||||
class QAPISchema:
|
class QAPISchema:
|
||||||
|
|
||||||
def __init__(self, fp, input_relname=None, include_hist=[],
|
def __init__(self, fp, previously_included = [], incl_info = None):
|
||||||
previously_included=[], parent_info=None):
|
abs_fname = os.path.abspath(fp.name)
|
||||||
""" include_hist is a stack used to detect inclusion cycles
|
fname = fp.name
|
||||||
previously_included is a global state used to avoid multiple
|
self.fname = fname
|
||||||
inclusions of the same file"""
|
previously_included.append(abs_fname)
|
||||||
input_fname = os.path.abspath(fp.name)
|
self.incl_info = incl_info
|
||||||
if input_relname is None:
|
|
||||||
input_relname = fp.name
|
|
||||||
self.input_dir = os.path.dirname(input_fname)
|
|
||||||
self.input_file = input_relname
|
|
||||||
self.include_hist = include_hist + [(input_relname, input_fname)]
|
|
||||||
previously_included.append(input_fname)
|
|
||||||
self.parent_info = parent_info
|
|
||||||
self.src = fp.read()
|
self.src = fp.read()
|
||||||
if self.src == '' or self.src[-1] != '\n':
|
if self.src == '' or self.src[-1] != '\n':
|
||||||
self.src += '\n'
|
self.src += '\n'
|
||||||
@ -124,7 +121,8 @@ class QAPISchema:
|
|||||||
self.accept()
|
self.accept()
|
||||||
|
|
||||||
while self.tok != None:
|
while self.tok != None:
|
||||||
expr_info = {'file': input_relname, 'line': self.line, 'parent': self.parent_info}
|
expr_info = {'file': fname, 'line': self.line,
|
||||||
|
'parent': self.incl_info}
|
||||||
expr = self.get_expr(False)
|
expr = self.get_expr(False)
|
||||||
if isinstance(expr, dict) and "include" in expr:
|
if isinstance(expr, dict) and "include" in expr:
|
||||||
if len(expr) != 1:
|
if len(expr) != 1:
|
||||||
@ -134,21 +132,25 @@ class QAPISchema:
|
|||||||
raise QAPIExprError(expr_info,
|
raise QAPIExprError(expr_info,
|
||||||
'Expected a file name (string), got: %s'
|
'Expected a file name (string), got: %s'
|
||||||
% include)
|
% include)
|
||||||
include_path = os.path.join(self.input_dir, include)
|
incl_abs_fname = os.path.join(os.path.dirname(abs_fname),
|
||||||
for elem in self.include_hist:
|
include)
|
||||||
if include_path == elem[1]:
|
# catch inclusion cycle
|
||||||
|
inf = expr_info
|
||||||
|
while inf:
|
||||||
|
if incl_abs_fname == os.path.abspath(inf['file']):
|
||||||
raise QAPIExprError(expr_info, "Inclusion loop for %s"
|
raise QAPIExprError(expr_info, "Inclusion loop for %s"
|
||||||
% include)
|
% include)
|
||||||
|
inf = inf['parent']
|
||||||
# skip multiple include of the same file
|
# skip multiple include of the same file
|
||||||
if include_path in previously_included:
|
if incl_abs_fname in previously_included:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
fobj = open(include_path, 'r')
|
fobj = open(incl_abs_fname, 'r')
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
raise QAPIExprError(expr_info,
|
raise QAPIExprError(expr_info,
|
||||||
'%s: %s' % (e.strerror, include))
|
'%s: %s' % (e.strerror, include))
|
||||||
exprs_include = QAPISchema(fobj, include, self.include_hist,
|
exprs_include = QAPISchema(fobj, previously_included,
|
||||||
previously_included, expr_info)
|
expr_info)
|
||||||
self.exprs.extend(exprs_include.exprs)
|
self.exprs.extend(exprs_include.exprs)
|
||||||
else:
|
else:
|
||||||
expr_elem = {'expr': expr,
|
expr_elem = {'expr': expr,
|
||||||
@ -219,20 +221,18 @@ class QAPISchema:
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
string += ch
|
string += ch
|
||||||
elif self.tok in "tfn":
|
elif self.src.startswith("true", self.pos):
|
||||||
val = self.src[self.cursor - 1:]
|
self.val = True
|
||||||
if val.startswith("true"):
|
self.cursor += 3
|
||||||
self.val = True
|
return
|
||||||
self.cursor += 3
|
elif self.src.startswith("false", self.pos):
|
||||||
return
|
self.val = False
|
||||||
elif val.startswith("false"):
|
self.cursor += 4
|
||||||
self.val = False
|
return
|
||||||
self.cursor += 4
|
elif self.src.startswith("null", self.pos):
|
||||||
return
|
self.val = None
|
||||||
elif val.startswith("null"):
|
self.cursor += 3
|
||||||
self.val = None
|
return
|
||||||
self.cursor += 3
|
|
||||||
return
|
|
||||||
elif self.tok == '\n':
|
elif self.tok == '\n':
|
||||||
if self.cursor == len(self.src):
|
if self.cursor == len(self.src):
|
||||||
self.tok = None
|
self.tok = None
|
||||||
@ -300,6 +300,10 @@ class QAPISchema:
|
|||||||
raise QAPISchemaError(self, 'Expected "{", "[" or string')
|
raise QAPISchemaError(self, 'Expected "{", "[" or string')
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
|
#
|
||||||
|
# Semantic analysis of schema expressions
|
||||||
|
#
|
||||||
|
|
||||||
def find_base_fields(base):
|
def find_base_fields(base):
|
||||||
base_struct_define = find_struct(base)
|
base_struct_define = find_struct(base)
|
||||||
if not base_struct_define:
|
if not base_struct_define:
|
||||||
@ -360,6 +364,60 @@ def check_name(expr_info, source, name, allow_optional = False,
|
|||||||
raise QAPIExprError(expr_info,
|
raise QAPIExprError(expr_info,
|
||||||
"%s uses invalid name '%s'" % (source, name))
|
"%s uses invalid name '%s'" % (source, name))
|
||||||
|
|
||||||
|
def add_name(name, info, meta, implicit = False):
|
||||||
|
global all_names
|
||||||
|
check_name(info, "'%s'" % meta, name)
|
||||||
|
if name in all_names:
|
||||||
|
raise QAPIExprError(info,
|
||||||
|
"%s '%s' is already defined"
|
||||||
|
% (all_names[name], name))
|
||||||
|
if not implicit and name[-4:] == 'Kind':
|
||||||
|
raise QAPIExprError(info,
|
||||||
|
"%s '%s' should not end in 'Kind'"
|
||||||
|
% (meta, name))
|
||||||
|
all_names[name] = meta
|
||||||
|
|
||||||
|
def add_struct(definition, info):
|
||||||
|
global struct_types
|
||||||
|
name = definition['struct']
|
||||||
|
add_name(name, info, 'struct')
|
||||||
|
struct_types.append(definition)
|
||||||
|
|
||||||
|
def find_struct(name):
|
||||||
|
global struct_types
|
||||||
|
for struct in struct_types:
|
||||||
|
if struct['struct'] == name:
|
||||||
|
return struct
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add_union(definition, info):
|
||||||
|
global union_types
|
||||||
|
name = definition['union']
|
||||||
|
add_name(name, info, 'union')
|
||||||
|
union_types.append(definition)
|
||||||
|
|
||||||
|
def find_union(name):
|
||||||
|
global union_types
|
||||||
|
for union in union_types:
|
||||||
|
if union['union'] == name:
|
||||||
|
return union
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add_enum(name, info, enum_values = None, implicit = False):
|
||||||
|
global enum_types
|
||||||
|
add_name(name, info, 'enum', implicit)
|
||||||
|
enum_types.append({"enum_name": name, "enum_values": enum_values})
|
||||||
|
|
||||||
|
def find_enum(name):
|
||||||
|
global enum_types
|
||||||
|
for enum in enum_types:
|
||||||
|
if enum['enum_name'] == name:
|
||||||
|
return enum
|
||||||
|
return None
|
||||||
|
|
||||||
|
def is_enum(name):
|
||||||
|
return find_enum(name) != None
|
||||||
|
|
||||||
def check_type(expr_info, source, value, allow_array = False,
|
def check_type(expr_info, source, value, allow_array = False,
|
||||||
allow_dict = False, allow_optional = False,
|
allow_dict = False, allow_optional = False,
|
||||||
allow_star = False, allow_metas = []):
|
allow_star = False, allow_metas = []):
|
||||||
@ -522,7 +580,7 @@ def check_union(expr, expr_info):
|
|||||||
# Each value must name a known type; furthermore, in flat unions,
|
# Each value must name a known type; furthermore, in flat unions,
|
||||||
# branches must be a struct with no overlapping member names
|
# branches must be a struct with no overlapping member names
|
||||||
check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
|
check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
|
||||||
value, allow_array=True, allow_metas=allow_metas)
|
value, allow_array=not base, allow_metas=allow_metas)
|
||||||
if base:
|
if base:
|
||||||
branch_struct = find_struct(value)
|
branch_struct = find_struct(value)
|
||||||
assert branch_struct
|
assert branch_struct
|
||||||
@ -607,26 +665,6 @@ def check_struct(expr, expr_info):
|
|||||||
if expr.get('base'):
|
if expr.get('base'):
|
||||||
check_member_clash(expr_info, expr['base'], expr['data'])
|
check_member_clash(expr_info, expr['base'], expr['data'])
|
||||||
|
|
||||||
def check_exprs(schema):
|
|
||||||
for expr_elem in schema.exprs:
|
|
||||||
expr = expr_elem['expr']
|
|
||||||
info = expr_elem['info']
|
|
||||||
|
|
||||||
if expr.has_key('enum'):
|
|
||||||
check_enum(expr, info)
|
|
||||||
elif expr.has_key('union'):
|
|
||||||
check_union(expr, info)
|
|
||||||
elif expr.has_key('alternate'):
|
|
||||||
check_alternate(expr, info)
|
|
||||||
elif expr.has_key('struct'):
|
|
||||||
check_struct(expr, info)
|
|
||||||
elif expr.has_key('command'):
|
|
||||||
check_command(expr, info)
|
|
||||||
elif expr.has_key('event'):
|
|
||||||
check_event(expr, info)
|
|
||||||
else:
|
|
||||||
assert False, 'unexpected meta type'
|
|
||||||
|
|
||||||
def check_keys(expr_elem, meta, required, optional=[]):
|
def check_keys(expr_elem, meta, required, optional=[]):
|
||||||
expr = expr_elem['expr']
|
expr = expr_elem['expr']
|
||||||
info = expr_elem['info']
|
info = expr_elem['info']
|
||||||
@ -650,69 +688,83 @@ def check_keys(expr_elem, meta, required, optional=[]):
|
|||||||
"Key '%s' is missing from %s '%s'"
|
"Key '%s' is missing from %s '%s'"
|
||||||
% (key, meta, name))
|
% (key, meta, name))
|
||||||
|
|
||||||
|
def check_exprs(exprs):
|
||||||
def parse_schema(input_file):
|
|
||||||
global all_names
|
global all_names
|
||||||
exprs = []
|
|
||||||
|
|
||||||
# First pass: read entire file into memory
|
# Learn the types and check for valid expression keys
|
||||||
|
for builtin in builtin_types.keys():
|
||||||
|
all_names[builtin] = 'built-in'
|
||||||
|
for expr_elem in exprs:
|
||||||
|
expr = expr_elem['expr']
|
||||||
|
info = expr_elem['info']
|
||||||
|
if expr.has_key('enum'):
|
||||||
|
check_keys(expr_elem, 'enum', ['data'])
|
||||||
|
add_enum(expr['enum'], info, expr['data'])
|
||||||
|
elif expr.has_key('union'):
|
||||||
|
check_keys(expr_elem, 'union', ['data'],
|
||||||
|
['base', 'discriminator'])
|
||||||
|
add_union(expr, info)
|
||||||
|
elif expr.has_key('alternate'):
|
||||||
|
check_keys(expr_elem, 'alternate', ['data'])
|
||||||
|
add_name(expr['alternate'], info, 'alternate')
|
||||||
|
elif expr.has_key('struct'):
|
||||||
|
check_keys(expr_elem, 'struct', ['data'], ['base'])
|
||||||
|
add_struct(expr, info)
|
||||||
|
elif expr.has_key('command'):
|
||||||
|
check_keys(expr_elem, 'command', [],
|
||||||
|
['data', 'returns', 'gen', 'success-response'])
|
||||||
|
add_name(expr['command'], info, 'command')
|
||||||
|
elif expr.has_key('event'):
|
||||||
|
check_keys(expr_elem, 'event', [], ['data'])
|
||||||
|
add_name(expr['event'], info, 'event')
|
||||||
|
else:
|
||||||
|
raise QAPIExprError(expr_elem['info'],
|
||||||
|
"Expression is missing metatype")
|
||||||
|
|
||||||
|
# Try again for hidden UnionKind enum
|
||||||
|
for expr_elem in exprs:
|
||||||
|
expr = expr_elem['expr']
|
||||||
|
if expr.has_key('union'):
|
||||||
|
if not discriminator_find_enum_define(expr):
|
||||||
|
add_enum('%sKind' % expr['union'], expr_elem['info'],
|
||||||
|
implicit=True)
|
||||||
|
elif expr.has_key('alternate'):
|
||||||
|
add_enum('%sKind' % expr['alternate'], expr_elem['info'],
|
||||||
|
implicit=True)
|
||||||
|
|
||||||
|
# Validate that exprs make sense
|
||||||
|
for expr_elem in exprs:
|
||||||
|
expr = expr_elem['expr']
|
||||||
|
info = expr_elem['info']
|
||||||
|
|
||||||
|
if expr.has_key('enum'):
|
||||||
|
check_enum(expr, info)
|
||||||
|
elif expr.has_key('union'):
|
||||||
|
check_union(expr, info)
|
||||||
|
elif expr.has_key('alternate'):
|
||||||
|
check_alternate(expr, info)
|
||||||
|
elif expr.has_key('struct'):
|
||||||
|
check_struct(expr, info)
|
||||||
|
elif expr.has_key('command'):
|
||||||
|
check_command(expr, info)
|
||||||
|
elif expr.has_key('event'):
|
||||||
|
check_event(expr, info)
|
||||||
|
else:
|
||||||
|
assert False, 'unexpected meta type'
|
||||||
|
|
||||||
|
return map(lambda expr_elem: expr_elem['expr'], exprs)
|
||||||
|
|
||||||
|
def parse_schema(fname):
|
||||||
try:
|
try:
|
||||||
schema = QAPISchema(open(input_file, "r"))
|
schema = QAPISchema(open(fname, "r"))
|
||||||
|
return check_exprs(schema.exprs)
|
||||||
except (QAPISchemaError, QAPIExprError), e:
|
except (QAPISchemaError, QAPIExprError), e:
|
||||||
print >>sys.stderr, e
|
print >>sys.stderr, e
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
try:
|
#
|
||||||
# Next pass: learn the types and check for valid expression keys. At
|
# Code generation helpers
|
||||||
# this point, top-level 'include' has already been flattened.
|
#
|
||||||
for builtin in builtin_types.keys():
|
|
||||||
all_names[builtin] = 'built-in'
|
|
||||||
for expr_elem in schema.exprs:
|
|
||||||
expr = expr_elem['expr']
|
|
||||||
info = expr_elem['info']
|
|
||||||
if expr.has_key('enum'):
|
|
||||||
check_keys(expr_elem, 'enum', ['data'])
|
|
||||||
add_enum(expr['enum'], info, expr['data'])
|
|
||||||
elif expr.has_key('union'):
|
|
||||||
check_keys(expr_elem, 'union', ['data'],
|
|
||||||
['base', 'discriminator'])
|
|
||||||
add_union(expr, info)
|
|
||||||
elif expr.has_key('alternate'):
|
|
||||||
check_keys(expr_elem, 'alternate', ['data'])
|
|
||||||
add_name(expr['alternate'], info, 'alternate')
|
|
||||||
elif expr.has_key('struct'):
|
|
||||||
check_keys(expr_elem, 'struct', ['data'], ['base'])
|
|
||||||
add_struct(expr, info)
|
|
||||||
elif expr.has_key('command'):
|
|
||||||
check_keys(expr_elem, 'command', [],
|
|
||||||
['data', 'returns', 'gen', 'success-response'])
|
|
||||||
add_name(expr['command'], info, 'command')
|
|
||||||
elif expr.has_key('event'):
|
|
||||||
check_keys(expr_elem, 'event', [], ['data'])
|
|
||||||
add_name(expr['event'], info, 'event')
|
|
||||||
else:
|
|
||||||
raise QAPIExprError(expr_elem['info'],
|
|
||||||
"Expression is missing metatype")
|
|
||||||
exprs.append(expr)
|
|
||||||
|
|
||||||
# Try again for hidden UnionKind enum
|
|
||||||
for expr_elem in schema.exprs:
|
|
||||||
expr = expr_elem['expr']
|
|
||||||
if expr.has_key('union'):
|
|
||||||
if not discriminator_find_enum_define(expr):
|
|
||||||
add_enum('%sKind' % expr['union'], expr_elem['info'],
|
|
||||||
implicit=True)
|
|
||||||
elif expr.has_key('alternate'):
|
|
||||||
add_enum('%sKind' % expr['alternate'], expr_elem['info'],
|
|
||||||
implicit=True)
|
|
||||||
|
|
||||||
# Final pass - validate that exprs make sense
|
|
||||||
check_exprs(schema)
|
|
||||||
except QAPIExprError, e:
|
|
||||||
print >>sys.stderr, e
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
return exprs
|
|
||||||
|
|
||||||
def parse_args(typeinfo):
|
def parse_args(typeinfo):
|
||||||
if isinstance(typeinfo, str):
|
if isinstance(typeinfo, str):
|
||||||
@ -831,60 +883,6 @@ def type_name(value):
|
|||||||
return value
|
return value
|
||||||
return c_name(value)
|
return c_name(value)
|
||||||
|
|
||||||
def add_name(name, info, meta, implicit = False):
|
|
||||||
global all_names
|
|
||||||
check_name(info, "'%s'" % meta, name)
|
|
||||||
if name in all_names:
|
|
||||||
raise QAPIExprError(info,
|
|
||||||
"%s '%s' is already defined"
|
|
||||||
% (all_names[name], name))
|
|
||||||
if not implicit and name[-4:] == 'Kind':
|
|
||||||
raise QAPIExprError(info,
|
|
||||||
"%s '%s' should not end in 'Kind'"
|
|
||||||
% (meta, name))
|
|
||||||
all_names[name] = meta
|
|
||||||
|
|
||||||
def add_struct(definition, info):
|
|
||||||
global struct_types
|
|
||||||
name = definition['struct']
|
|
||||||
add_name(name, info, 'struct')
|
|
||||||
struct_types.append(definition)
|
|
||||||
|
|
||||||
def find_struct(name):
|
|
||||||
global struct_types
|
|
||||||
for struct in struct_types:
|
|
||||||
if struct['struct'] == name:
|
|
||||||
return struct
|
|
||||||
return None
|
|
||||||
|
|
||||||
def add_union(definition, info):
|
|
||||||
global union_types
|
|
||||||
name = definition['union']
|
|
||||||
add_name(name, info, 'union')
|
|
||||||
union_types.append(definition)
|
|
||||||
|
|
||||||
def find_union(name):
|
|
||||||
global union_types
|
|
||||||
for union in union_types:
|
|
||||||
if union['union'] == name:
|
|
||||||
return union
|
|
||||||
return None
|
|
||||||
|
|
||||||
def add_enum(name, info, enum_values = None, implicit = False):
|
|
||||||
global enum_types
|
|
||||||
add_name(name, info, 'enum', implicit)
|
|
||||||
enum_types.append({"enum_name": name, "enum_values": enum_values})
|
|
||||||
|
|
||||||
def find_enum(name):
|
|
||||||
global enum_types
|
|
||||||
for enum in enum_types:
|
|
||||||
if enum['enum_name'] == name:
|
|
||||||
return enum
|
|
||||||
return None
|
|
||||||
|
|
||||||
def is_enum(name):
|
|
||||||
return find_enum(name) != None
|
|
||||||
|
|
||||||
eatspace = '\033EATSPACE.'
|
eatspace = '\033EATSPACE.'
|
||||||
pointer_suffix = ' *' + eatspace
|
pointer_suffix = ' *' + eatspace
|
||||||
|
|
||||||
@ -981,6 +979,10 @@ def guardend(name):
|
|||||||
''',
|
''',
|
||||||
name=guardname(name))
|
name=guardname(name))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Common command line parsing
|
||||||
|
#
|
||||||
|
|
||||||
def parse_command_line(extra_options = "", extra_long_options = []):
|
def parse_command_line(extra_options = "", extra_long_options = []):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -1018,9 +1020,13 @@ def parse_command_line(extra_options = "", extra_long_options = []):
|
|||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
|
print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
input_file = args[0]
|
fname = args[0]
|
||||||
|
|
||||||
return (input_file, output_dir, do_c, do_h, prefix, extra_opts)
|
return (fname, output_dir, do_c, do_h, prefix, extra_opts)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generate output files with boilerplate
|
||||||
|
#
|
||||||
|
|
||||||
def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
|
def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
|
||||||
c_comment, h_comment):
|
c_comment, h_comment):
|
||||||
|
@ -237,7 +237,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
|
|||||||
flat-union-invalid-branch-key.json flat-union-reverse-define.json \
|
flat-union-invalid-branch-key.json flat-union-reverse-define.json \
|
||||||
flat-union-string-discriminator.json union-base-no-discriminator.json \
|
flat-union-string-discriminator.json union-base-no-discriminator.json \
|
||||||
flat-union-bad-discriminator.json flat-union-bad-base.json \
|
flat-union-bad-discriminator.json flat-union-bad-base.json \
|
||||||
flat-union-base-star.json flat-union-int-branch.json \
|
flat-union-base-star.json \
|
||||||
|
flat-union-array-branch.json flat-union-int-branch.json \
|
||||||
flat-union-base-union.json flat-union-branch-clash.json \
|
flat-union-base-union.json flat-union-branch-clash.json \
|
||||||
alternate-nested.json alternate-unknown.json alternate-clash.json \
|
alternate-nested.json alternate-unknown.json alternate-clash.json \
|
||||||
alternate-good.json alternate-base.json alternate-array.json \
|
alternate-good.json alternate-base.json alternate-array.json \
|
||||||
|
1
tests/qapi-schema/flat-union-array-branch.err
Normal file
1
tests/qapi-schema/flat-union-array-branch.err
Normal file
@ -0,0 +1 @@
|
|||||||
|
tests/qapi-schema/flat-union-array-branch.json:8: Member 'value1' of union 'TestUnion' cannot be an array
|
1
tests/qapi-schema/flat-union-array-branch.exit
Normal file
1
tests/qapi-schema/flat-union-array-branch.exit
Normal file
@ -0,0 +1 @@
|
|||||||
|
1
|
12
tests/qapi-schema/flat-union-array-branch.json
Normal file
12
tests/qapi-schema/flat-union-array-branch.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# we require flat union branches to be a struct
|
||||||
|
{ 'enum': 'TestEnum',
|
||||||
|
'data': [ 'value1', 'value2' ] }
|
||||||
|
{ 'struct': 'Base',
|
||||||
|
'data': { 'enum1': 'TestEnum' } }
|
||||||
|
{ 'struct': 'TestTypeB',
|
||||||
|
'data': { 'integer': 'int' } }
|
||||||
|
{ 'union': 'TestUnion',
|
||||||
|
'base': 'Base',
|
||||||
|
'discriminator': 'enum1',
|
||||||
|
'data': { 'value1': ['TestTypeB'],
|
||||||
|
'value2': 'TestTypeB' } }
|
0
tests/qapi-schema/flat-union-array-branch.out
Normal file
0
tests/qapi-schema/flat-union-array-branch.out
Normal file
@ -1,3 +1,3 @@
|
|||||||
In file included from tests/qapi-schema/include-cycle.json:1:
|
In file included from tests/qapi-schema/include-cycle.json:1:
|
||||||
In file included from include-cycle-b.json:1:
|
In file included from tests/qapi-schema/include-cycle-b.json:1:
|
||||||
include-cycle-c.json:1: Inclusion loop for include-cycle.json
|
tests/qapi-schema/include-cycle-c.json:1: Inclusion loop for include-cycle.json
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
In file included from tests/qapi-schema/include-nested-err.json:1:
|
In file included from tests/qapi-schema/include-nested-err.json:1:
|
||||||
missing-colon.json:1:10: Expected ":"
|
tests/qapi-schema/missing-colon.json:1:10: Expected ":"
|
||||||
|
Loading…
Reference in New Issue
Block a user