[glib: 2/5] codegen: Add --interface-info-[body|header] modes



commit 8916874ee6f3ff0f887dbe1eda55c23c2c0097ee
Author: Philip Withnall <withnall endlessm com>
Date:   Tue Apr 17 14:10:07 2018 +0100

    codegen: Add --interface-info-[body|header] modes
    
    These generate basic .c and .h files containing the GDBusInterfaceInfo
    for a D-Bus introspection XML file, but no other code (no skeletons,
    proxies, GObjects, etc.).
    
    This is useful for projects who want to describe their D-Bus interfaces
    using introspection XML, but who wish to implement the interfaces
    manually (for various reasons, typically because the skeletons generated
    by gdbus-codegen are too simplistic and limiting). Previously, these
    projects would have had to write the GDBusInterfaceInfo manually, which
    is painstaking and error-prone.
    
    The new --interface-info-[body|header] options are very similar to
    --[body|header], but mutually exclusive with them.
    
    Signed-off-by: Philip Withnall <withnall endlessm com>
    
    https://bugzilla.gnome.org/show_bug.cgi?id=795304

 docs/reference/gio/gdbus-codegen.xml  |  65 +++++++-
 gio/gdbus-2.0/codegen/codegen.py      | 280 ++++++++++++++++++++++++++++++++++
 gio/gdbus-2.0/codegen/codegen_main.py |  39 +++++
 3 files changed, 377 insertions(+), 7 deletions(-)
---
diff --git a/docs/reference/gio/gdbus-codegen.xml b/docs/reference/gio/gdbus-codegen.xml
index b1145e5ef..3e1a9d668 100644
--- a/docs/reference/gio/gdbus-codegen.xml
+++ b/docs/reference/gio/gdbus-codegen.xml
@@ -39,6 +39,8 @@
     <arg><option>--xml-files</option> <replaceable>FILE</replaceable></arg>
     <arg><option>--header</option></arg>
     <arg><option>--body</option></arg>
+    <arg><option>--interface-info-header</option></arg>
+    <arg><option>--interface-info-body</option></arg>
     <arg><option>--output</option> <replaceable>OUTFILE</replaceable></arg>
     <group choice="plain" rep="repeat">
       <arg>
@@ -69,7 +71,11 @@
     arguments on the command line and generates output files.
     It currently supports generating C source code (via
     <option>--body</option>) or header (via <option>--header</option>)
-    and Docbook XML (via <option>--generate-docbook</option>).
+    and Docbook XML (via <option>--generate-docbook</option>). Alternatively,
+    more restricted C source code and headers can be generated, which just
+    contain the interface information (as <type>GDBusInterfaceInfo</type>
+    structures) using <option>--interface-info-body</option> and
+    <option>--interface-info-header</option>.
   </para>
 </refsect1>
 
@@ -90,8 +96,11 @@
   </para>
   <para>
     For C code generation either <option>--body</option> that
-    generates source code, or <option>--header</option> that
-    generates headers, can be used. These options must be used along with
+    generates source code, <option>--header</option> that
+    generates headers, <option>--interface-info-body</option> that generates
+    interface information source code, or
+    <option>--interface-info-header</option> that generates interface information
+    headers, can be used. These options must be used along with
     <option>--output</option>, which is used to specify the file to output to.
   </para>
   <para>
@@ -282,8 +291,10 @@
           Directory to output generated source to. Equivalent to changing directory before generation.
         </para>
         <para>
-          This option cannot be used with neither <option>--body</option> nor
-          <option>--header</option>, and <option>--output</option> must be used.
+          This option cannot be used with <option>--body</option>,
+          <option>--header</option>, <option>--interface-info-body</option> or
+          <option>--interface-info-header</option>; and
+          <option>--output</option> must be used.
         </para>
 
       </listitem>
@@ -321,12 +332,52 @@
       </listitem>
     </varlistentry>
 
