From nobody Tue Feb 10 16:22:34 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@gnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@gnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1506958770610777.713371277968; Mon, 2 Oct 2017 08:39:30 -0700 (PDT) Received: from localhost ([::1]:52873 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dz2oV-0001x1-Nb for importer@patchew.org; Mon, 02 Oct 2017 11:39:23 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38815) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dz2be-0007z5-Cz for qemu-devel@nongnu.org; Mon, 02 Oct 2017 11:26:10 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dz2bb-0007xe-Qe for qemu-devel@nongnu.org; Mon, 02 Oct 2017 11:26:06 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34798) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dz2bb-0007wB-DB for qemu-devel@nongnu.org; Mon, 02 Oct 2017 11:26:03 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 5E3537E449; Mon, 2 Oct 2017 15:26:02 +0000 (UTC) Received: from blackfin.pond.sub.org (ovpn-116-91.ams2.redhat.com [10.36.116.91]) by smtp.corp.redhat.com (Postfix) with ESMTPS id F34875C544; Mon, 2 Oct 2017 15:26:01 +0000 (UTC) Received: by blackfin.pond.sub.org (Postfix, from userid 1000) id BD70911562E7; Mon, 2 Oct 2017 17:25:52 +0200 (CEST) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 5E3537E449 Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=armbru@redhat.com From: Markus Armbruster To: qemu-devel@nongnu.org Date: Mon, 2 Oct 2017 17:25:40 +0200 Message-Id: <20171002152552.27999-21-armbru@redhat.com> In-Reply-To: <20171002152552.27999-1-armbru@redhat.com> References: <20171002152552.27999-1-armbru@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Mon, 02 Oct 2017 15:26:02 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [RFC PATCH 20/32] qapi: Frontend for defining command line options X-BeenThere: qemu-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: marcandre.lureau@redhat.com, mdroth@linux.vnet.ibm.com Errors-To: qemu-devel-bounces+importer=patchew.org@gnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" TODO explain why TODO update qapi-code-gen.txt TODO negative tests Signed-off-by: Markus Armbruster --- scripts/qapi.py | 91 +++++++++++++++++++++++++++++= ++++ tests/qapi-schema/qapi-schema-test.json | 19 +++++++ tests/qapi-schema/qapi-schema-test.out | 34 ++++++++++++ tests/qapi-schema/test-qapi.py | 10 ++++ 4 files changed, 154 insertions(+) diff --git a/scripts/qapi.py b/scripts/qapi.py index 18c8175866..1e03b62943 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -606,6 +606,11 @@ def check_name(info, source, name, meta): if meta !=3D 'member-struct': raise QAPISemError(info, "%s does not allow optional name '%s'" % (source, name)) + # Options start with '--' + if meta =3D=3D 'option': + if not name.startswith('--'): + raise QAPISemError(info, "%s must start with '--'" % source) + membername =3D name[2:] # Enum members can start with a digit, because the generated C # code always prefixes it with the enum name if meta =3D=3D 'member-enum' and membername[0].isdigit(): @@ -708,6 +713,17 @@ def check_event(expr, info): expr.get('data'), allow_dict=3Dnot boxed, allow_metas=3Dmet= a) =20 =20 +def check_option(expr, info): + name =3D expr['option'] + boxed =3D expr.get('boxed', False) + + meta =3D ['built-in', 'struct', 'enum'] + if boxed: + meta +=3D ['union', 'alternate'] + check_type(info, "'data' for option '%s'" % name, + expr.get('data'), allow_dict=3Dnot boxed, allow_metas=3Dmet= a) + + def check_union(expr, info): name =3D expr['union'] base =3D expr.get('base') @@ -912,6 +928,10 @@ def check_exprs(exprs): elif 'event' in expr: meta =3D 'event' check_keys(expr_elem, 'event', [], ['data', 'boxed']) + elif 'option' in expr: + meta =3D 'option' + check_keys(expr_elem, 'option', ['help'], + ['data', 'short', 'implied-key', 'boxed']) else: raise QAPISemError(expr_elem['info'], "Expression is missing metatype") @@ -951,6 +971,8 @@ def check_exprs(exprs): check_command(expr, info) elif 'event' in expr: check_event(expr, info) + elif 'option' in expr: + check_option(expr, info) else: assert False, 'unexpected meta type' =20 @@ -1025,6 +1047,10 @@ class QAPISchemaVisitor(object): def visit_event(self, name, info, arg_type, boxed): pass =20 + def visit_option(self, name, info, arg_type, short, implied_key, + boxed, help_): + pass + =20 class QAPISchemaType(QAPISchemaEntity): # Return the C type for common use. @@ -1458,6 +1484,56 @@ class QAPISchemaEvent(QAPISchemaEntity): visitor.visit_event(self.name, self.info, self.arg_type, self.boxe= d) =20 =20 +class QAPISchemaOption(QAPISchemaEntity): + def __init__(self, name, info, doc, arg_type, short, implied_key, + boxed, help_): + QAPISchemaEntity.__init__(self, name, info, doc) + assert not arg_type or isinstance(arg_type, str) + self._arg_type_name =3D arg_type + self.arg_type =3D None + self.short =3D short + self.implied_key =3D implied_key + self.boxed =3D boxed + self.help =3D help_ + + def check(self, schema): + if self._arg_type_name: + self.arg_type =3D schema.lookup_type(self._arg_type_name) + assert (isinstance(self.arg_type, QAPISchemaType) + and not isinstance(self.arg_type, QAPISchemaArrayType)) + self.arg_type.check(schema) + if self.boxed: + if self.arg_type.is_empty(): + raise QAPISemError(self.info, + "Cannot use 'boxed' with empty type= ") + else: + assert not isinstance(self.arg_type, QAPISchemaAlternateTy= pe) + assert (not isinstance(self.arg_type, QAPISchemaObjectType) + or not self.arg_type.variants) + elif self.boxed: + raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") + if self.short and not (isinstance(self.short, str) + and len(self.short) =3D=3D 1): + raise QAPISemError(self.info, + "Value of 'short' must be a character") + if self.implied_key and not isinstance(self.implied_key, str): + raise QAPISemError(self.info, + "Value of 'implied-key' must be a string") + if self.help is None: + self.help =3D [] + if not isinstance(self.help, list): + self.help =3D [self.help] + if not all([isinstance(elt, str) for elt in self.help]): + raise QAPISemError( + self.info, + "Value of 'help' must be a string or a list of strings") + + def visit(self, visitor, builtins): + visitor.visit_option(self.name, self.info, self.arg_type, + self.short, self.implied_key, + self.boxed, self.help) + + class QAPISchema(object): def __init__(self, file): try: @@ -1663,6 +1739,19 @@ class QAPISchema(object): name, info, doc, 'arg', self._make_members(data, info)) self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) =20 + def _def_option(self, expr, info, doc): + name =3D expr['option'] + data =3D expr.get('data') + short =3D expr.get('short') + implied_key =3D expr.get('implied-key') + boxed =3D expr.get('boxed', False) + help_ =3D expr.get('help') + if isinstance(data, OrderedDict): + data =3D self._make_implicit_object_type( + name[2:], info, doc, 'optarg', self._make_members(data, in= fo)) + self._def_entity(QAPISchemaOption(name, info, doc, data, + short, implied_key, boxed, help_= )) + def _def_exprs(self): for expr_elem in self.exprs: expr =3D expr_elem['expr'] @@ -1680,6 +1769,8 @@ class QAPISchema(object): self._def_command(expr, info, doc) elif 'event' in expr: self._def_event(expr, info, doc) + elif 'option' in expr: + self._def_option(expr, info, doc) else: assert False =20 diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qa= pi-schema-test.json index c74d5632a5..42b9968c72 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -193,3 +193,22 @@ 'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'], 'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' }, 'returns': '__org.qemu_x-Union1' } + +# testing option +{ 'option': '--help', 'short': 'h', + 'help': "option without an argument" } +{ 'option': '--opt-str', 'data': 'str', + 'help': "option's argument is a string" } +{ 'option': '--opt-int', 'data': 'int', + 'help': "option's argument is an integer" } +{ 'option': '--opt-enum', 'data': 'EnumOne', + 'help': "option's argument is an enumeration" } +{ 'option': '--opt-any', 'data': 'any', + 'help': "--opt-any INT option's argument is an integer" } +{ 'option': '--opt-struct', 'data': { 's': 'str', '*i': 'int' }, + 'implied-key': 's', + 'help': [ + "an option with a complex argument", + "and multi-line help" ] } +{ 'option': '--opt-boxed', 'data': 'UserDefZero', 'boxed': true, + 'help': null } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qap= i-schema-test.out index fff25e26d0..9ee06539ac 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,3 +1,34 @@ +option --help None + short=3Dh + boxed=3DFalse + help=3D +option without an argument +option --opt-any any + boxed=3DFalse + help=3D +--opt-any INT option's argument is an integer +option --opt-boxed UserDefZero + boxed=3DTrue + help=3D + +option --opt-enum EnumOne + boxed=3DFalse + help=3D +option's argument is an enumeration +option --opt-int int + boxed=3DFalse + help=3D +option's argument is an integer +option --opt-str str + boxed=3DFalse + help=3D +option's argument is a string +option --opt-struct q_obj_opt-struct-optarg + implied-key=3Ds + boxed=3DFalse + help=3D +an option with a complex argument +and multi-line help alternate AltEnumBool tag type case e: EnumOne @@ -210,6 +241,9 @@ object q_obj_intList-wrapper member data: intList optional=3DFalse object q_obj_numberList-wrapper member data: numberList optional=3DFalse +object q_obj_opt-struct-optarg + member s: str optional=3DFalse + member i: int optional=3DTrue object q_obj_sizeList-wrapper member data: sizeList optional=3DFalse object q_obj_strList-wrapper diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 0294a66619..2e5e7bfeb1 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -45,6 +45,16 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print 'event %s %s' % (name, arg_type and arg_type.name) print ' boxed=3D%s' % boxed =20 + def visit_option(self, name, info, arg_type, short, implied_key, + boxed, help): + print 'option %s %s' % (name, arg_type and arg_type.name) + if short: + print ' short=3D%s' % short + if implied_key: + print ' implied-key=3D%s' % implied_key + print ' boxed=3D%s' % boxed + print ' help=3D\n%s' % '\n'.join(help) + @staticmethod def _print_variants(variants): if variants: --=20 2.13.6