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. Signed-off-by: Benoit Canet <ben...@irqsave.net> --- docs/qapi-code-gen.txt | 14 ++++++++ scripts/qapi.py | 38 +++++++++++++++++++- tests/Makefile | 8 +++-- .../qapi-schema/error_after_sucessful_include.err | 1 + .../qapi-schema/error_after_sucessful_include.exit | 1 + .../qapi-schema/error_after_sucessful_include.json | 4 +++ tests/qapi-schema/error_in_included_file.err | 1 + tests/qapi-schema/error_in_included_file.exit | 1 + tests/qapi-schema/error_in_included_file.json | 4 +++ tests/qapi-schema/include.exit | 1 + tests/qapi-schema/include.json | 4 +++ tests/qapi-schema/include.out | 8 +++++ tests/qapi-schema/include/error.json | 6 ++++ tests/qapi-schema/include/include.json | 7 ++++ tests/qapi-schema/include/multi.json | 1 + tests/qapi-schema/include/multi_loop.json | 1 + tests/qapi-schema/include_loop.exit | 1 + tests/qapi-schema/include_loop.json | 1 + tests/qapi-schema/include_loop.out | 3 ++ tests/qapi-schema/include_non_filename.err | 1 + tests/qapi-schema/include_non_filename.exit | 1 + tests/qapi-schema/include_non_filename.json | 4 +++ tests/qapi-schema/multi_file_loop_include.exit | 1 + tests/qapi-schema/multi_file_loop_include.json | 4 +++ tests/qapi-schema/multi_file_loop_include.out | 4 +++ .../multiple_relative_path_include.exit | 1 + .../multiple_relative_path_include.json | 4 +++ .../qapi-schema/multiple_relative_path_include.out | 6 ++++ 28 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 tests/qapi-schema/error_after_sucessful_include.err create mode 100644 tests/qapi-schema/error_after_sucessful_include.exit create mode 100644 tests/qapi-schema/error_after_sucessful_include.json create mode 100644 tests/qapi-schema/error_after_sucessful_include.out create mode 100644 tests/qapi-schema/error_in_included_file.err create mode 100644 tests/qapi-schema/error_in_included_file.exit create mode 100644 tests/qapi-schema/error_in_included_file.json create mode 100644 tests/qapi-schema/error_in_included_file.out 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/error.json create mode 100644 tests/qapi-schema/include/include.json create mode 100644 tests/qapi-schema/include/multi.json create mode 100644 tests/qapi-schema/include/multi_loop.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 create mode 100644 tests/qapi-schema/include_non_filename.err create mode 100644 tests/qapi-schema/include_non_filename.exit create mode 100644 tests/qapi-schema/include_non_filename.json create mode 100644 tests/qapi-schema/include_non_filename.out create mode 100644 tests/qapi-schema/multi_file_loop_include.err create mode 100644 tests/qapi-schema/multi_file_loop_include.exit create mode 100644 tests/qapi-schema/multi_file_loop_include.json create mode 100644 tests/qapi-schema/multi_file_loop_include.out create mode 100644 tests/qapi-schema/multiple_relative_path_include.err create mode 100644 tests/qapi-schema/multiple_relative_path_include.exit create mode 100644 tests/qapi-schema/multiple_relative_path_include.json create mode 100644 tests/qapi-schema/multiple_relative_path_include.out diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index d78921f..a16aa47 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -180,6 +180,20 @@ An example command is: 'data': { 'arg1': 'str', '*arg2': 'str' }, 'returns': 'str' } +=== Includes === + +A schema file can include other sub schema files with the include +directive. + +An example of include directive is: + +{ 'include': 'path/to/sub_schema.json' } + +The include path is relative to the current schema file. +The include parsing method is recursive. +The expressions resulting from the parsing of the sub schema are injected +in place of the include directive like a C #include would do. + == Code generation == diff --git a/scripts/qapi.py b/scripts/qapi.py index ae3f756..df624ce 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -273,14 +273,47 @@ def build_schema(filename): exit(1) return schema +loop_stack = [] + +def include_sub_file(filename, expr_elem): + expr = expr_elem['expr'] + expr_info = expr_elem['info'] + + # is the include a string ? + include = expr['include'] + if not isinstance(include, str): + raise QAPIExprError(expr_info, + "filename to include must be a string") + + # get sub filename + prefix = os.path.split(filename)[0] + sub_filename = os.path.join(prefix, include) + + return parse_schema(sub_filename) + def parse_schema(filename): + abspath = os.path.abspath(filename) + + if abspath in loop_stack: + print "Inclusion loop detected with file: %s"% filename + print "Path to the broken include is:" + for i in loop_stack: + print "\t%s" % i + sys.exit(1) + + # push the abspath of the current file in the stack + loop_stack.append(abspath) + schema = build_schema(filename) exprs = [] for expr_elem in schema.exprs: expr = expr_elem['expr'] - if expr.has_key('enum'): + if expr.has_key('include'): + exprs += include_sub_file(filename, expr_elem) + continue + elif expr.has_key('enum'): add_enum(expr['enum'], expr['data']) elif expr.has_key('union'): add_union(expr) @@ -301,6 +334,9 @@ def parse_schema(filename): print >>sys.stderr, e exit(1) + # pop the abspath of the current file from the stack + loop_stack.pop() + return exprs def parse_args(typeinfo): diff --git a/tests/Makefile b/tests/Makefile index 0a3c439..7ad20d1 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.json include_loop.json \ + include_non_filename.json error_in_included_file.json \ + error_after_sucessful_include.json multi_file_loop_include.json \ + multiple_relative_path_include.json) GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h @@ -363,8 +366,9 @@ 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") -# filter in a second step to avoid redirection madness +# filter abspaths @sed -i s=.*/== $*.test.err + @sed -i s="/.*/"== $*.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/error_after_sucessful_include.err b/tests/qapi-schema/error_after_sucessful_include.err new file mode 100644 index 0000000..d562a83 --- /dev/null +++ b/tests/qapi-schema/error_after_sucessful_include.err @@ -0,0 +1 @@ +error_after_sucessful_include.json:4:10: Stray "T" diff --git a/tests/qapi-schema/error_after_sucessful_include.exit b/tests/qapi-schema/error_after_sucessful_include.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/error_after_sucessful_include.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/error_after_sucessful_include.json b/tests/qapi-schema/error_after_sucessful_include.json new file mode 100644 index 0000000..c6cf313 --- /dev/null +++ b/tests/qapi-schema/error_after_sucessful_include.json @@ -0,0 +1,4 @@ +{ 'enum': 'Status', + 'data': [ 'good', 'bad', 'ugly' ] } +{ 'include': './include/include.json' } +{ 'foo': True } diff --git a/tests/qapi-schema/error_after_sucessful_include.out b/tests/qapi-schema/error_after_sucessful_include.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/error_in_included_file.err b/tests/qapi-schema/error_in_included_file.err new file mode 100644 index 0000000..c011fee --- /dev/null +++ b/tests/qapi-schema/error_in_included_file.err @@ -0,0 +1 @@ +error.json:4:12: Stray "T" diff --git a/tests/qapi-schema/error_in_included_file.exit b/tests/qapi-schema/error_in_included_file.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/error_in_included_file.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/error_in_included_file.json b/tests/qapi-schema/error_in_included_file.json new file mode 100644 index 0000000..ae8c732 --- /dev/null +++ b/tests/qapi-schema/error_in_included_file.json @@ -0,0 +1,4 @@ +{ 'enum': 'Status', + 'data': [ 'good', 'bad', 'ugly' ] } +{ 'include': 'include/error.json' } +{ 'foo': '42' } diff --git a/tests/qapi-schema/error_in_included_file.out b/tests/qapi-schema/error_in_included_file.out new file mode 100644 index 0000000..e69de29 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/error.json b/tests/qapi-schema/include/error.json new file mode 100644 index 0000000..33ac12c --- /dev/null +++ b/tests/qapi-schema/include/error.json @@ -0,0 +1,6 @@ + +{ 'foo': '43' } + +{ 'zerg' : True } + +{ 'bar' : '65' } 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/multi.json b/tests/qapi-schema/include/multi.json new file mode 100644 index 0000000..692480c --- /dev/null +++ b/tests/qapi-schema/include/multi.json @@ -0,0 +1 @@ +{ 'include': '../comments.json' } diff --git a/tests/qapi-schema/include/multi_loop.json b/tests/qapi-schema/include/multi_loop.json new file mode 100644 index 0000000..7c187b8 --- /dev/null +++ b/tests/qapi-schema/include/multi_loop.json @@ -0,0 +1 @@ +{ 'include': '../multi_file_loop_include.json' } 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..eaecfd3 --- /dev/null +++ b/tests/qapi-schema/include_loop.out @@ -0,0 +1,3 @@ +Inclusion loop detected with file: include_loop.json +Path to the broken include is: + include_loop.json diff --git a/tests/qapi-schema/include_non_filename.err b/tests/qapi-schema/include_non_filename.err new file mode 100644 index 0000000..9b02b3a --- /dev/null +++ b/tests/qapi-schema/include_non_filename.err @@ -0,0 +1 @@ +include_non_filename.json:3: filename to include must be a string diff --git a/tests/qapi-schema/include_non_filename.exit b/tests/qapi-schema/include_non_filename.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/include_non_filename.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/include_non_filename.json b/tests/qapi-schema/include_non_filename.json new file mode 100644 index 0000000..6042b12 --- /dev/null +++ b/tests/qapi-schema/include_non_filename.json @@ -0,0 +1,4 @@ +{ 'enum': 'Status', + 'data': [ 'good', 'bad', 'ugly' ] } +{ 'include': [ 'foo', 'bar'] } +{ 'foo': '42' } diff --git a/tests/qapi-schema/include_non_filename.out b/tests/qapi-schema/include_non_filename.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/multi_file_loop_include.err b/tests/qapi-schema/multi_file_loop_include.err new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/multi_file_loop_include.exit b/tests/qapi-schema/multi_file_loop_include.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/multi_file_loop_include.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/multi_file_loop_include.json b/tests/qapi-schema/multi_file_loop_include.json new file mode 100644 index 0000000..0b11f27 --- /dev/null +++ b/tests/qapi-schema/multi_file_loop_include.json @@ -0,0 +1,4 @@ +{ 'enum': 'Status', + 'data': [ 'good', 'bad', 'ugly' ] } +{ 'include': './include/multi_loop.json' } +{ 'foo': '42' } diff --git a/tests/qapi-schema/multi_file_loop_include.out b/tests/qapi-schema/multi_file_loop_include.out new file mode 100644 index 0000000..9af609b --- /dev/null +++ b/tests/qapi-schema/multi_file_loop_include.out @@ -0,0 +1,4 @@ +Inclusion loop detected with file: multi_file_loop_include.json +Path to the broken include is: + multi_file_loop_include.json + multi_loop.json diff --git a/tests/qapi-schema/multiple_relative_path_include.err b/tests/qapi-schema/multiple_relative_path_include.err new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/multiple_relative_path_include.exit b/tests/qapi-schema/multiple_relative_path_include.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/qapi-schema/multiple_relative_path_include.exit @@ -0,0 +1 @@ +0 diff --git a/tests/qapi-schema/multiple_relative_path_include.json b/tests/qapi-schema/multiple_relative_path_include.json new file mode 100644 index 0000000..3afd95b --- /dev/null +++ b/tests/qapi-schema/multiple_relative_path_include.json @@ -0,0 +1,4 @@ +{ 'enum': 'Failure', + 'data': [ 'orange', 'apple', 'apricot' ] } +{ 'include': './include/multi.json' } +{ 'foo': '42' } diff --git a/tests/qapi-schema/multiple_relative_path_include.out b/tests/qapi-schema/multiple_relative_path_include.out new file mode 100644 index 0000000..e75c817 --- /dev/null +++ b/tests/qapi-schema/multiple_relative_path_include.out @@ -0,0 +1,6 @@ +[OrderedDict([('enum', 'Failure'), ('data', ['orange', 'apple', 'apricot'])]), + OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])]), + OrderedDict([('foo', '42')])] +[{'enum_name': 'Failure', 'enum_values': ['orange', 'apple', 'apricot']}, + {'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}] +[] -- 1.7.10.4