[glib/gdbus-codegen] Add support for org.gtk.GDBus.Since annotation
- From: David Zeuthen <davidz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/gdbus-codegen] Add support for org.gtk.GDBus.Since annotation
- Date: Tue, 12 Apr 2011 20:18:46 +0000 (UTC)
commit 34a28f2f062281d9fb75fcd02c4df238def6396e
Author: David Zeuthen <davidz redhat com>
Date: Tue Apr 12 16:17:28 2011 -0400
Add support for org.gtk.GDBus.Since annotation
And use this for a) documentation purposes; and b) to preserve C ABI
when an interface is extended. See
https://bugzilla.gnome.org/show_bug.cgi?id=647577#c5
for more details. Also add test cases for this.
Signed-off-by: David Zeuthen <davidz redhat com>
docs/reference/gio/gdbus-codegen.xml | 22 ++++++++++
gio/gdbus-codegen/codegen.py | 60 ++++++++++++++++++++++------
gio/gdbus-codegen/codegen_docbook.py | 8 ++++
gio/gdbus-codegen/dbustypes.py | 15 +++++++
gio/gdbus-codegen/parser.py | 8 ++++
gio/gdbus-codegen/utils.py | 7 +++
gio/tests/gdbus-example-objectmanager.xml | 4 ++
gio/tests/gdbus-test-codegen.c | 22 ++++++++++
gio/tests/test-codegen.xml | 60 +++++++++++++++++++++++++++++
9 files changed, 193 insertions(+), 13 deletions(-)
---
diff --git a/docs/reference/gio/gdbus-codegen.xml b/docs/reference/gio/gdbus-codegen.xml
index 50cf264..d41d6f0 100644
--- a/docs/reference/gio/gdbus-codegen.xml
+++ b/docs/reference/gio/gdbus-codegen.xml
@@ -216,6 +216,28 @@ gdbus-codegen --c-namespace MyApp \
</varlistentry>
<varlistentry>
+ <term><literal>org.gtk.GDBus.Since</literal></term>
+ <listitem>
+ <para>
+ Can be used on any <literal><interface></literal>,
+ <literal><method></literal>,
+ <literal><signal></literal> and
+ <literal><property></literal> element to specify the
+ version (any free-form string but compared using a
+ version-aware sort function) the element appeared in.
+ </para>
+ <para>
+ When generating C code, this field is used to ensure
+ function pointer order for preserving ABI/API.
+ </para>
+ <para>
+ When generating Docbook XML, the value of this tag appears
+ in the documentation.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>org.gtk.GDBus.C.ForceGVariant</literal></term>
<listitem>
<para>
diff --git a/gio/gdbus-codegen/codegen.py b/gio/gdbus-codegen/codegen.py
index fbe2521..9ad1293 100644
--- a/gio/gdbus-codegen/codegen.py
+++ b/gio/gdbus-codegen/codegen.py
@@ -2,6 +2,7 @@
import sys
import argparse
+import distutils.version
import config
import utils
@@ -201,27 +202,60 @@ class CodeGenerator:
self.h.write('struct _%sIface\n'%(i.camel_name))
self.h.write('{\n')
self.h.write(' GTypeInterface parent_iface;\n')
+
+ function_pointers = {}
+
if len(i.methods) > 0:
self.h.write('\n')
- self.h.write(' /* GObject signal class handlers for incoming D-Bus method calls: */\n')
for m in i.methods:
- self.h.write(' gboolean (*handle_%s) (\n'
- ' %s *object,\n'
- ' GDBusMethodInvocation *invocation'%(m.name_lower, i.camel_name))
+ key = (m.since, '_method_%s'%m.name_lower)
+ value = ' gboolean (*handle_%s) (\n'%(m.name_lower)
+ value += ' %s *object,\n'%(i.camel_name)
+ value += ' GDBusMethodInvocation *invocation'%()
for a in m.in_args:
- self.h.write(',\n %s%s'%(a.ctype_in, a.name))
- self.h.write(');\n')
- self.h.write('\n')
+ value += ',\n %s%s'%(a.ctype_in, a.name)
+ value += ');\n\n'
+ function_pointers[key] = value
+
if len(i.signals) > 0:
self.h.write('\n')
- self.h.write(' /* GObject signal class handlers for received D-Bus signals: */\n')
for s in i.signals:
- self.h.write(' void (*%s) (\n'
- ' %s *object'%(s.name_lower, i.camel_name))
+ key = (s.since, '_signal_%s'%s.name_lower)
+ value = ' void (*%s) (\n'%(s.name_lower)
+ value += ' %s *object'%(i.camel_name)
for a in s.args:
- self.h.write(',\n %s%s'%(a.ctype_in, a.name))
- self.h.write(');\n')
- self.h.write('\n')
+ value += ',\n %s%s'%(a.ctype_in, a.name)
+ value += ');\n\n'
+ function_pointers[key] = value
+
+ # Sort according to @since tag, then name.. this ensures
+ # that the function pointers don't change order assuming
+ # judicious use of @since
+ #
+ # Also use a proper version comparison function so e.g.
+ # 10.0 comes after 2.0.
+ #
+ # See https://bugzilla.gnome.org/show_bug.cgi?id=647577#c5
+ # for discussion
+
+ # I'm sure this could be a lot more elegant if I was
+ # more fluent in python...
+ def my_version_cmp(a, b):
+ if len(a[0]) > 0 and len(b[0]) > 0:
+ va = distutils.version.LooseVersion(a[0])
+ vb = distutils.version.LooseVersion(b[0])
+ ret = va.__cmp__(vb)
+ else:
+ ret = cmp(a[0], b[0])
+ if ret != 0:
+ return ret
+ return cmp(a[1], b[1])
+ keys = function_pointers.keys()
+ if len(keys) > 0:
+ keys.sort(cmp=my_version_cmp)
+ for key in keys:
+ self.h.write('%s'%function_pointers[key])
+
self.h.write('};\n')
self.h.write('\n')
self.h.write('GType %s_get_gtype (void) G_GNUC_CONST;\n'%(i.name_lower))
diff --git a/gio/gdbus-codegen/codegen_docbook.py b/gio/gdbus-codegen/codegen_docbook.py
index 889c965..0babb20 100644
--- a/gio/gdbus-codegen/codegen_docbook.py
+++ b/gio/gdbus-codegen/codegen_docbook.py
@@ -163,6 +163,8 @@ class DocbookCodeGenerator:
self.out.write(' <listitem><para>%s</para></listitem>\n'%(self.expand(a.doc_string)))
self.out.write('</varlistentry>\n'%())
self.out.write('</variablelist>\n')
+ if len(m.since) > 0:
+ self.out.write('<para role="since">Since %s</para>\n'%(m.since))
self.out.write('</refsect2>\n')
def print_signal(self, i, s):
@@ -180,6 +182,8 @@ class DocbookCodeGenerator:
self.out.write(' <listitem><para>%s</para></listitem>\n'%(self.expand(a.doc_string)))
self.out.write('</varlistentry>\n'%())
self.out.write('</variablelist>\n')
+ if len(s.since) > 0:
+ self.out.write('<para role="since">Since %s</para>\n'%(s.since))
self.out.write('</refsect2>\n')
def print_property(self, i, p):
@@ -190,6 +194,8 @@ class DocbookCodeGenerator:
self.print_property_prototype(i, p, in_synopsis=False)
self.out.write('</programlisting>\n')
self.out.write('<para>%s</para>\n'%(self.expand(p.doc_string)))
+ if len(p.since) > 0:
+ self.out.write('<para role="since">Since %s</para>\n'%(p.since))
self.out.write('</refsect2>\n')
def expand(self, s):
@@ -255,6 +261,8 @@ class DocbookCodeGenerator:
self.out.write('<refsect1 role="desc" id="gdbus-interface-%s">\n'%(utils.dots_to_hyphens(i.name)))
self.out.write(' <title role="desc.title">Description</title>\n'%())
self.out.write(' <para>%s</para>\n'%(self.expand(i.doc_string)))
+ if len(i.since) > 0:
+ self.out.write(' <para role="since">Since %s</para>\n'%(i.since))
self.out.write('</refsect1>\n'%())
if len(i.methods) > 0:
diff --git a/gio/gdbus-codegen/dbustypes.py b/gio/gdbus-codegen/dbustypes.py
index a0cecbb..5241371 100644
--- a/gio/gdbus-codegen/dbustypes.py
+++ b/gio/gdbus-codegen/dbustypes.py
@@ -14,10 +14,13 @@ class Arg:
self.signature = signature
self.annotations = []
self.doc_string = ''
+ self.since = ''
def post_process(self, interface_prefix, c_namespace, arg_number):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
+ if len(self.since) == 0:
+ self.since = utils.lookup_since(self.annotations)
if self.name == None:
self.name = 'unnamed_arg%d'%arg_number
@@ -158,10 +161,13 @@ class Method:
self.out_args = []
self.annotations = []
self.doc_string = ''
+ self.since = ''
def post_process(self, interface_prefix, c_namespace):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
+ if len(self.since) == 0:
+ self.since = utils.lookup_since(self.annotations)
name = self.name
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.Name')
@@ -186,10 +192,13 @@ class Signal:
self.args = []
self.annotations = []
self.doc_string = ''
+ self.since = ''
def post_process(self, interface_prefix, c_namespace):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
+ if len(self.since) == 0:
+ self.since = utils.lookup_since(self.annotations)
name = self.name
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.Name')
@@ -224,10 +233,13 @@ class Property:
else:
raise RuntimeError('Invalid access type %s'%self.access)
self.doc_string = ''
+ self.since = ''
def post_process(self, interface_prefix, c_namespace):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
+ if len(self.since) == 0:
+ self.since = utils.lookup_since(self.annotations)
name = self.name
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.Name')
@@ -250,12 +262,15 @@ class Interface:
self.annotations = []
self.doc_string = ''
self.doc_string_brief = ''
+ self.since = ''
def post_process(self, interface_prefix, c_namespace):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
if len(self.doc_string_brief) == 0:
self.doc_string_brief = utils.lookup_brief_docs(self.annotations)
+ if len(self.since) == 0:
+ self.since = utils.lookup_since(self.annotations)
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.Name')
if overridden_name:
diff --git a/gio/gdbus-codegen/parser.py b/gio/gdbus-codegen/parser.py
index e5d93bc..a566c5c 100644
--- a/gio/gdbus-codegen/parser.py
+++ b/gio/gdbus-codegen/parser.py
@@ -133,6 +133,8 @@ class DBusXMLParser:
if self.doc_comment_params.has_key('short_description'):
short_description = self.doc_comment_params['short_description']
self._cur_object.doc_string_brief = short_description
+ if self.doc_comment_params.has_key('since'):
+ self._cur_object.since = self.doc_comment_params['since']
elif self.state == DBusXMLParser.STATE_INTERFACE:
if name == DBusXMLParser.STATE_METHOD:
@@ -161,6 +163,8 @@ class DBusXMLParser:
# assign docs, if any
if attrs.has_key('name') and self.doc_comment_last_symbol == attrs['name']:
self._cur_object.doc_string = self.doc_comment_body
+ if self.doc_comment_params.has_key('since'):
+ self._cur_object.since = self.doc_comment_params['since']
elif self.state == DBusXMLParser.STATE_METHOD:
if name == DBusXMLParser.STATE_ARG:
@@ -191,6 +195,8 @@ class DBusXMLParser:
doc_string = self.doc_comment_params[attrs['name']]
if doc_string != None:
self._cur_object.doc_string = doc_string
+ if self.doc_comment_params.has_key('since'):
+ self._cur_object.since = self.doc_comment_params['since']
elif self.state == DBusXMLParser.STATE_SIGNAL:
if name == DBusXMLParser.STATE_ARG:
@@ -215,6 +221,8 @@ class DBusXMLParser:
doc_string = self.doc_comment_params[attrs['name']]
if doc_string != None:
self._cur_object.doc_string = doc_string
+ if self.doc_comment_params.has_key('since'):
+ self._cur_object.since = self.doc_comment_params['since']
elif self.state == DBusXMLParser.STATE_PROPERTY:
if name == DBusXMLParser.STATE_ANNOTATION:
diff --git a/gio/gdbus-codegen/utils.py b/gio/gdbus-codegen/utils.py
index 4966077..0cb8d61 100644
--- a/gio/gdbus-codegen/utils.py
+++ b/gio/gdbus-codegen/utils.py
@@ -48,6 +48,13 @@ def lookup_docs(annotations):
else:
return s
+def lookup_since(annotations):
+ s = lookup_annotation(annotations, 'org.gtk.GDBus.Since')
+ if s == None:
+ return ''
+ else:
+ return s
+
def lookup_brief_docs(annotations):
s = lookup_annotation(annotations, 'org.gtk.GDBus.DocString.Short')
if s == None:
diff --git a/gio/tests/gdbus-example-objectmanager.xml b/gio/tests/gdbus-example-objectmanager.xml
index 1ce7a11..1036b7a 100644
--- a/gio/tests/gdbus-example-objectmanager.xml
+++ b/gio/tests/gdbus-example-objectmanager.xml
@@ -1,11 +1,13 @@
<node>
<!-- org.gtk.GDBus.Example.ObjectManager.Animal:
@short_description: Example docs generated by gdbus-codegen
+ @since: 2.30
This D-Bus interface is used to describe a simple animal.
-->
<interface name="org.gtk.GDBus.Example.ObjectManager.Animal">
<!-- Mood: The mood of the animal.
+ @since: 2.30
Known values for this property include
<literal>Happy</literal> and <literal>Sad</literal>. Use the
@@ -23,6 +25,7 @@
Poke:
@make_sad: Whether to make the animal sad.
@make_happy: Whether to make the animal happy.
+ @since: 2.30
Method used to changing the mood of the animal. See also the
#org.gtk.GDBus.Example.ObjectManager.Animal:Mood property.
@@ -35,6 +38,7 @@
<!--
Jumped:
@height: Height, in meters, that the animal jumped.
+ @since: 2.30
Emitted when the animal decides to jump.
-->
diff --git a/gio/tests/gdbus-test-codegen.c b/gio/tests/gdbus-test-codegen.c
index b70bd42..ea85a4b 100644
--- a/gio/tests/gdbus-test-codegen.c
+++ b/gio/tests/gdbus-test-codegen.c
@@ -2144,6 +2144,27 @@ gpointer name_forcing_4 = foo_rocket123_get_speed_xyz;
/* ---------------------------------------------------------------------------------------------------- */
+/* See https://bugzilla.gnome.org/show_bug.cgi?id=647577#c5 for details */
+
+#define CHECK_FIELD(name, v1, v2) g_assert_cmpint (G_STRUCT_OFFSET (FooChangingInterface##v1##Iface, name), ==, G_STRUCT_OFFSET (FooChangingInterface##v2##Iface, name));
+
+static void
+test_interface_stability (void)
+{
+ CHECK_FIELD(handle_foo_method, V1, V2);
+ CHECK_FIELD(handle_bar_method, V1, V2);
+ CHECK_FIELD(handle_baz_method, V1, V2);
+ CHECK_FIELD(foo_signal, V1, V2);
+ CHECK_FIELD(bar_signal, V1, V2);
+ CHECK_FIELD(baz_signal, V1, V2);
+ CHECK_FIELD(handle_new_method_in2, V2, V10);
+ CHECK_FIELD(new_signal_in2, V2, V10);
+}
+
+#undef CHECK_FIELD
+
+/* ---------------------------------------------------------------------------------------------------- */
+
int
main (int argc,
char *argv[])
@@ -2167,6 +2188,7 @@ main (int argc,
usleep (500 * 1000);
g_test_add_func ("/gdbus/codegen/annotations", test_annotations);
+ g_test_add_func ("/gdbus/codegen/interface_stability", test_interface_stability);
g_test_add_func ("/gdbus/codegen/object-manager", test_object_manager);
ret = g_test_run();
diff --git a/gio/tests/test-codegen.xml b/gio/tests/test-codegen.xml
index 328d888..236d5fd 100644
--- a/gio/tests/test-codegen.xml
+++ b/gio/tests/test-codegen.xml
@@ -338,4 +338,64 @@
<property name="FancyProperty" type="s" access="read"/>
</interface>
+ <interface name="ChangingInterfaceV1">
+ <method name="FooMethod"/>
+ <method name="BarMethod"/>
+ <method name="BazMethod"/>
+ <signal name="FooSignal"/>
+ <signal name="BarSignal"/>
+ <signal name="BazSignal"/>
+ </interface>
+
+ <interface name="ChangingInterfaceV2">
+ <!--
+ NewSignalIn2:
+ @since: 2.0
+ -->
+ <signal name="NewSignalIn2"/>
+ <!--
+ NewMethodIn2:
+ @since: 2.0
+ -->
+ <method name="NewMethodIn2"/>
+
+ <!-- reverse order -->
+ <signal name="BazSignal"/>
+ <signal name="BarSignal"/>
+ <signal name="FooSignal"/>
+ <method name="BazMethod"/>
+ <method name="BarMethod"/>
+ <method name="FooMethod"/>
+ </interface>
+
+ <interface name="ChangingInterfaceV10">
+ <!--
+ AddedSignalIn10:
+ @since: 10.0
+ -->
+ <signal name="AddedSignalIn10"/>
+ <method name="AddedMethodIn10">
+ <annotation name="org.gtk.GDBus.Since" value="10.0"/>
+ </method>
+
+ <!--
+ NewSignalIn2:
+ @since: 2.0
+ -->
+ <signal name="NewSignalIn2"/>
+ <!--
+ NewMethodIn2:
+ @since: 2.0
+ -->
+ <method name="NewMethodIn2"/>
+
+ <!-- reverse order -->
+ <signal name="BazSignal"/>
+ <signal name="BarSignal"/>
+ <signal name="FooSignal"/>
+ <method name="BazMethod"/>
+ <method name="BarMethod"/>
+ <method name="FooMethod"/>
+ </interface>
+
</node>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]