diff mbox

[2/2] qapi: Create an include directive for use in the JSON description files.

Message ID 1395417673-17710-3-git-send-email-benoit.canet@irqsave.net
State New
Headers show

Commit Message

Benoît Canet March 21, 2014, 4:01 p.m. UTC
The new directive in the form { 'include': 'path/to/file.json' } will trigger the
parsing of path/to/file.json.
The directive will be replaced by the result of the parsing.

This will allow for easy modularisation of qapi JSON descriptions files.

The qapi commands now takes the input file path as first argument to make it
easier to detect include loops: stdin would not allow to do this.

Signed-off-by: Benoit Canet <benoit@irqsave.net>
---
 Makefile                                           | 24 +++++------
 scripts/qapi-commands.py                           |  8 +++-
 scripts/qapi-types.py                              |  8 +++-
 scripts/qapi-visit.py                              |  8 +++-
 scripts/qapi.py                                    | 46 +++++++++++++++++-----
 tests/Makefile                                     | 16 ++++----
 tests/qapi-schema/duplicate-key.err                |  2 +-
 .../qapi-schema/flat-union-invalid-branch-key.err  |  2 +-
 .../flat-union-invalid-discriminator.err           |  2 +-
 tests/qapi-schema/flat-union-no-base.err           |  2 +-
 .../flat-union-string-discriminator.err            |  2 +-
 tests/qapi-schema/funny-char.err                   |  2 +-
 tests/qapi-schema/include.err                      |  0
 tests/qapi-schema/include.exit                     |  1 +
 tests/qapi-schema/include.json                     |  4 ++
 tests/qapi-schema/include.out                      |  8 ++++
 tests/qapi-schema/include/include.json             |  7 ++++
 tests/qapi-schema/include_loop.err                 |  0
 tests/qapi-schema/include_loop.exit                |  1 +
 tests/qapi-schema/include_loop.json                |  1 +
 tests/qapi-schema/include_loop.out                 |  1 +
 tests/qapi-schema/missing-colon.err                |  2 +-
 tests/qapi-schema/missing-comma-list.err           |  2 +-
 tests/qapi-schema/missing-comma-object.err         |  2 +-
 tests/qapi-schema/non-objects.err                  |  2 +-
 tests/qapi-schema/quoted-structural-chars.err      |  2 +-
 tests/qapi-schema/test-qapi.py                     |  2 +-
 tests/qapi-schema/trailing-comma-list.err          |  2 +-
 tests/qapi-schema/trailing-comma-object.err        |  2 +-
 tests/qapi-schema/unclosed-list.err                |  2 +-
 tests/qapi-schema/unclosed-object.err              |  2 +-
 tests/qapi-schema/unclosed-string.err              |  2 +-
 tests/qapi-schema/union-invalid-base.err           |  2 +-
 33 files changed, 116 insertions(+), 53 deletions(-)
 create mode 100644 tests/qapi-schema/include.err
 create mode 100644 tests/qapi-schema/include.exit
 create mode 100644 tests/qapi-schema/include.json
 create mode 100644 tests/qapi-schema/include.out
 create mode 100644 tests/qapi-schema/include/include.json
 create mode 100644 tests/qapi-schema/include_loop.err
 create mode 100644 tests/qapi-schema/include_loop.exit
 create mode 100644 tests/qapi-schema/include_loop.json
 create mode 100644 tests/qapi-schema/include_loop.out

Comments

Wenchao Xia March 27, 2014, 1:49 a.m. UTC | #1
Just saw the discuss of Lluís's patch, either
{ 'include': './include/include.json' }
or
include("include-cycle-b.json")
is OK to me, but hope only one way is kepted in the end.
Eric Blake March 27, 2014, 2:58 a.m. UTC | #2
On 03/21/2014 10:01 AM, Benoît Canet wrote:
> The new directive in the form { 'include': 'path/to/file.json' } will trigger the
> parsing of path/to/file.json.
> The directive will be replaced by the result of the parsing.
> 
> This will allow for easy modularisation of qapi JSON descriptions files.
> 
> The qapi commands now takes the input file path as first argument to make it
> easier to detect include loops: stdin would not allow to do this.

