qapi: Add a primitive to include other files from a QAPI schema file
The primitive uses JSON syntax, and include paths are relative to the file using the directive: { 'include': 'path/to/file.json' } Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
This commit is contained in:
parent
33aaad529e
commit
a719a27c82
@ -40,6 +40,17 @@ enumeration types and union types.
|
||||
Generally speaking, types definitions should always use CamelCase for the type
|
||||
names. Command names should be all lower case with words separated by a hyphen.
|
||||
|
||||
|
||||
=== Includes ===
|
||||
|
||||
The QAPI schema definitions can be modularized using the 'include' directive:
|
||||
|
||||
{ 'include': 'path/to/file.json'}
|
||||
|
||||
The directive is evaluated recursively, and include paths are relative to the
|
||||
file using the directive.
|
||||
|
||||
|
||||
=== Complex types ===
|
||||
|
||||
A complex type is a dictionary containing a single key whose value is a
|
||||
|
@ -11,6 +11,7 @@
|
||||
# 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 ordereddict import OrderedDict
|
||||
import os
|
||||
import sys
|
||||
@ -36,9 +37,17 @@ builtin_type_qtypes = {
|
||||
'uint64': 'QTYPE_QINT',
|
||||
}
|
||||
|
||||
def error_path(parent):
|
||||
res = ""
|
||||
while parent:
|
||||
res = ("In file included from %s:%d:\n" % (parent['file'],
|
||||
parent['line'])) + res
|
||||
parent = parent['parent']
|
||||
return res
|
||||
|
||||
class QAPISchemaError(Exception):
|
||||
def __init__(self, schema, msg):
|
||||
self.fp = schema.fp
|
||||
self.input_file = schema.input_file
|
||||
self.msg = msg
|
||||
self.col = 1
|
||||
self.line = schema.line
|
||||
@ -47,23 +56,31 @@ class QAPISchemaError(Exception):
|
||||
self.col = (self.col + 7) % 8 + 1
|
||||
else:
|
||||
self.col += 1
|
||||
self.info = schema.parent_info
|
||||
|
||||
def __str__(self):
|
||||
return "%s:%s:%s: %s" % (self.fp.name, self.line, self.col, self.msg)
|
||||
return error_path(self.info) + \
|
||||
"%s:%d:%d: %s" % (self.input_file, self.line, self.col, self.msg)
|
||||
|
||||
class QAPIExprError(Exception):
|
||||
def __init__(self, expr_info, msg):
|
||||
self.fp = expr_info['fp']
|
||||
self.line = expr_info['line']
|
||||
self.info = expr_info
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return "%s:%s: %s" % (self.fp.name, self.line, self.msg)
|
||||
return error_path(self.info['parent']) + \
|
||||
"%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
|
||||
|
||||
class QAPISchema:
|
||||
|
||||
def __init__(self, fp):
|
||||
self.fp = fp
|
||||
def __init__(self, fp, input_relname=None, include_hist=[], parent_info=None):
|
||||
input_fname = os.path.abspath(fp.name)
|
||||
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)]
|
||||
self.parent_info = parent_info
|
||||
self.src = fp.read()
|
||||
if self.src == '' or self.src[-1] != '\n':
|
||||
self.src += '\n'
|
||||
@ -74,10 +91,33 @@ class QAPISchema:
|
||||
self.accept()
|
||||
|
||||
while self.tok != None:
|
||||
expr_info = {'fp': fp, 'line': self.line}
|
||||
expr_elem = {'expr': self.get_expr(False),
|
||||
'info': expr_info}
|
||||
self.exprs.append(expr_elem)
|
||||
expr_info = {'file': input_relname, 'line': self.line, 'parent': self.parent_info}
|
||||
expr = self.get_expr(False)
|
||||
if isinstance(expr, dict) and "include" in expr:
|
||||
if len(expr) != 1:
|
||||
raise QAPIExprError(expr_info, "Invalid 'include' directive")
|
||||
include = expr["include"]
|
||||
if not isinstance(include, str):
|
||||
raise QAPIExprError(expr_info,
|
||||
'Expected a file name (string), got: %s'
|
||||
% include)
|
||||
include_path = os.path.join(self.input_dir, include)
|
||||
if any(include_path == elem[1]
|
||||
for elem in self.include_hist):
|
||||
raise QAPIExprError(expr_info, "Inclusion loop for %s"
|
||||
% include)
|
||||
try:
|
||||
fobj = open(include_path, 'r')
|
||||
except IOError as e:
|
||||
raise QAPIExprError(expr_info,
|
||||
'%s: %s' % (e.strerror, include))
|
||||
exprs_include = QAPISchema(fobj, include,
|
||||
self.include_hist, expr_info)
|
||||
self.exprs.extend(exprs_include.exprs)
|
||||
else:
|
||||
expr_elem = {'expr': expr,
|
||||
'info': expr_info}
|
||||
self.exprs.append(expr_elem)
|
||||
|
||||
def accept(self):
|
||||
while True:
|
||||
@ -267,7 +307,7 @@ def check_exprs(schema):
|
||||
def parse_schema(input_file):
|
||||
try:
|
||||
schema = QAPISchema(open(input_file, "r"))
|
||||
except QAPISchemaError, e:
|
||||
except (QAPISchemaError, QAPIExprError), e:
|
||||
print >>sys.stderr, e
|
||||
exit(1)
|
||||
|
||||
|
@ -190,7 +190,10 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
|
||||
duplicate-key.json union-invalid-base.json flat-union-no-base.json \
|
||||
flat-union-invalid-discriminator.json \
|
||||
flat-union-invalid-branch-key.json flat-union-reverse-define.json \
|
||||
flat-union-string-discriminator.json)
|
||||
flat-union-string-discriminator.json \
|
||||
include-simple.json include-relpath.json include-format-err.json \
|
||||
include-non-file.json include-no-file.json include-before-err.json \
|
||||
include-nested-err.json include-self-cycle.json include-cycle.json)
|
||||
|
||||
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
|
||||
|
||||
|
1
tests/qapi-schema/include-before-err.err
Normal file
1
tests/qapi-schema/include-before-err.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/include-before-err.json:2:13: Expected ":"
|
1
tests/qapi-schema/include-before-err.exit
Normal file
1
tests/qapi-schema/include-before-err.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/include-before-err.json
Normal file
2
tests/qapi-schema/include-before-err.json
Normal file
@ -0,0 +1,2 @@
|
||||
{ 'include': 'include-simple-sub.json' }
|
||||
{ 'command' 'missing-colon' }
|
0
tests/qapi-schema/include-before-err.out
Normal file
0
tests/qapi-schema/include-before-err.out
Normal file
1
tests/qapi-schema/include-cycle-b.json
Normal file
1
tests/qapi-schema/include-cycle-b.json
Normal file
@ -0,0 +1 @@
|
||||
{ 'include': 'include-cycle-c.json' }
|
1
tests/qapi-schema/include-cycle-c.json
Normal file
1
tests/qapi-schema/include-cycle-c.json
Normal file
@ -0,0 +1 @@
|
||||
{ 'include': 'include-cycle.json' }
|
3
tests/qapi-schema/include-cycle.err
Normal file
3
tests/qapi-schema/include-cycle.err
Normal file
@ -0,0 +1,3 @@
|
||||
In file included from tests/qapi-schema/include-cycle.json:1:
|
||||
In file included from include-cycle-b.json:1:
|
||||
include-cycle-c.json:1: Inclusion loop for include-cycle.json
|
1
tests/qapi-schema/include-cycle.exit
Normal file
1
tests/qapi-schema/include-cycle.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
1
tests/qapi-schema/include-cycle.json
Normal file
1
tests/qapi-schema/include-cycle.json
Normal file
@ -0,0 +1 @@
|
||||
{ 'include': 'include-cycle-b.json' }
|
0
tests/qapi-schema/include-cycle.out
Normal file
0
tests/qapi-schema/include-cycle.out
Normal file
1
tests/qapi-schema/include-format-err.err
Normal file
1
tests/qapi-schema/include-format-err.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/include-format-err.json:1: Invalid 'include' directive
|
1
tests/qapi-schema/include-format-err.exit
Normal file
1
tests/qapi-schema/include-format-err.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/include-format-err.json
Normal file
2
tests/qapi-schema/include-format-err.json
Normal file
@ -0,0 +1,2 @@
|
||||
{ 'include': 'include-simple-sub.json',
|
||||
'foo': 'bar' }
|
0
tests/qapi-schema/include-format-err.out
Normal file
0
tests/qapi-schema/include-format-err.out
Normal file
2
tests/qapi-schema/include-nested-err.err
Normal file
2
tests/qapi-schema/include-nested-err.err
Normal file
@ -0,0 +1,2 @@
|
||||
In file included from tests/qapi-schema/include-nested-err.json:1:
|
||||
missing-colon.json:1:10: Expected ":"
|
1
tests/qapi-schema/include-nested-err.exit
Normal file
1
tests/qapi-schema/include-nested-err.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
1
tests/qapi-schema/include-nested-err.json
Normal file
1
tests/qapi-schema/include-nested-err.json
Normal file
@ -0,0 +1 @@
|
||||
{ 'include': 'missing-colon.json' }
|
0
tests/qapi-schema/include-nested-err.out
Normal file
0
tests/qapi-schema/include-nested-err.out
Normal file
1
tests/qapi-schema/include-no-file.err
Normal file
1
tests/qapi-schema/include-no-file.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/include-no-file.json:1: No such file or directory: include-no-file-sub.json
|
1
tests/qapi-schema/include-no-file.exit
Normal file
1
tests/qapi-schema/include-no-file.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
1
tests/qapi-schema/include-no-file.json
Normal file
1
tests/qapi-schema/include-no-file.json
Normal file
@ -0,0 +1 @@
|
||||
{ 'include': 'include-no-file-sub.json' }
|
0
tests/qapi-schema/include-no-file.out
Normal file
0
tests/qapi-schema/include-no-file.out
Normal file
1
tests/qapi-schema/include-non-file.err
Normal file
1
tests/qapi-schema/include-non-file.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/include-non-file.json:1: Expected a file name (string), got: ['foo', 'bar']
|
1
tests/qapi-schema/include-non-file.exit
Normal file
1
tests/qapi-schema/include-non-file.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
1
tests/qapi-schema/include-non-file.json
Normal file
1
tests/qapi-schema/include-non-file.json
Normal file
@ -0,0 +1 @@
|
||||
{ 'include': [ 'foo', 'bar' ] }
|
0
tests/qapi-schema/include-non-file.out
Normal file
0
tests/qapi-schema/include-non-file.out
Normal file
2
tests/qapi-schema/include-relpath-sub.json
Normal file
2
tests/qapi-schema/include-relpath-sub.json
Normal file
@ -0,0 +1,2 @@
|
||||
{ 'enum': 'Status',
|
||||
'data': [ 'good', 'bad', 'ugly' ] }
|
0
tests/qapi-schema/include-relpath.err
Normal file
0
tests/qapi-schema/include-relpath.err
Normal file
1
tests/qapi-schema/include-relpath.exit
Normal file
1
tests/qapi-schema/include-relpath.exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
1
tests/qapi-schema/include-relpath.json
Normal file
1
tests/qapi-schema/include-relpath.json
Normal file
@ -0,0 +1 @@
|
||||
{ 'include': 'include/relpath.json' }
|
3
tests/qapi-schema/include-relpath.out
Normal file
3
tests/qapi-schema/include-relpath.out
Normal file
@ -0,0 +1,3 @@
|
||||
[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
|
||||
[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
|
||||
[]
|
1
tests/qapi-schema/include-self-cycle.err
Normal file
1
tests/qapi-schema/include-self-cycle.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/include-self-cycle.json:1: Inclusion loop for include-self-cycle.json
|
1
tests/qapi-schema/include-self-cycle.exit
Normal file
1
tests/qapi-schema/include-self-cycle.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
1
tests/qapi-schema/include-self-cycle.json
Normal file
1
tests/qapi-schema/include-self-cycle.json
Normal file
@ -0,0 +1 @@
|
||||
{ 'include': 'include-self-cycle.json' }
|
0
tests/qapi-schema/include-self-cycle.out
Normal file
0
tests/qapi-schema/include-self-cycle.out
Normal file
2
tests/qapi-schema/include-simple-sub.json
Normal file
2
tests/qapi-schema/include-simple-sub.json
Normal file
@ -0,0 +1,2 @@
|
||||
{ 'enum': 'Status',
|
||||
'data': [ 'good', 'bad', 'ugly' ] }
|
0
tests/qapi-schema/include-simple.err
Normal file
0
tests/qapi-schema/include-simple.err
Normal file
1
tests/qapi-schema/include-simple.exit
Normal file
1
tests/qapi-schema/include-simple.exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
1
tests/qapi-schema/include-simple.json
Normal file
1
tests/qapi-schema/include-simple.json
Normal file
@ -0,0 +1 @@
|
||||
{ 'include': 'include-simple-sub.json' }
|
3
tests/qapi-schema/include-simple.out
Normal file
3
tests/qapi-schema/include-simple.out
Normal file
@ -0,0 +1,3 @@
|
||||
[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
|
||||
[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
|
||||
[]
|
1
tests/qapi-schema/include/relpath.json
Normal file
1
tests/qapi-schema/include/relpath.json
Normal file
@ -0,0 +1 @@
|
||||
{ 'include': '../include-relpath-sub.json' }
|
Loading…
Reference in New Issue
Block a user