All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: qemu-devel@nongnu.org
Cc: mdroth@linux.vnet.ibm.com, marcandre.lureau@redhat.com,
	eblake@redhat.com
Subject: [Qemu-devel] [RFC PATCH 20/32] qapi: Frontend for defining command line options
Date: Mon,  2 Oct 2017 17:25:40 +0200	[thread overview]
Message-ID: <20171002152552.27999-21-armbru@redhat.com> (raw)
In-Reply-To: <20171002152552.27999-1-armbru@redhat.com>

TODO explain why
TODO update qapi-code-gen.txt
TODO negative tests

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 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 != 'member-struct':
             raise QAPISemError(info, "%s does not allow optional name '%s'"
                                % (source, name))
+    # Options start with '--'
+    if meta == 'option':
+        if not name.startswith('--'):
+            raise QAPISemError(info, "%s must start with '--'" % source)
+        membername = name[2:]
     # Enum members can start with a digit, because the generated C
     # code always prefixes it with the enum name
     if meta == 'member-enum' and membername[0].isdigit():
@@ -708,6 +713,17 @@ def check_event(expr, info):
                expr.get('data'), allow_dict=not boxed, allow_metas=meta)
 
 
+def check_option(expr, info):
+    name = expr['option']
+    boxed = expr.get('boxed', False)
+
+    meta = ['built-in', 'struct', 'enum']
+    if boxed:
+        meta += ['union', 'alternate']
+    check_type(info, "'data' for option '%s'" % name,
+               expr.get('data'), allow_dict=not boxed, allow_metas=meta)
+
+
 def check_union(expr, info):
     name = expr['union']
     base = expr.get('base')
@@ -912,6 +928,10 @@ def check_exprs(exprs):
         elif 'event' in expr:
             meta = 'event'
             check_keys(expr_elem, 'event', [], ['data', 'boxed'])
+        elif 'option' in expr:
+            meta = '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'
 
@@ -1025,6 +1047,10 @@ class QAPISchemaVisitor(object):
     def visit_event(self, name, info, arg_type, boxed):
         pass
 
+    def visit_option(self, name, info, arg_type, short, implied_key,
+                     boxed, help_):
+        pass
+
 
 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.boxed)
 
 
+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 = arg_type
+        self.arg_type = None
+        self.short = short
+        self.implied_key = implied_key
+        self.boxed = boxed
+        self.help = help_
+
+    def check(self, schema):
+        if self._arg_type_name:
+            self.arg_type = 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, QAPISchemaAlternateType)
+                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) == 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 = []
+        if not isinstance(self.help, list):
+            self.help = [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))
 
+    def _def_option(self, expr, info, doc):
+        name = expr['option']
+        data = expr.get('data')
+        short = expr.get('short')
+        implied_key = expr.get('implied-key')
+        boxed = expr.get('boxed', False)
+        help_ = expr.get('help')
+        if isinstance(data, OrderedDict):
+            data = self._make_implicit_object_type(
+                name[2:], info, doc, 'optarg', self._make_members(data, info))
+        self._def_entity(QAPISchemaOption(name, info, doc, data,
+                                          short, implied_key, boxed, help_))
+
     def _def_exprs(self):
         for expr_elem in self.exprs:
             expr = 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
 
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-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/qapi-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=h
+    boxed=False
+    help=
+option without an argument
+option --opt-any any
+    boxed=False
+    help=
+--opt-any INT   option's argument is an integer
+option --opt-boxed UserDefZero
+    boxed=True
+    help=
+
+option --opt-enum EnumOne
+    boxed=False
+    help=
+option's argument is an enumeration
+option --opt-int int
+    boxed=False
+    help=
+option's argument is an integer
+option --opt-str str
+    boxed=False
+    help=
+option's argument is a string
+option --opt-struct q_obj_opt-struct-optarg
+    implied-key=s
+    boxed=False
+    help=
+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=False
 object q_obj_numberList-wrapper
     member data: numberList optional=False
+object q_obj_opt-struct-optarg
+    member s: str optional=False
+    member i: int optional=True
 object q_obj_sizeList-wrapper
     member data: sizeList optional=False
 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=%s' % boxed
 