It would be better to split this into two patches - one that alters the
framework to pass filenames and updates the testsuite to reflect the
improved error messages in existing tests, and the second to add the new
'include' directive with the new tests.

> 
> Signed-off-by: Benoit Canet <benoit@irqsave.net>
> ---
>  Makefile                                           | 24 +++++------
>  scripts/qapi-commands.py                           |  8 +++-
>  scripts/qapi-types.py                              |  8 +++-
>  scripts/qapi-visit.py                              |  8 +++-
>  scripts/qapi.py                                    | 46 +++++++++++++++++-----
>  tests/Makefile                                     | 16 ++++----

Missing a change to docs/qapi-code-gen.txt.  New features require
documentation.
Eric Blake March 27, 2014, 2:58 a.m. UTC | #3
On 03/26/2014 07:49 PM, Wenchao Xia wrote:
> Just saw the discuss of Lluís's patch, either
> { 'include': './include/include.json' }
> or
> include("include-cycle-b.json")
> is OK to me, but hope only one way is kepted in the end.

We already had this discussion; we want the { 'include':
'path/to/file.json' } form.
diff mbox

Patch

diff --git a/Makefile b/Makefile
index ec74039..9bec4ff 100644
--- a/Makefile
+++ b/Makefile
@@ -236,24 +236,24 @@  gen-out-type = $(subst .,-,$(suffix $@))
 qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
 
 qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
-$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, "  GEN   $@")
+$(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(SRC_PATH)/qga/qapi-schema.json $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, "  GEN   $@")
 qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h :\
-$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, "  GEN   $@")
+$(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(SRC_PATH)/qga/qapi-schema.json $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, "  GEN   $@")
 qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\
-$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, "  GEN   $@")
+$(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(SRC_PATH)/qga/qapi-schema.json $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, "  GEN   $@")
 
 qapi-types.c qapi-types.h :\
-$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "." -b < $<, "  GEN   $@")
+$(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(SRC_PATH)/qapi-schema.json $(gen-out-type) -o "." -b < $<, "  GEN   $@")
 qapi-visit.c qapi-visit.h :\
-$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "." -b < $<, "  GEN   $@")
+$(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(SRC_PATH)/qapi-schema.json $(gen-out-type) -o "." -b < $<, "  GEN   $@")
 qmp-commands.h qmp-marshal.c :\
-$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, "  GEN   $@")
+$(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(SRC_PATH)/qapi-schema.json $(gen-out-type) -m -o "." < $<, "  GEN   $@")
 
 QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
 $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 9734ab0..2995eab 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -369,13 +369,17 @@  def gen_command_def_prologue(prefix="", proxy=False):
 
 
 try:
-    opts, args = getopt.gnu_getopt(sys.argv[1:], "chp:o:m",
+    opts, args = getopt.gnu_getopt(sys.argv[2:], "chp:o:m",
                                    ["source", "header", "prefix=",
                                     "output-dir=", "type=", "middle"])
 except getopt.GetoptError, err:
     print str(err)
     sys.exit(1)
 
+if len(sys.argv) < 2:
+    print "The first argument must be the file name to parse"
+    sys.exit(1)
+
 output_dir = ""
 prefix = ""
 dispatch_type = "sync"
@@ -420,7 +424,7 @@  except os.error, e:
     if e.errno != errno.EEXIST:
         raise
 
-exprs = parse_schema(sys.stdin)
+exprs = parse_schema(sys.argv[1])
 commands = filter(lambda expr: expr.has_key('command'), exprs)
 commands = filter(lambda expr: not expr.has_key('gen'), commands)
 
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 10864ef..07df991 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -279,13 +279,17 @@  void qapi_free_%(type)s(%(c_type)s obj)
 
 
 try:
-    opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:o:",
+    opts, args = getopt.gnu_getopt(sys.argv[2:], "chbp:o:",
                                    ["source", "header", "builtins",
                                     "prefix=", "output-dir="])
 except getopt.GetoptError, err:
     print str(err)
     sys.exit(1)
 
+if len(sys.argv) < 2:
+    print "The first argument must be the file name to parse"
+    sys.exit(1)
+
 output_dir = ""
 prefix = ""
 c_file = 'qapi-types.c'
@@ -378,7 +382,7 @@  fdecl.write(mcgen('''
 ''',
                   guard=guardname(h_file)))
 
-exprs = parse_schema(sys.stdin)
+exprs = parse_schema(sys.argv[1])
 exprs = filter(lambda expr: not expr.has_key('gen'), exprs)
 
 fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 45ce3a9..cd53c97 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -397,13 +397,17 @@  void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **e
                 name=name)
 
 try:
-    opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:o:",
+    opts, args = getopt.gnu_getopt(sys.argv[2:], "chbp:o:",
                                    ["source", "header", "builtins", "prefix=",
                                     "output-dir="])
 except getopt.GetoptError, err:
     print str(err)
     sys.exit(1)
 
+if len(sys.argv) < 2:
+    print "The first argument must be the file name to parse"
+    sys.exit(1)
+
 output_dir = ""
 prefix = ""
 c_file = 'qapi-visit.c'
@@ -494,7 +498,7 @@  fdecl.write(mcgen('''
 ''',
                   prefix=prefix, guard=guardname(h_file)))
 
-exprs = parse_schema(sys.stdin)
+exprs = parse_schema(sys.argv[1])
 
 # to avoid header dependency hell, we always generate declarations
 # for built-in types in our header files and simply guard them
diff --git a/scripts/qapi.py b/scripts/qapi.py
index b474c39..0b0c8e4 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -12,6 +12,7 @@ 
 # See the COPYING file in the top-level directory.
 
 from ordereddict import OrderedDict
+import os
 import sys
 
 builtin_types = [
@@ -35,6 +36,9 @@  builtin_type_qtypes = {
     'uint64':   'QTYPE_QINT',
 }
 
+def get_filename(path):
+    return os.path.split(path)[1]
+
 class QAPISchemaError(Exception):
     def __init__(self, schema, msg):
         self.fp = schema.fp
@@ -48,7 +52,8 @@  class QAPISchemaError(Exception):
                 self.col += 1
 
     def __str__(self):
-        return "%s:%s:%s: %s" % (self.fp.name, self.line, self.col, self.msg)
+        return "%s:%s:%s: %s" % (get_filename(self.fp.name),
+                                 self.line, self.col, self.msg)
 
 class QAPIExprError(Exception):
     def __init__(self, expr_info, msg):
@@ -57,7 +62,8 @@  class QAPIExprError(Exception):
         self.msg = msg
 
     def __str__(self):
-        return "%s:%s: %s" % (self.fp.name, self.line, self.msg)
+        return "%s:%s: %s" % (get_filename(self.fp.name),
+                              self.line, self.msg)
 
 class QAPISchema:
 
@@ -263,18 +269,40 @@  def check_exprs(schema):
         if expr.has_key('union'):
             check_union(expr, expr_elem['info'])
 
-def parse_schema(fp):
-    try:
-        schema = QAPISchema(fp)
-    except QAPISchemaError, e:
-        print >>sys.stderr, e
-        exit(1)
+modules = []
+
+def build_schema(path):
+    with open(path, "r") as fp:
+        try:
+            schema = QAPISchema(fp)
+        except QAPISchemaError, e:
+            print >>sys.stderr, e
+            exit(1)
+    return schema
+
+def parse_schema(path):
+    path = os.path.abspath(path)
+
+    if path in modules:
+        print "Module inclusion loop detected with module: %s" %\
+              get_filename(path)
+        sys.exit(1)
+
+    modules.append(path)
+
+    schema = build_schema(path)
 
     exprs = []
 
     for expr_elem in schema.exprs:
         expr = expr_elem['expr']
-        if expr.has_key('enum'):
+        if expr.has_key('include'):
+            prefix = os.path.split(path)[0]
+            sub_path = os.path.join(prefix, expr['include'])
+            sub_exprs = parse_schema(sub_path)
+            exprs += sub_exprs
+            continue
+        elif expr.has_key('enum'):
             add_enum(expr['enum'], expr['data'])
         elif expr.has_key('union'):
             add_union(expr)
diff --git a/tests/Makefile b/tests/Makefile
index 2d021fb..b5e4cf0 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -164,7 +164,7 @@  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.json include_loop.json)
 
 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
 
