Signed-off-by: Lluís Vilanova <vilan...@ac.upc.edu> --- docs/qapi-code-gen.txt | 11 +++++++++++ scripts/qapi.py | 39 ++++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 5 deletions(-)
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 824f6e5..70b4eeb 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..9f73426 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 @@ -62,8 +64,11 @@ class QAPIExprError(Exception): class QAPISchema: - def __init__(self, fp): + def __init__(self, fp, included=[]): self.fp = fp + input_path = os.path.abspath(fp.name) + self.included = included + [input_path] + self.input_dir = os.path.dirname(input_path) self.src = fp.read() if self.src == '' or self.src[-1] != '\n': self.src += '\n' @@ -75,9 +80,33 @@ class QAPISchema: 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 = self.get_expr(False) + if isinstance(expr, dict) and "include" in expr: + if len(expr) != 1: + raise QAPIExprError(expr_info, "Invalid 'include' directive") + include_path = expr["include"] + if not isinstance(include_path, str): + raise QAPIExprError(expr_info, + 'Expected a file path (string), got: %s' + % include_path) + 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 included file "%s"' % + include_path) + if include_path in self.included: + raise QAPIExprError(expr_info, "Infinite inclusion loop: %s" + % " -> ".join(self.included + + [include_path])) + exprs_include = QAPISchema(open(include_path, "r"), + self.included) + 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 +296,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)