+    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=%s' % short
+        if implied_key:
+            print '    implied-key=%s' % implied_key
+        print '    boxed=%s' % boxed
+        print '    help=\n%s' % '\n'.join(help)
+
     @staticmethod
     def _print_variants(variants):
         if variants:
-- 
2.13.6

  parent reply	other threads:[~2017-10-02 15:26 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-02 15:25 [Qemu-devel] [RFC PATCH 00/32] Command line QAPIfication Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 01/32] tests/qapi-schema: Improve coverage of '@' Markus Armbruster
2017-10-04 10:37   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 02/32] texi2pod: Support @verbatim environment Markus Armbruster
2017-10-05 14:58   ` Eric Blake
2017-10-06  5:24     ` Markus Armbruster
2017-10-06 13:40       ` Eric Blake
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 03/32] qapi2texi: Fix for examples containing '@' Markus Armbruster
2017-10-04 10:45   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 04/32] qapi2texi: Fix for '@' not followed by \w character Markus Armbruster
2017-10-04 10:47   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 05/32] qapi2texi: Provide access to Texinfo markup Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 06/32] qapi2texi: Drop | example markup Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 07/32] qapi: Drop superfluous allow_optional=True Markus Armbruster
2017-10-04 10:52   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 08/32] qapi: Simplify check_name() parameters Markus Armbruster
2017-10-04 10:54   ` Marc-André Lureau
2017-10-04 10:54   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 09/32] qapi: check_type() parameter allow_optional is now unused, drop Markus Armbruster
2017-10-04 10:55   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 10/32] qapi: Don't run generators twice Markus Armbruster
2017-10-04 11:04   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 11/32] qapi: Drop the options to generate only .c or .h Markus Armbruster
2017-10-04 11:07   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 12/32] qapi: Use argparse to parse command line arguments Markus Armbruster
2017-10-04 11:13   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 13/32] qapi: Use argparse to open schema file Markus Armbruster
2017-10-04 11:18   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 14/32] qapi: Rework generated code for built-in types Markus Armbruster
2017-10-04 11:52   ` Marc-André Lureau
2017-10-05  4:24     ` Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 15/32] tests/qapi-schema: Improve simple union coverage Markus Armbruster
2017-10-04 12:02   ` Marc-André Lureau
2017-10-05  4:29     ` Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 16/32] qapi: Factor out _make_implicit_wrapper_type() Markus Armbruster
2017-10-04 12:00   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 17/32] qapi: Fix simple union lowering with multiple schemas Markus Armbruster
2017-10-04 12:04   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 18/32] docs/devel/qapi-code-gen.txt: Rewrite section on schema syntax Markus Armbruster
2017-10-04 11:59   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 19/32] qapi: Accept double-quoted strings Markus Armbruster
2017-10-04 11:58   ` Marc-André Lureau
2017-10-05  4:41     ` Markus Armbruster
2017-10-05 14:13       ` Marc-André Lureau
2017-10-06  5:29         ` Markus Armbruster
2017-10-05 15:16       ` Eric Blake
2017-10-06  5:27         ` Markus Armbruster
2017-10-02 15:25 ` Markus Armbruster [this message]
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 21/32] qapi: Define QAPIOptionKind and QAPIOption automatically Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 22/32] qapi: New helper c_string() Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 23/32] qapi-options: Command line option backend Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 24/32] qapi-options: Generate help string Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 25/32] qapi-introspect: Include command line options information Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 26/32] qapi2texi: " Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 27/32] os-posix: Drop misleading comment Markus Armbruster
2017-10-04 12:10   ` Marc-André Lureau
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 28/32] vl: QAPIfy command line option definition Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 29/32] qapi/options: QAPIfy --echr argument type Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 30/32] qapi/options: QAPIfy --watchdog-action " Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 31/32] qapi/options: QAPIfy --blockdev " Markus Armbruster
2017-10-02 15:25 ` [Qemu-devel] [RFC PATCH 32/32] qapi/options: QAPIfy --add-fd " Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20171002152552.27999-21-armbru@redhat.com \
    --to=armbru@redhat.com \
    --cc=eblake@redhat.com \
    --cc=marcandre.lureau@redhat.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.