[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v9 4/4] qapi: Add a primitive to include other files
From: |
Lluís Vilanova |
Subject: |
[Qemu-devel] [PATCH v9 4/4] qapi: Add a primitive to include other files from a QAPI schema file |
Date: |
Sun, 13 Apr 2014 21:08:15 +0200 |
User-agent: |
StGit/0.17.1-dirty |
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 <address@hidden>
---
docs/qapi-code-gen.txt | 11 +++++
scripts/qapi.py | 66 +++++++++++++++++++++++-----
tests/Makefile | 5 ++
tests/qapi-schema/include-after-err.err | 1
tests/qapi-schema/include-after-err.exit | 1
tests/qapi-schema/include-after-err.json | 2 +
tests/qapi-schema/include-after-err.out | 0
tests/qapi-schema/include-cycle-b.json | 1
tests/qapi-schema/include-cycle-c.json | 1
tests/qapi-schema/include-cycle.err | 3 +
tests/qapi-schema/include-cycle.exit | 1
tests/qapi-schema/include-cycle.json | 1
tests/qapi-schema/include-cycle.out | 0
tests/qapi-schema/include-format-err.err | 1
tests/qapi-schema/include-format-err.exit | 1
tests/qapi-schema/include-format-err.json | 2 +
tests/qapi-schema/include-format-err.out | 0
tests/qapi-schema/include-nested-err.err | 2 +
tests/qapi-schema/include-nested-err.exit | 1
tests/qapi-schema/include-nested-err.json | 1
tests/qapi-schema/include-nested-err.out | 0
tests/qapi-schema/include-no-file.err | 1
tests/qapi-schema/include-no-file.exit | 1
tests/qapi-schema/include-no-file.json | 1
tests/qapi-schema/include-no-file.out | 0
tests/qapi-schema/include-non-file.err | 1
tests/qapi-schema/include-non-file.exit | 1
tests/qapi-schema/include-non-file.json | 1
tests/qapi-schema/include-non-file.out | 0
tests/qapi-schema/include-relpath-sub.json | 2 +
tests/qapi-schema/include-relpath.err | 0
tests/qapi-schema/include-relpath.exit | 1
tests/qapi-schema/include-relpath.json | 1
tests/qapi-schema/include-relpath.out | 3 +
tests/qapi-schema/include-self-cycle.err | 1
tests/qapi-schema/include-self-cycle.exit | 1
tests/qapi-schema/include-self-cycle.json | 1
tests/qapi-schema/include-self-cycle.out | 0
tests/qapi-schema/include-simple-sub.json | 2 +
tests/qapi-schema/include-simple.err | 0
tests/qapi-schema/include-simple.exit | 1
tests/qapi-schema/include-simple.json | 1
tests/qapi-schema/include-simple.out | 3 +
tests/qapi-schema/include/relpath.json | 1
44 files changed, 113 insertions(+), 12 deletions(-)
create mode 100644 tests/qapi-schema/include-after-err.err
create mode 100644 tests/qapi-schema/include-after-err.exit
create mode 100644 tests/qapi-schema/include-after-err.json
create mode 100644 tests/qapi-schema/include-after-err.out
create mode 100644 tests/qapi-schema/include-cycle-b.json
create mode 100644 tests/qapi-schema/include-cycle-c.json
create mode 100644 tests/qapi-schema/include-cycle.err
create mode 100644 tests/qapi-schema/include-cycle.exit
create mode 100644 tests/qapi-schema/include-cycle.json
create mode 100644 tests/qapi-schema/include-cycle.out
create mode 100644 tests/qapi-schema/include-format-err.err
create mode 100644 tests/qapi-schema/include-format-err.exit
create mode 100644 tests/qapi-schema/include-format-err.json
create mode 100644 tests/qapi-schema/include-format-err.out
create mode 100644 tests/qapi-schema/include-nested-err.err
create mode 100644 tests/qapi-schema/include-nested-err.exit
create mode 100644 tests/qapi-schema/include-nested-err.json
create mode 100644 tests/qapi-schema/include-nested-err.out
create mode 100644 tests/qapi-schema/include-no-file.err
create mode 100644 tests/qapi-schema/include-no-file.exit
create mode 100644 tests/qapi-schema/include-no-file.json
create mode 100644 tests/qapi-schema/include-no-file.out
create mode 100644 tests/qapi-schema/include-non-file.err
create mode 100644 tests/qapi-schema/include-non-file.exit
create mode 100644 tests/qapi-schema/include-non-file.json
create mode 100644 tests/qapi-schema/include-non-file.out
create mode 100644 tests/qapi-schema/include-relpath-sub.json
create mode 100644 tests/qapi-schema/include-relpath.err
create mode 100644 tests/qapi-schema/include-relpath.exit
create mode 100644 tests/qapi-schema/include-relpath.json
create mode 100644 tests/qapi-schema/include-relpath.out
create mode 100644 tests/qapi-schema/include-self-cycle.err
create mode 100644 tests/qapi-schema/include-self-cycle.exit
create mode 100644 tests/qapi-schema/include-self-cycle.json
create mode 100644 tests/qapi-schema/include-self-cycle.out
create mode 100644 tests/qapi-schema/include-simple-sub.json
create mode 100644 tests/qapi-schema/include-simple.err
create mode 100644 tests/qapi-schema/include-simple.exit
create mode 100644 tests/qapi-schema/include-simple.json
create mode 100644 tests/qapi-schema/include-simple.out
create mode 100644 tests/qapi-schema/include/relpath.json
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 63b03cf..051d109 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -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
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 3a38e27..a013742 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -11,6 +11,8 @@
# 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
from ordereddict import OrderedDict
import os
import sys
@@ -36,9 +38,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 +57,32 @@ 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):
+ def __init__(self, fp, input_rel=None, include_hist=[], parent_info=None):
self.fp = fp
+ input_path = os.path.abspath(fp.name)
+ if input_rel is None:
+ input_rel = fp.name
+ self.input_dir = os.path.dirname(input_path)
+ self.input_file = input_rel
+ self.include_hist = include_hist + [(input_rel, input_path)]
+ self.parent_info = parent_info
self.src = fp.read()
if self.src == '' or self.src[-1] != '\n':
self.src += '\n'
@@ -74,10 +93,35 @@ 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_rel, '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 = include
+ if not os.path.isabs(include_path):
+ include_path = os.path.join(self.input_dir, include_path)
+ if not os.path.isfile(include_path):
+ raise QAPIExprError(
+ expr_info,
+ 'Non-existing include file "%s"' %
+ include)
+ if any(include_path == elem[1]
+ for elem in self.include_hist):
+ raise QAPIExprError(expr_info, "Inclusion loop for %s"
+ % include)
+ exprs_include = QAPISchema(open(include_path, "r"), 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 +311,7 @@ def check_exprs(schema):
def parse_schema(input_path):
try:
schema = QAPISchema(open(input_path, "r"))
- except QAPISchemaError, e:
+ except (QAPISchemaError, QAPIExprError), e:
print >>sys.stderr, e
exit(1)
diff --git a/tests/Makefile b/tests/Makefile
index 6803e7b..4ee7dc5 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -164,7 +164,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-after-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
diff --git a/tests/qapi-schema/include-after-err.err
b/tests/qapi-schema/include-after-err.err
new file mode 100644
index 0000000..eaf632a
--- /dev/null
+++ b/tests/qapi-schema/include-after-err.err
@@ -0,0 +1 @@
+tests/qapi-schema/include-after-err.json:2:13: Expected ":"
diff --git a/tests/qapi-schema/include-after-err.exit
b/tests/qapi-schema/include-after-err.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/include-after-err.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/include-after-err.json
b/tests/qapi-schema/include-after-err.json
new file mode 100644
index 0000000..afb6cb6
--- /dev/null
+++ b/tests/qapi-schema/include-after-err.json
@@ -0,0 +1,2 @@
+{ 'include': 'include-simple-sub.json' }
+{ 'command' 'missing-colon' }
diff --git a/tests/qapi-schema/include-after-err.out
b/tests/qapi-schema/include-after-err.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include-cycle-b.json
b/tests/qapi-schema/include-cycle-b.json
new file mode 100644
index 0000000..4fa985d
--- /dev/null
+++ b/tests/qapi-schema/include-cycle-b.json
@@ -0,0 +1 @@
+{ 'include': 'include-cycle-c.json' }
diff --git a/tests/qapi-schema/include-cycle-c.json
b/tests/qapi-schema/include-cycle-c.json
new file mode 100644
index 0000000..d12b592
--- /dev/null
+++ b/tests/qapi-schema/include-cycle-c.json
@@ -0,0 +1 @@
+{ 'include': 'include-cycle.json' }
diff --git a/tests/qapi-schema/include-cycle.err
b/tests/qapi-schema/include-cycle.err
new file mode 100644
index 0000000..602cf62
--- /dev/null
+++ b/tests/qapi-schema/include-cycle.err
@@ -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
diff --git a/tests/qapi-schema/include-cycle.exit
b/tests/qapi-schema/include-cycle.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/include-cycle.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/include-cycle.json
b/tests/qapi-schema/include-cycle.json
new file mode 100644
index 0000000..6fcf1eb
--- /dev/null
+++ b/tests/qapi-schema/include-cycle.json
@@ -0,0 +1 @@
+{ 'include': 'include-cycle-b.json' }
diff --git a/tests/qapi-schema/include-cycle.out
b/tests/qapi-schema/include-cycle.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include-format-err.err
b/tests/qapi-schema/include-format-err.err
new file mode 100644
index 0000000..721ff4e
--- /dev/null
+++ b/tests/qapi-schema/include-format-err.err
@@ -0,0 +1 @@
+tests/qapi-schema/include-format-err.json:1: Invalid 'include' directive
diff --git a/tests/qapi-schema/include-format-err.exit
b/tests/qapi-schema/include-format-err.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/include-format-err.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/include-format-err.json
b/tests/qapi-schema/include-format-err.json
new file mode 100644
index 0000000..44980f0
--- /dev/null
+++ b/tests/qapi-schema/include-format-err.json
@@ -0,0 +1,2 @@
+{ 'include': 'include-simple-sub.json',
+ 'foo': 'bar' }
diff --git a/tests/qapi-schema/include-format-err.out
b/tests/qapi-schema/include-format-err.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include-nested-err.err
b/tests/qapi-schema/include-nested-err.err
new file mode 100644
index 0000000..1dacbda
--- /dev/null
+++ b/tests/qapi-schema/include-nested-err.err
@@ -0,0 +1,2 @@
+In file included from tests/qapi-schema/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
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/include-nested-err.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/include-nested-err.json
b/tests/qapi-schema/include-nested-err.json
new file mode 100644
index 0000000..5631e56
--- /dev/null
+++ b/tests/qapi-schema/include-nested-err.json
@@ -0,0 +1 @@
+{ 'include': 'missing-colon.json' }
diff --git a/tests/qapi-schema/include-nested-err.out
b/tests/qapi-schema/include-nested-err.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include-no-file.err
b/tests/qapi-schema/include-no-file.err
new file mode 100644
index 0000000..526ab9d
--- /dev/null
+++ b/tests/qapi-schema/include-no-file.err
@@ -0,0 +1 @@
+tests/qapi-schema/include-no-file.json:1: Non-existing include file
"include-no-file-sub.json"
diff --git a/tests/qapi-schema/include-no-file.exit
b/tests/qapi-schema/include-no-file.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/include-no-file.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/include-no-file.json
b/tests/qapi-schema/include-no-file.json
new file mode 100644
index 0000000..9249ebd
--- /dev/null
+++ b/tests/qapi-schema/include-no-file.json
@@ -0,0 +1 @@
+{ 'include': 'include-no-file-sub.json' }
diff --git a/tests/qapi-schema/include-no-file.out
b/tests/qapi-schema/include-no-file.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include-non-file.err
b/tests/qapi-schema/include-non-file.err
new file mode 100644
index 0000000..9658c78
--- /dev/null
+++ b/tests/qapi-schema/include-non-file.err
@@ -0,0 +1 @@
+tests/qapi-schema/include-non-file.json:1: Expected a file name (string), got:
['foo', 'bar']
diff --git a/tests/qapi-schema/include-non-file.exit
b/tests/qapi-schema/include-non-file.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/include-non-file.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/include-non-file.json
b/tests/qapi-schema/include-non-file.json
new file mode 100644
index 0000000..cd43c3f
--- /dev/null
+++ b/tests/qapi-schema/include-non-file.json
@@ -0,0 +1 @@
+{ 'include': [ 'foo', 'bar' ] }
diff --git a/tests/qapi-schema/include-non-file.out
b/tests/qapi-schema/include-non-file.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include-relpath-sub.json
b/tests/qapi-schema/include-relpath-sub.json
new file mode 100644
index 0000000..4bd4af4
--- /dev/null
+++ b/tests/qapi-schema/include-relpath-sub.json
@@ -0,0 +1,2 @@
+{ 'enum': 'Status',
+ 'data': [ 'good', 'bad', 'ugly' ] }
diff --git a/tests/qapi-schema/include-relpath.err
b/tests/qapi-schema/include-relpath.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include-relpath.exit
b/tests/qapi-schema/include-relpath.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/include-relpath.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/include-relpath.json
b/tests/qapi-schema/include-relpath.json
new file mode 100644
index 0000000..05018f3
--- /dev/null
+++ b/tests/qapi-schema/include-relpath.json
@@ -0,0 +1 @@
+{ 'include': 'include/relpath.json' }
diff --git a/tests/qapi-schema/include-relpath.out
b/tests/qapi-schema/include-relpath.out
new file mode 100644
index 0000000..4ce3dcf
--- /dev/null
+++ b/tests/qapi-schema/include-relpath.out
@@ -0,0 +1,3 @@
+[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
+[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
+[]
diff --git a/tests/qapi-schema/include-self-cycle.err
b/tests/qapi-schema/include-self-cycle.err
new file mode 100644
index 0000000..981742a
--- /dev/null
+++ b/tests/qapi-schema/include-self-cycle.err
@@ -0,0 +1 @@
+tests/qapi-schema/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
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/include-self-cycle.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/include-self-cycle.json
b/tests/qapi-schema/include-self-cycle.json
new file mode 100644
index 0000000..55fb1b5
--- /dev/null
+++ b/tests/qapi-schema/include-self-cycle.json
@@ -0,0 +1 @@
+{ 'include': 'include-self-cycle.json' }
diff --git a/tests/qapi-schema/include-self-cycle.out
b/tests/qapi-schema/include-self-cycle.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include-simple-sub.json
b/tests/qapi-schema/include-simple-sub.json
new file mode 100644
index 0000000..4bd4af4
--- /dev/null
+++ b/tests/qapi-schema/include-simple-sub.json
@@ -0,0 +1,2 @@
+{ 'enum': 'Status',
+ 'data': [ 'good', 'bad', 'ugly' ] }
diff --git a/tests/qapi-schema/include-simple.err
b/tests/qapi-schema/include-simple.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include-simple.exit
b/tests/qapi-schema/include-simple.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/include-simple.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/include-simple.json
b/tests/qapi-schema/include-simple.json
new file mode 100644
index 0000000..1dd391a
--- /dev/null
+++ b/tests/qapi-schema/include-simple.json
@@ -0,0 +1 @@
+{ 'include': 'include-simple-sub.json' }
diff --git a/tests/qapi-schema/include-simple.out
b/tests/qapi-schema/include-simple.out
new file mode 100644
index 0000000..4ce3dcf
--- /dev/null
+++ b/tests/qapi-schema/include-simple.out
@@ -0,0 +1,3 @@
+[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
+[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
+[]
diff --git a/tests/qapi-schema/include/relpath.json
b/tests/qapi-schema/include/relpath.json
new file mode 100644
index 0000000..45dee24
--- /dev/null
+++ b/tests/qapi-schema/include/relpath.json
@@ -0,0 +1 @@
+{ 'include': '../include-relpath-sub.json' }
- Re: [Qemu-devel] [PATCH v9 1/4] qapi: [trivial] Break long command lines, (continued)