+    <varlistentry>
+      <term><option>--interface-info-header</option></term>
+      <listitem>
+        <para>
+          If this option is passed, it will generate the header code for the
+          <type>GDBusInterfaceInfo</type> structures only and will write it to
+          the disk by using the path and file name provided by
+          <option>--output</option>.
+        </para>
+        <para>
+          Using <option>--generate-c-code</option>, <option>--generate-docbook</option> or
+          <option>--output-directory</option> are not allowed to be used along with
+          the <option>--interface-info-header</option> and
+          <option>--interface-info-body</option> options, because these options
+          are used to generate only one file.
+        </para>
+      </listitem>
+    </varlistentry>
+
+    <varlistentry>
+      <term><option>--interface-info-body</option></term>
+      <listitem>
+        <para>
+          If this option is passed, it will generate the source code for the
+          <type>GDBusInterfaceInfo</type> structures only and will write it to
+          the disk by using the path and file name provided by
+          <option>--output</option>.
+        </para>
+        <para>
+          Using <option>--generate-c-code</option>, <option>--generate-docbook</option> or
+          <option>--output-directory</option> are not allowed to be used along with
+          the <option>--interface-info-header</option> and
+          <option>--interface-info-body</option> options, because these options
+          are used to generate only one file.
+        </para>
+      </listitem>
+    </varlistentry>
+
     <varlistentry>
       <term><option>--output</option> <replaceable>OUTFILE</replaceable></term>
       <listitem>
         <para>
-          The full path where the header (<option>--header</option>) or the source code
-          (<option>--body</option>) will be written, using the path and filename provided by
+          The full path where the header (<option>--header</option>,
+          <option>--interface-info-header</option>) or the source code
+          (<option>--body</option>, <option>--interface-info-body</option>) will
+          be written, using the path and filename provided by
           <option>--output</option>. The full path could be something like
           <literal>$($OUTFILE).{c,h}</literal>.
         </para>
diff --git a/gio/gdbus-2.0/codegen/codegen.py b/gio/gdbus-2.0/codegen/codegen.py
index 032a29ed5..307018571 100644
--- a/gio/gdbus-2.0/codegen/codegen.py
+++ b/gio/gdbus-2.0/codegen/codegen.py
@@ -613,6 +613,286 @@ class HeaderCodeGenerator:
 
 # ----------------------------------------------------------------------------------------------------
 