@@ -215,14 +215,14 @@  tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
 	libqemuutil.a
 
 tests/test-qapi-types.c tests/test-qapi-types.h :\
-$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o tests -p "test-" < $<, "  GEN   $@")
+$(SRC_PATH)/scripts/qapi-types.py
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(gen-out-type) -o tests -p "test-" < $<, "  GEN   $@")
 tests/test-qapi-visit.c tests/test-qapi-visit.h :\
-$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o tests -p "test-" < $<, "  GEN   $@")
+$(SRC_PATH)/scripts/qapi-visit.py
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(gen-out-type) -o tests -p "test-" < $<, "  GEN   $@")
 tests/test-qmp-commands.h tests/test-qmp-marshal.c :\
-$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o tests -p "test-" < $<, "  GEN   $@")
+$(SRC_PATH)/scripts/qapi-commands.py
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(gen-out-type) -o tests -p "test-" < $<, "  GEN   $@")
 
 tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
 tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
@@ -362,7 +362,7 @@  check-tests/test-qapi.py: tests/test-qapi.py
 
 .PHONY: $(patsubst %, check-%, $(check-qapi-schema-y))
 $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json
-	$(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py <$^ >$*.test.out 2>$*.test.err; echo $$? >$*.test.exit, "  TEST  $*.out")
+	$(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py $^ >$*.test.out 2>$*.test.err; echo $$? >$*.test.exit, "  TEST  $*.out")
 	@diff -q $(SRC_PATH)/$*.out $*.test.out
 	@diff -q $(SRC_PATH)/$*.err $*.test.err
 	@diff -q $(SRC_PATH)/$*.exit $*.test.exit
