From nobody Tue Feb 10 16:22:42 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 150695939813330.07666950793032; Mon, 2 Oct 2017 08:49:58 -0700 (PDT) Received: from localhost ([::1]:52923 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dz2yh-0003xh-1z for importer@patchew.org; Mon, 02 Oct 2017 11:49:55 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38949) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dz2bj-00084N-3P for qemu-devel@nongnu.org; Mon, 02 Oct 2017 11:26:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dz2bf-000819-7Q for qemu-devel@nongnu.org; Mon, 02 Oct 2017 11:26:11 -0400 Received: from mx1.redhat.com ([209.132.183.28]:38824) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dz2be-000805-UY for qemu-devel@nongnu.org; Mon, 02 Oct 2017 11:26:07 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0554780469; Mon, 2 Oct 2017 15:26:06 +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 27909841C0; Mon, 2 Oct 2017 15:26:02 +0000 (UTC) Received: by blackfin.pond.sub.org (Postfix, from userid 1000) id C712511562EA; Mon, 2 Oct 2017 17:25:52 +0200 (CEST) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 0554780469 Authentication-Results: ext-mx04.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx04.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:43 +0200 Message-Id: <20171002152552.27999-24-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.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Mon, 02 Oct 2017 15:26:06 +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 23/32] qapi-options: Command line option backend 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" New qapi generator qapi-options.py generates code for parsing command line options into an array of QAPIOption. TODO negative tests Signed-off-by: Markus Armbruster --- Makefile | 11 ++- Makefile.objs | 1 + scripts/qapi-options.py | 199 ++++++++++++++++++++++++++++++++++++++++++= ++++ tests/Makefile.include | 16 +++- tests/test-qapi-options.c | 74 +++++++++++++++++ 5 files changed, 297 insertions(+), 4 deletions(-) create mode 100644 scripts/qapi-options.py create mode 100644 tests/test-qapi-options.c diff --git a/Makefile b/Makefile index 421e65d833..5e858f7295 100644 --- a/Makefile +++ b/Makefile @@ -56,8 +56,8 @@ GENERATED_FILES +=3D builtin-qapi-types.h builtin-qapi-ty= pes.c GENERATED_FILES +=3D builtin-qapi-visit.h builtin-qapi-visit.c GENERATED_FILES +=3D qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h GENERATED_FILES +=3D qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c -GENERATED_FILES +=3D qmp-introspect.h -GENERATED_FILES +=3D qmp-introspect.c +GENERATED_FILES +=3D qapi-options.h qmp-introspect.h +GENERATED_FILES +=3D qapi-options.c qmp-introspect.c =20 GENERATED_FILES +=3D trace/generated-tcg-tracers.h =20 @@ -470,6 +470,13 @@ qapi-commands-gen: $(qapi-modules) $(SRC_PATH)/scripts= /qapi-commands.py $(qapi-p $<, \ "GEN","$@") =20 +.INTERMEDIATE: qapi-options-gen +qapi-options.h qapi-options.c: qapi-options-gen +qapi-options-gen: $(qapi-modules) $(SRC_PATH)/scripts/qapi-options.py $(qa= pi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-options.py \ + $<, \ + "GEN","$@") + .INTERMEDIATE: qapi-introspect-gen qmp-introspect.h qmp-introspect.c: qapi-introspect-gen qapi-introspect-gen: $(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.p= y $(qapi-py) diff --git a/Makefile.objs b/Makefile.objs index c2c62cb462..908ec053fe 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -80,6 +80,7 @@ common-obj-$(CONFIG_FDT) +=3D device_tree.o # qapi =20 common-obj-y +=3D qmp-marshal.o +common-obj-y +=3D qapi-options.o common-obj-y +=3D qmp-introspect.o common-obj-y +=3D qmp.o hmp.o endif diff --git a/scripts/qapi-options.py b/scripts/qapi-options.py new file mode 100644 index 0000000000..240c9021c7 --- /dev/null +++ b/scripts/qapi-options.py @@ -0,0 +1,199 @@ +# +# QAPI option generator +# +# Copyright (C) 2017 Red Hat, Inc. +# +# Authors: +# Markus Armbruster +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +from qapi import * + + +class QAPISchemaGenOptionVisitor(QAPISchemaVisitor): + def __init__(self): + self.decl =3D None + self.defn =3D None + self._shortopts =3D None + self._longopts =3D None + self._cases =3D None + + def visit_begin(self, schema): + self.decl =3D '' + self.defn =3D '' + self._shortopts =3D '' + self._longopts =3D '' + self._cases =3D '' + + def visit_end(self): + if self._cases: + c_max =3D c_enum_const(args.prefix + 'QAPIOptionKind', '_MAX') + self.decl +=3D mcgen(''' +%(c_prefix)sQAPIOption *%(c_prefix)sqapi_options_parse(int argc, char *arg= v[]); +''', + c_prefix=3Dc_name(args.prefix, protect=3DFa= lse)) + self.defn +=3D mcgen(''' +%(c_prefix)sQAPIOption *%(c_prefix)sqapi_options_parse(int argc, char *arg= v[]) +{ + static const struct option longopts[] =3D { +%(longopts)s {0} + }; + %(c_prefix)sQAPIOption *opt =3D g_new(%(c_prefix)sQAPIOption, argc); + int nopt, longidx, ret; + Visitor *v; + + optind =3D 0; + + for (nopt =3D 0, opt[nopt].idx =3D 1; + (ret =3D getopt_long_only(argc, argv, %(c_shortopts)s, + longopts, &longidx)) >=3D 0; + opt[++nopt].idx =3D optind) { + if (ret > 255) { + opt[nopt].type =3D longopts[longidx].val - 256; + } + opt[nopt].cnt =3D optind - opt[nopt].idx; + loc_set_cmdline(argv, opt[nopt].idx, opt[nopt].cnt); + + switch(ret) { +%(cases)s + case '?': + exit(1); + default: + abort(); + } + } + + opt[nopt].type =3D %(c_max)s; + opt[nopt].cnt =3D 0; + + return g_renew(%(c_prefix)sQAPIOption, opt, nopt + 1); +} +''', + c_prefix=3Dc_name(args.prefix, protect=3DFa= lse), + c_shortopts=3Dc_string(self._shortopts), + longopts=3Dself._longopts, + cases=3Dself._cases, + c_max=3Dc_max) + self._shortopts =3D None + self._longopts =3D None + self._cases =3D None + + def visit_option(self, name, info, arg_type, short, implied_key, + boxed, help_): + name =3D name[2:] + enum_val =3D c_enum_const(args.prefix + 'QAPIOptionKind', name) + + push_indent(8) + + self._longopts +=3D mcgen(''' +{ + .name =3D "%(name)s", + .has_arg =3D %(has_arg)s, + .val =3D %(val)s +}, +''', + name=3Dname, + has_arg=3D'required_argument' + if arg_type else 'no_argument', + val=3D'256 + ' + enum_val) + if short: + self._shortopts +=3D short + if arg_type: + self._shortopts +=3D ':' + self._cases +=3D mcgen(''' +case '%(char)s': + opt[nopt].type =3D %(case)s; + /* fall through */ +''', + char=3Dshort, case=3Denum_val) + + self._cases +=3D mcgen(''' +case 256 + %(case)s: +''', + case=3Denum_val) + if arg_type is None: + pass + elif isinstance(arg_type, QAPISchemaObjectType): + self._cases +=3D mcgen(''' + v =3D qobject_input_visitor_new_str(optarg, %(c_implied_key)s, &error_= fatal); + visit_start_struct(v, NULL, NULL, 0, &error_fatal); + visit_type_%(c_type)s_members(v, &opt[nopt].u.%(c_name)s, &error_fatal= ); + visit_check_struct(v, &error_fatal); + visit_end_struct(v, NULL); + visit_free(v); +''', + c_name=3Dc_name(name), c_type=3Darg_type.= c_name(), + c_implied_key=3Dc_string(implied_key)) + else: + assert isinstance(arg_type, + (QAPISchemaBuiltinType, QAPISchemaEnumType)) + self._cases +=3D mcgen(''' + v =3D qobject_input_visitor_new_keyval(QOBJECT(qstring_from_str(optarg= ))); + visit_type_%(c_type)s(v, NULL, &opt[nopt].u.%(c_name)s.data, &error_fa= tal); + visit_free(v); +''', + c_name=3Dc_name(name), c_type=3Darg_type.= c_name()) + self._cases +=3D mcgen(''' + break; +''') + + pop_indent(8) + + +args =3D common_argument_parser().parse_args() + +c_comment =3D ''' +/* + * QAPI command line options + * + * Copyright (C) 2017 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or l= ater. + * See the COPYING.LIB file in the top-level directory. + * + */ +''' +h_comment =3D ''' +/* + * QAPI command line options + * + * Copyright (C) 2017 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or l= ater. + * See the COPYING.LIB file in the top-level directory. + * + */ +''' + +(fdef, fdecl) =3D open_output(args.output_dir, args.prefix, + 'qapi-options.c', 'qapi-options.h', + c_comment, h_comment) + +fdef.write(mcgen(''' +#include "qemu/osdep.h" +#include +#include "%(prefix)sqapi-options.h" +#include "%(prefix)sqapi-visit.h" +#include "qapi/error.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qobject-input-visitor.h" +#include "qemu/error-report.h" + +''', + prefix=3Dargs.prefix)) + +fdecl.write(mcgen(''' +#include "%(prefix)sqapi-types.h" + +''', + prefix=3Dargs.prefix)) + +schema =3D QAPISchema(args.schema, args.prefix) +gen =3D QAPISchemaGenOptionVisitor() +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) + +close_output(fdef, fdecl) diff --git a/tests/Makefile.include b/tests/Makefile.include index 2ef5dc51f1..442de4e0aa 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -59,6 +59,7 @@ check-unit-y +=3D tests/test-string-output-visitor$(EXESU= F) gcov-files-test-string-output-visitor-y =3D qapi/string-output-visitor.c check-unit-y +=3D tests/test-qmp-event$(EXESUF) gcov-files-test-qmp-event-y +=3D qapi/qmp-event.c +check-unit-y +=3D tests/test-qapi-options(EXESUF) check-unit-y +=3D tests/test-opts-visitor$(EXESUF) gcov-files-test-opts-visitor-y =3D qapi/opts-visitor.c check-unit-y +=3D tests/test-coroutine$(EXESUF) @@ -542,7 +543,7 @@ check-qapi-schema-y :=3D $(addprefix tests/qapi-schema/= , $(qapi-schema)) =20 GENERATED_FILES +=3D tests/test-qapi-types.h tests/test-qapi-visit.h \ tests/test-qmp-commands.h tests/test-qapi-event.h \ - tests/test-qmp-introspect.h + tests/test-qapi-options.h tests/test-qmp-introspect.h =20 test-obj-y =3D tests/check-qnum.o tests/check-qstring.o tests/check-qdict.= o \ tests/check-qlist.o tests/check-qnull.o \ @@ -567,7 +568,8 @@ QEMU_CFLAGS +=3D -I$(SRC_PATH)/tests test-util-obj-y =3D libqemuutil.a test-qom-obj-y =3D $(qom-obj-y) $(test-util-obj-y) test-qapi-obj-y =3D tests/test-qapi-visit.o tests/test-qapi-types.o \ - tests/test-qapi-event.o tests/test-qmp-introspect.o \ + tests/test-qapi-event.o tests/test-qapi-options.o \ + tests/test-qmp-introspect.o \ $(test-qom-obj-y) benchmark-crypto-obj-y =3D $(crypto-obj-y) $(test-qom-obj-y) test-crypto-obj-y =3D $(crypto-obj-y) $(test-qom-obj-y) @@ -651,6 +653,15 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(= SRC_PATH)/scripts/qapi-com -o tests -p "test-" $<, \ "GEN","$@") =20 +.INTERMEDIATE: tests/test-qapi-options-gen +tests/test-qapi-options.c tests/test-qapi-options.h: tests/test-qapi-optio= ns-gen +tests/test-qapi-options-gen: \ +$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qa= pi-options.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-options.py \ + -o tests -p "test-" $<, \ + "GEN","$@") +tests/test-qmp-introspect.c tests/test-qmp-introspect.h :\ + .INTERMEDIATE: tests/test-qapi-event-gen tests/test-qapi-event.c tests/test-qapi-event.h: tests/test-qapi-event-gen= ; tests/test-qapi-event-gen: \ @@ -673,6 +684,7 @@ tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests= /qapi-schema/doc-good.jso tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visito= r.o $(test-qapi-obj-y) tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.= o $(test-qapi-obj-y) tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) +tests/test-qapi-options(EXESUF): tests/test-qapi-options.o $(test-qapi-obj= -y) tests/test-qobject-output-visitor$(EXESUF): tests/test-qobject-output-visi= tor.o $(test-qapi-obj-y) tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-= obj-y) tests/test-qobject-input-visitor$(EXESUF): tests/test-qobject-input-visito= r.o $(test-qapi-obj-y) qmp-introspect.o diff --git a/tests/test-qapi-options.c b/tests/test-qapi-options.c new file mode 100644 index 0000000000..aa4394abdb --- /dev/null +++ b/tests/test-qapi-options.c @@ -0,0 +1,74 @@ +#include "qemu/osdep.h" +#include "qapi/qmp/qlit.h" +#include "test-qapi-options.h" + +static void test_qapi_options_parse(void) +{ + static const char *argv[] =3D { + "progname", + "--help", + "-h", + "--opt-str", "hello", + "--opt-int", "123", + "--opt-enum", "value1", + "--opt-any", "hello", + "--opt-any", "123", + "--opt-struct", "sval,i=3D1", + "--opt-boxed", "integer=3D42", + NULL + }; + static QLitObject qlit_hello =3D QLIT_QSTR("hello"); + static QLitObject qlit_123 =3D QLIT_QSTR("123"); + test_QAPIOption *opt; + + opt =3D test_qapi_options_parse(ARRAY_SIZE(argv) - 1, (char **)argv); + g_assert_cmpint(opt[0].type, =3D=3D, TEST_QAPI_OPTION_KIND_HELP); + g_assert_cmpint(opt[0].idx, =3D=3D, 1); + g_assert_cmpint(opt[0].cnt, =3D=3D, 1); + g_assert_cmpint(opt[1].type, =3D=3D, TEST_QAPI_OPTION_KIND_HELP); + g_assert_cmpint(opt[1].idx, =3D=3D, 2); + g_assert_cmpint(opt[1].cnt, =3D=3D, 1); + g_assert_cmpint(opt[2].type, =3D=3D, TEST_QAPI_OPTION_KIND_OPT_STR); + g_assert_cmpint(opt[2].idx, =3D=3D, 3); + g_assert_cmpint(opt[2].cnt, =3D=3D, 2); + g_assert_cmpstr(opt[2].u.opt_str.data, =3D=3D, "hello"); + g_assert_cmpint(opt[3].type, =3D=3D, TEST_QAPI_OPTION_KIND_OPT_INT); + g_assert_cmpint(opt[3].idx, =3D=3D, 5); + g_assert_cmpint(opt[3].cnt, =3D=3D, 2); + g_assert_cmpint(opt[3].u.opt_int.data, =3D=3D, 123); + g_assert_cmpint(opt[4].type, =3D=3D, TEST_QAPI_OPTION_KIND_OPT_ENUM); + g_assert_cmpint(opt[4].idx, =3D=3D, 7); + g_assert_cmpint(opt[4].cnt, =3D=3D, 2); + g_assert_cmpint(opt[4].u.opt_enum.data, =3D=3D, ENUM_ONE_VALUE1); + g_assert_cmpint(opt[5].type, =3D=3D, TEST_QAPI_OPTION_KIND_OPT_ANY); + g_assert_cmpint(opt[5].idx, =3D=3D, 9); + g_assert_cmpint(opt[5].cnt, =3D=3D, 2); + g_assert(qlit_equal_qobject(&qlit_hello, opt[5].u.opt_any.data)); + g_assert_cmpint(opt[6].type, =3D=3D, TEST_QAPI_OPTION_KIND_OPT_ANY); + g_assert_cmpint(opt[6].idx, =3D=3D, 11); + g_assert_cmpint(opt[6].cnt, =3D=3D, 2); + g_assert(qlit_equal_qobject(&qlit_123, opt[6].u.opt_any.data)); + g_assert_cmpint(opt[7].type, =3D=3D, TEST_QAPI_OPTION_KIND_OPT_STRUCT); + g_assert_cmpint(opt[7].idx, =3D=3D, 13); + g_assert_cmpint(opt[7].cnt, =3D=3D, 2); + g_assert_cmpstr(opt[7].u.opt_struct.s, =3D=3D, "sval"); + g_assert_cmpint(opt[8].type, =3D=3D, TEST_QAPI_OPTION_KIND_OPT_BOXED); + g_assert_cmpint(opt[8].idx, =3D=3D, 15); + g_assert_cmpint(opt[8].cnt, =3D=3D, 2); + g_assert_cmpint(opt[8].u.opt_boxed.integer, =3D=3D, 42); + g_assert_cmpint(opt[9].type, =3D=3D, TEST_QAPI_OPTION_KIND__MAX); + g_assert_cmpint(opt[9].idx, =3D=3D, 17); + g_assert_cmpint(opt[9].cnt, =3D=3D, 0); + g_free(opt); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/qapi/options-parse", test_qapi_options_parse); + + g_test_run(); + + return 0; +} --=20 2.13.6