+class InterfaceInfoHeaderCodeGenerator:
+    def __init__(self, ifaces, namespace, header_name, use_pragma, outfile):
+        self.ifaces = ifaces
+        self.namespace, self.ns_upper, self.ns_lower = generate_namespace(namespace)
+        self.header_guard = header_name.upper().replace('.', '_').replace('-', '_').replace('/', 
'_').replace(':', '_')
+        self.use_pragma = use_pragma
+        self.outfile = outfile
+
+    # ----------------------------------------------------------------------------------------------------
+
+    def generate_header_preamble(self):
+        self.outfile.write(LICENSE_STR.format(config.VERSION))
+        self.outfile.write('\n')
+
+        if self.use_pragma:
+            self.outfile.write('#pragma once\n')
+        else:
+            self.outfile.write('#ifndef __{!s}__\n'.format(self.header_guard))
+            self.outfile.write('#define __{!s}__\n'.format(self.header_guard))
+
+        self.outfile.write('\n')
+        self.outfile.write('#include <gio/gio.h>\n')
+        self.outfile.write('\n')
+        self.outfile.write('G_BEGIN_DECLS\n')
+        self.outfile.write('\n')
+
+    # ----------------------------------------------------------------------------------------------------
+
+    def declare_infos(self):
+        for i in self.ifaces:
+            self.outfile.write('extern const GDBusInterfaceInfo %s_interface;\n' % i.name_lower)
+
+    # ----------------------------------------------------------------------------------------------------
+
+    def generate_header_postamble(self):
+        self.outfile.write('\n')
+        self.outfile.write('G_END_DECLS\n')
+
+        if not self.use_pragma:
+            self.outfile.write('\n')
+            self.outfile.write('#endif /* __{!s}__ */\n'.format(self.header_guard))
+
+    # ----------------------------------------------------------------------------------------------------
+
+    def generate(self):
+        self.generate_header_preamble()
+        self.declare_infos()
+        self.generate_header_postamble()
+
+# ----------------------------------------------------------------------------------------------------
+
+class InterfaceInfoBodyCodeGenerator:
+    def __init__(self, ifaces, namespace, header_name, outfile):
+        self.ifaces = ifaces
+        self.namespace, self.ns_upper, self.ns_lower = generate_namespace(namespace)
+        self.header_name = header_name
+        self.outfile = outfile
+
+    # ----------------------------------------------------------------------------------------------------
+
+    def generate_body_preamble(self):
+        self.outfile.write(LICENSE_STR.format(config.VERSION))
+        self.outfile.write('\n')
+        self.outfile.write('#ifdef HAVE_CONFIG_H\n'
+                           '#  include "config.h"\n'
+                           '#endif\n'
+                           '\n'
+                           '#include "%s"\n'
+                           '\n'
+                           '#include <string.h>\n'
+                           % (self.header_name))
+        self.outfile.write('\n')
+
+    # ----------------------------------------------------------------------------------------------------
+
+    def generate_array(self, array_name_lower, element_type, elements):
+        self.outfile.write('const %s * const %s[] =\n' % (element_type, array_name_lower))
+        self.outfile.write('{\n')
+        for (_, name) in sorted(elements, key=utils.version_cmp_key):
+            self.outfile.write('  &%s,\n' % name)
+        self.outfile.write('  NULL,\n')
+        self.outfile.write('};\n')
+        self.outfile.write('\n')
+
+    def define_annotations(self, array_name_lower, annotations):
+        if len(annotations) == 0:
+            return
+
+        annotation_pointers = []
+
+        for a in annotations:
+            # Skip internal annotations.
+            if a.key.startswith('org.gtk.GDBus'):
+                continue
+
+            self.define_annotations('%s__%s_annotations' % (array_name_lower, a.key_lower), a.annotations)
+
+            self.outfile.write('const GDBusAnnotationInfo %s__%s_annotation =\n' % (array_name_lower, 
a.key_lower))
+            self.outfile.write('{\n')
+            self.outfile.write('  -1,  /* ref count */\n')
+            self.outfile.write('  (gchar *) "%s",\n' % a.key)
+            self.outfile.write('  (gchar *) "%s",\n' % a.value)
+            if len(a.annotations) > 0:
+                self.outfile.write('  (GDBusAnnotationInfo **) %s__%s_annotations,\n' % (array_name_lower, 
a.key_lower))
+            else:
+                self.outfile.write('  NULL,  /* no annotations */\n')
+            self.outfile.write('};\n')
+            self.outfile.write('\n')
+
+            key = (a.since, '%s__%s_annotation' % (array_name_lower, a.key_lower))
+            annotation_pointers.append(key)
+
+        self.generate_array(array_name_lower, 'GDBusAnnotationInfo',
+                            annotation_pointers)
+
+    def define_args(self, array_name_lower, args):
+        if len(args) == 0:
+            return
+
+        arg_pointers = []
+
+        for a in args:
+            self.define_annotations('%s__%s_arg_annotations' % (array_name_lower, a.name), a.annotations)
+
+            self.outfile.write('const GDBusArgInfo %s__%s_arg =\n' % (array_name_lower, a.name))
+            self.outfile.write('{\n')
+            self.outfile.write('  -1,  /* ref count */\n')
+            self.outfile.write('  (gchar *) "%s",\n' % a.name)
+            self.outfile.write('  (gchar *) "%s",\n' % a.signature)
+            if len(a.annotations) > 0:
+                self.outfile.write('  (GDBusAnnotationInfo **) %s__%s_arg_annotations,\n' % 
(array_name_lower, a.name))
+            else:
+                self.outfile.write('  NULL,  /* no annotations */\n')
+            self.outfile.write('};\n')
+            self.outfile.write('\n')
+
+            key = (a.since, '%s__%s_arg' % (array_name_lower, a.name))
+            arg_pointers.append(key)
+
+        self.generate_array(array_name_lower, 'GDBusArgInfo', arg_pointers)
+
+    def define_infos(self):
+        for i in self.ifaces:
+            self.outfile.write('/* ------------------------------------------------------------------------ 
*/\n')
+            self.outfile.write('/* Definitions for %s */\n' % i.name)
+            self.outfile.write('\n')
+
+            # GDBusMethodInfos.
+            if len(i.methods) > 0:
+                method_pointers = []
+
+                for m in i.methods:
+                    self.define_args('%s_interface__%s_method_in_args' % (i.name_lower, m.name_lower), 
m.in_args)
+                    self.define_args('%s_interface__%s_method_out_args' % (i.name_lower, m.name_lower), 
m.out_args)
+                    self.define_annotations('%s_interface__%s_method_annotations' % (i.name_lower, 
m.name_lower), m.annotations)
+
+                    self.outfile.write('const GDBusMethodInfo %s_interface__%s_method =\n' % (i.name_lower, 
m.name_lower))
+                    self.outfile.write('{\n')
+                    self.outfile.write('  -1,  /* ref count */\n')
+                    self.outfile.write('  (gchar *) "%s",\n' % m.name)
+                    if len(m.in_args) > 0:
+                        self.outfile.write('  (GDBusArgInfo **) %s_interface__%s_method_in_args,\n' % 
(i.name_lower, m.name_lower))
+                    else:
+                        self.outfile.write('  NULL,  /* no in args */\n')
+                    if len(m.out_args) > 0:
+                        self.outfile.write('  (GDBusArgInfo **) %s_interface__%s_method_out_args,\n' % 
(i.name_lower, m.name_lower))
+                    else:
+                        self.outfile.write('  NULL,  /* no out args */\n')
+                    if len(m.annotations) > 0:
+                        self.outfile.write('  (GDBusAnnotationInfo **) 
%s_interface__%s_method_annotations,\n' % (i.name_lower, m.name_lower))
+                    else:
+                        self.outfile.write('  NULL,  /* no annotations */\n')
+                    self.outfile.write('};\n')
+                    self.outfile.write('\n')
+
+                    key = (m.since, '%s_interface__%s_method' % (i.name_lower, m.name_lower))
+                    method_pointers.append(key)
+
+                self.generate_array('%s_interface_methods' % i.name_lower,
+                                    'GDBusMethodInfo', method_pointers)
+
+            # GDBusSignalInfos.
+            if len(i.signals) > 0:
+                signal_pointers = []
+
+                for s in i.signals:
+                    self.define_args('%s_interface__%s_signal_args' % (i.name_lower, s.name_lower), s.args)
+                    self.define_annotations('%s_interface__%s_signal_annotations' % (i.name_lower, 
s.name_lower), s.annotations)
+
+                    self.outfile.write('const GDBusSignalInfo %s_interface__%s_signal =\n' % (i.name_lower, 
s.name_lower))
+                    self.outfile.write('{\n')
+                    self.outfile.write('  -1,  /* ref count */\n')
+                    self.outfile.write('  (gchar *) "%s",\n' % s.name)
+                    if len(s.args) > 0:
+                        self.outfile.write('  (GDBusArgInfo **) %s_interface__%s_signal_args,\n' % 
(i.name_lower, s.name_lower))
+                    else:
+                        self.outfile.write('  NULL,  /* no args */\n')
+                    if len(s.annotations) > 0:
+                        self.outfile.write('  (GDBusAnnotationInfo **) 
%s_interface__%s_signal_annotations,\n' % (i.name_lower, s.name_lower))
+                    else:
+                        self.outfile.write('  NULL,  /* no annotations */\n')
+                    self.outfile.write('};\n')
+                    self.outfile.write('\n')
+
+                    key = (m.since, '%s_interface__%s_signal' % (i.name_lower, s.name_lower))
+                    signal_pointers.append(key)
+
+                self.generate_array('%s_interface_signals' % i.name_lower,
+                                    'GDBusSignalInfo', signal_pointers)
+
+            # GDBusPropertyInfos.
+            if len(i.properties) > 0:
+                property_pointers = []
+
+                for p in i.properties:
+                    if p.readable and p.writable:
+                        flags = 'G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE'
+                    elif p.readable:
+                        flags = 'G_DBUS_PROPERTY_INFO_FLAGS_READABLE'
+                    elif p.writable:
+                        flags = 'G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE'
+                    else:
+                        flags = 'G_DBUS_PROPERTY_INFO_FLAGS_NONE'
+
+                    self.define_annotations('%s_interface__%s_property_annotations' % (i.name_lower, 
p.name_lower), p.annotations)
+
+                    self.outfile.write('const GDBusPropertyInfo %s_interface__%s_property =\n' % 
(i.name_lower, p.name_lower))
+                    self.outfile.write('{\n')
+                    self.outfile.write('  -1,  /* ref count */\n')
+                    self.outfile.write('  (gchar *) "%s",\n' % p.name)
+                    self.outfile.write('  (gchar *) "%s",\n' % p.signature)
+                    self.outfile.write('  %s,\n' % flags)
+                    if len(p.annotations) > 0:
+                        self.outfile.write('  (GDBusAnnotationInfo **) 
%s_interface__%s_property_annotations,\n' % (i.name_lower, p.name_lower))
+                    else:
+                        self.outfile.write('  NULL,  /* no annotations */\n')
+                    self.outfile.write('};\n')
+                    self.outfile.write('\n')
+
+                    key = (m.since, '%s_interface__%s_property' % (i.name_lower, p.name_lower))
+                    property_pointers.append(key)
+
+                self.generate_array('%s_interface_properties' % i.name_lower,
+                                    'GDBusPropertyInfo', property_pointers)
+
+            # Finally the GDBusInterfaceInfo.
+            self.define_annotations('%s_interface_annotations' % i.name_lower,
+                                    i.annotations)
+
+            self.outfile.write('const GDBusInterfaceInfo %s_interface =\n' % i.name_lower)
+            self.outfile.write('{\n')
+            self.outfile.write('  -1,  /* ref count */\n')
+            self.outfile.write('  (gchar *) "%s",\n' % i.name)
+            if len(i.methods) > 0:
+                self.outfile.write('  (GDBusMethodInfo **) %s_interface_methods,\n' % i.name_lower)
+            else:
+                self.outfile.write('  NULL,  /* no methods */\n')
+            if len(i.signals) > 0:
+                self.outfile.write('  (GDBusSignalInfo **) %s_interface_signals,\n' % i.name_lower)
+            else:
+                self.outfile.write('  NULL,  /* no signals */\n')
+            if len(i.properties) > 0:
+                self.outfile.write('  (GDBusPropertyInfo **) %s_interface_properties,\n' % i.name_lower)
+            else:
+                self.outfile.write(  'NULL,  /* no properties */\n')
+            if len(i.annotations) > 0:
+                self.outfile.write('  (GDBusAnnotationInfo **) %s_interface_annotations,\n' % i.name_lower)
+            else:
+                self.outfile.write('  NULL,  /* no annotations */\n')
+            self.outfile.write('};\n')
+            self.outfile.write('\n')
+
+    # ----------------------------------------------------------------------------------------------------
+
+    def generate(self):
+        self.generate_body_preamble()
+        self.define_infos()
+
+# ----------------------------------------------------------------------------------------------------
+
 class CodeGenerator:
     def __init__(self, ifaces, namespace, generate_objmanager, header_name,
                  input_files_basenames, docbook_gen, outfile):
diff --git a/gio/gdbus-2.0/codegen/codegen_main.py b/gio/gdbus-2.0/codegen/codegen_main.py
index 65876a2e8..344cb4cc2 100755
--- a/gio/gdbus-2.0/codegen/codegen_main.py
+++ b/gio/gdbus-2.0/codegen/codegen_main.py
@@ -175,6 +175,10 @@ def codegen_main():
                        help='Generate C headers')
     group.add_argument('--body', action='store_true',
                        help='Generate C code')
+    group.add_argument('--interface-info-header', action='store_true',
+                       help='Generate GDBusInterfaceInfo C header')
+    group.add_argument('--interface-info-body', action='store_true',
+                       help='Generate GDBusInterfaceInfo C code')
 
     group = arg_parser.add_mutually_exclusive_group()
     group.add_argument('--output', metavar='FILE',
@@ -210,6 +214,24 @@ def codegen_main():
 
         c_file = args.output
         header_name = os.path.splitext(os.path.basename(c_file))[0] + '.h'
+    elif args.interface_info_header:
+        if args.output is None:
+            print_error('Using --interface-info-header requires --output')
+        if args.c_generate_object_manager:
+            print_error('--c-generate-object-manager is incompatible with '
+                        '--interface-info-header')
+
+        h_file = args.output
+        header_name = os.path.basename(h_file)
+    elif args.interface_info_body:
+        if args.output is None:
+            print_error('Using --interface-info-body requires --output')
+        if args.c_generate_object_manager:
+            print_error('--c-generate-object-manager is incompatible with '
+                        '--interface-info-body')
+
+        c_file = args.output
+        header_name = os.path.splitext(os.path.basename(c_file))[0] + '.h'
 
     all_ifaces = []
     input_files_basenames = []
@@ -254,6 +276,23 @@ def codegen_main():
                                         outfile)
             gen.generate()
 
+    if args.interface_info_header:
+        with open(h_file, 'w') as outfile:
+            gen = codegen.InterfaceInfoHeaderCodeGenerator(all_ifaces,
+                                                           args.c_namespace,
+                                                           header_name,
+                                                           args.pragma_once,
+                                                           outfile)
+            gen.generate()
+
+    if args.interface_info_body:
+        with open(c_file, 'w') as outfile:
+            gen = codegen.InterfaceInfoBodyCodeGenerator(all_ifaces,
+                                                         args.c_namespace,
+                                                         header_name,
+                                                         outfile)
+            gen.generate()
+
     sys.exit(0)
 
 if __name__ == "__main__":


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]