diff --git a/tests/qapi-schema/duplicate-key.err b/tests/qapi-schema/duplicate-key.err
index 0801c6a..21303ef 100644
--- a/tests/qapi-schema/duplicate-key.err
+++ b/tests/qapi-schema/duplicate-key.err
@@ -1 +1 @@ 
-<stdin>:2:10: Duplicate key "key"
+duplicate-key.json:2:10: Duplicate key "key"
diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.err b/tests/qapi-schema/flat-union-invalid-branch-key.err
index 1125caf..34b4584 100644
--- a/tests/qapi-schema/flat-union-invalid-branch-key.err
+++ b/tests/qapi-schema/flat-union-invalid-branch-key.err
@@ -1 +1 @@ 
-<stdin>:13: Discriminator value 'value_wrong' is not found in enum 'TestEnum'
+flat-union-invalid-branch-key.json:13: Discriminator value 'value_wrong' is not found in enum 'TestEnum'
diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.err b/tests/qapi-schema/flat-union-invalid-discriminator.err
index cad9dbf..b6ce3b7 100644
--- a/tests/qapi-schema/flat-union-invalid-discriminator.err
+++ b/tests/qapi-schema/flat-union-invalid-discriminator.err
@@ -1 +1 @@ 
-<stdin>:13: Discriminator 'enum_wrong' is not a member of base type 'TestBase'
+flat-union-invalid-discriminator.json:13: Discriminator 'enum_wrong' is not a member of base type 'TestBase'
diff --git a/tests/qapi-schema/flat-union-no-base.err b/tests/qapi-schema/flat-union-no-base.err
index e2d7443..7929455 100644
--- a/tests/qapi-schema/flat-union-no-base.err
+++ b/tests/qapi-schema/flat-union-no-base.err
@@ -1 +1 @@ 
-<stdin>:7: Flat union 'TestUnion' must have a base field
+flat-union-no-base.json:7: Flat union 'TestUnion' must have a base field
diff --git a/tests/qapi-schema/flat-union-string-discriminator.err b/tests/qapi-schema/flat-union-string-discriminator.err
index 8748270..fc1e2d5 100644
--- a/tests/qapi-schema/flat-union-string-discriminator.err
+++ b/tests/qapi-schema/flat-union-string-discriminator.err
@@ -1 +1 @@ 
-<stdin>:13: Discriminator 'kind' must be of enumeration type
+flat-union-string-discriminator.json:13: Discriminator 'kind' must be of enumeration type
diff --git a/tests/qapi-schema/funny-char.err b/tests/qapi-schema/funny-char.err
index d3dd293..ee65869 100644
--- a/tests/qapi-schema/funny-char.err
+++ b/tests/qapi-schema/funny-char.err
@@ -1 +1 @@ 
-<stdin>:2:36: Stray ";"
+funny-char.json:2:36: Stray ";"
diff --git a/tests/qapi-schema/include.err b/tests/qapi-schema/include.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include.exit b/tests/qapi-schema/include.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/include.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/include.json b/tests/qapi-schema/include.json
new file mode 100644
index 0000000..ffece21
--- /dev/null
+++ b/tests/qapi-schema/include.json
@@ -0,0 +1,4 @@ 
+{ 'enum': 'Status',
+  'data': [ 'good', 'bad', 'ugly' ] }
+{ 'include': './include/include.json' }
+{ 'foo': '42' }
diff --git a/tests/qapi-schema/include.out b/tests/qapi-schema/include.out
new file mode 100644
index 0000000..89e43e8
--- /dev/null
+++ b/tests/qapi-schema/include.out
@@ -0,0 +1,8 @@ 
+[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])]),
+ OrderedDict([('bar', '33')]),
+ OrderedDict([('enum', 'Fruits'), ('data', ['orange', 'apple', 'gooseberry'])]),
+ OrderedDict([('baz', '54')]),
+ OrderedDict([('foo', '42')])]
+[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']},
+ {'enum_name': 'Fruits', 'enum_values': ['orange', 'apple', 'gooseberry']}]
+[]
diff --git a/tests/qapi-schema/include/include.json b/tests/qapi-schema/include/include.json
new file mode 100644
index 0000000..f445eee
--- /dev/null
+++ b/tests/qapi-schema/include/include.json
@@ -0,0 +1,7 @@ 
+
+{ 'bar': '33' }
+
+{ 'enum': 'Fruits',
+  'data': [ 'orange', 'apple', 'gooseberry' ] }
+
+{ 'baz': '54' }
diff --git a/tests/qapi-schema/include_loop.err b/tests/qapi-schema/include_loop.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/include_loop.exit b/tests/qapi-schema/include_loop.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/include_loop.exit
@@ -0,0 +1 @@ 
+1
diff --git a/tests/qapi-schema/include_loop.json b/tests/qapi-schema/include_loop.json
new file mode 100644
index 0000000..cb8ff03
--- /dev/null
+++ b/tests/qapi-schema/include_loop.json
@@ -0,0 +1 @@ 
+{ 'include': 'include_loop.json' }
diff --git a/tests/qapi-schema/include_loop.out b/tests/qapi-schema/include_loop.out
new file mode 100644
index 0000000..35da4dd
--- /dev/null
+++ b/tests/qapi-schema/include_loop.out
@@ -0,0 +1 @@ 
+Module inclusion loop detected with module: include_loop.json
diff --git a/tests/qapi-schema/missing-colon.err b/tests/qapi-schema/missing-colon.err
index 9f2a355..676cce5 100644
--- a/tests/qapi-schema/missing-colon.err
+++ b/tests/qapi-schema/missing-colon.err
@@ -1 +1 @@ 
-<stdin>:1:10: Expected ":"
+missing-colon.json:1:10: Expected ":"
diff --git a/tests/qapi-schema/missing-comma-list.err b/tests/qapi-schema/missing-comma-list.err
index 4fe0700..d0ed8c3 100644
--- a/tests/qapi-schema/missing-comma-list.err
+++ b/tests/qapi-schema/missing-comma-list.err
@@ -1 +1 @@ 
-<stdin>:2:20: Expected "," or "]"
+missing-comma-list.json:2:20: Expected "," or "]"
diff --git a/tests/qapi-schema/missing-comma-object.err b/tests/qapi-schema/missing-comma-object.err
index b0121b5..ad9b457 100644
--- a/tests/qapi-schema/missing-comma-object.err
+++ b/tests/qapi-schema/missing-comma-object.err
@@ -1 +1 @@ 
-<stdin>:2:3: Expected "," or "}"
+missing-comma-object.json:2:3: Expected "," or "}"
diff --git a/tests/qapi-schema/non-objects.err b/tests/qapi-schema/non-objects.err
index a6c2dc2..e958cf0 100644
--- a/tests/qapi-schema/non-objects.err
+++ b/tests/qapi-schema/non-objects.err
@@ -1 +1 @@ 
-<stdin>:1:1: Expected "{"
+non-objects.json:1:1: Expected "{"
diff --git a/tests/qapi-schema/quoted-structural-chars.err b/tests/qapi-schema/quoted-structural-chars.err
index a6c2dc2..77732d0 100644
--- a/tests/qapi-schema/quoted-structural-chars.err
+++ b/tests/qapi-schema/quoted-structural-chars.err
@@ -1 +1 @@ 
-<stdin>:1:1: Expected "{"
+quoted-structural-chars.json:1:1: Expected "{"
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index ac6da13..4f217d2 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -15,7 +15,7 @@  from pprint import pprint
 import sys
 
 try:
-    exprs = parse_schema(sys.stdin)
+    exprs = parse_schema(sys.argv[1])
 except SystemExit:
     raise
 except Exception, e:
diff --git a/tests/qapi-schema/trailing-comma-list.err b/tests/qapi-schema/trailing-comma-list.err
index ff839a3..13b79f9 100644
--- a/tests/qapi-schema/trailing-comma-list.err
+++ b/tests/qapi-schema/trailing-comma-list.err
@@ -1 +1 @@ 
-<stdin>:2:36: Expected "{", "[" or string
+trailing-comma-list.json:2:36: Expected "{", "[" or string
diff --git a/tests/qapi-schema/trailing-comma-object.err b/tests/qapi-schema/trailing-comma-object.err
index f540962..d1d57f0 100644
--- a/tests/qapi-schema/trailing-comma-object.err
+++ b/tests/qapi-schema/trailing-comma-object.err
@@ -1 +1 @@ 
-<stdin>:2:38: Expected string
+trailing-comma-object.json:2:38: Expected string
diff --git a/tests/qapi-schema/unclosed-list.err b/tests/qapi-schema/unclosed-list.err
index 0e837a7..1a699a2 100644
--- a/tests/qapi-schema/unclosed-list.err
+++ b/tests/qapi-schema/unclosed-list.err
@@ -1 +1 @@ 
-<stdin>:1:20: Expected "," or "]"
+unclosed-list.json:1:20: Expected "," or "]"
diff --git a/tests/qapi-schema/unclosed-object.err b/tests/qapi-schema/unclosed-object.err
index e6dc950..3ddb126 100644
--- a/tests/qapi-schema/unclosed-object.err
+++ b/tests/qapi-schema/unclosed-object.err
@@ -1 +1 @@ 
-<stdin>:1:21: Expected "," or "}"
+unclosed-object.json:1:21: Expected "," or "}"
diff --git a/tests/qapi-schema/unclosed-string.err b/tests/qapi-schema/unclosed-string.err
index 948d883..cdd3dca 100644
--- a/tests/qapi-schema/unclosed-string.err
+++ b/tests/qapi-schema/unclosed-string.err
@@ -1 +1 @@ 
-<stdin>:1:11: Missing terminating "'"
+unclosed-string.json:1:11: Missing terminating "'"
diff --git a/tests/qapi-schema/union-invalid-base.err b/tests/qapi-schema/union-invalid-base.err
index dd8e3d1..5e38b12 100644
--- a/tests/qapi-schema/union-invalid-base.err
+++ b/tests/qapi-schema/union-invalid-base.err
@@ -1 +1 @@ 
-<stdin>:7: Base 'TestBaseWrong' is not a valid type
+union-invalid-base.json:7: Base 'TestBaseWrong' is not a valid type