summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2011-03-31 12:07:41 -0400
committerDavid Zeuthen <davidz@redhat.com>2011-03-31 12:07:41 -0400
commit579547ce5fd3acabb6cae81feb76154e1184167d (patch)
tree9076a2e9b627c358ffa60849aeb42f26fad4ab51 /src
parent064d552013646c2c772517f5bd04c2c90b810349 (diff)
Add a --generate-docbook option
Signed-off-by: David Zeuthen <davidz@redhat.com>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am9
-rw-r--r--src/codegen.py153
-rw-r--r--src/codegen_docbook.py242
-rw-r--r--src/codegen_main.py171
-rw-r--r--src/dbustypes.py49
-rw-r--r--src/org.project.xml121
-rw-r--r--src/parser.py99
-rw-r--r--src/utils.py14
8 files changed, 685 insertions, 173 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 65845c8..df461f4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,6 +7,8 @@ codegendir = $(libdir)/gdbus-codegen
codegen_PYTHON = \
__init__.py \
codegen.py \
+ codegen_main.py \
+ codegen_docbook.py \
config.py \
dbustypes.py \
parser.py \
@@ -95,10 +97,11 @@ libgdbus_codegen_la_LIBADD = \
BUILT_SOURCES += test-generated.h test-generated.c
test-generated.h test-generated.c : Makefile.am org.project.xml $(codegen_PYTHON)
- $(PYTHON) ./codegen.py \
- --namespace Foo \
- --strip-prefix org.project. \
+ $(PYTHON) ./codegen_main.py \
+ --c-namespace Foo \
+ --c-strip-prefix org.project. \
--generate-c-code test-generated \
+ --generate-docbook test-generated-docs.xml \
--annotate "org.project.Bar" Key1 Value1 \
--annotate "org.project.Bar" org.gtk.GDBus.Internal Value2 \
--annotate "org.project.Bar.HelloWorld()" Key3 Value3 \
diff --git a/src/codegen.py b/src/codegen.py
index e953461..8e38f22 100644
--- a/src/codegen.py
+++ b/src/codegen.py
@@ -2143,156 +2143,3 @@ class CodeGenerator:
self.generate_stub(i)
self.generate_object_manager_client()
self.generate_outro()
-
-def find_arg(arg_list, arg_name):
- for a in arg_list:
- if a.name == arg_name:
- return a
- return None
-
-def find_method(iface, method):
- for m in iface.methods:
- if m.name == method:
- return m
- return None
-
-def find_signal(iface, signal):
- for m in iface.signals:
- if m.name == signal:
- return m
- return None
-
-def find_prop(iface, prop):
- for m in iface.properties:
- if m.name == prop:
- return m
- return None
-
-def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
- for i in iface_list:
- if i.name == iface:
- iface_obj = i
- break
-
- if iface_obj == None:
- raise RuntimeError('No interface %s'%iface)
-
- target_obj = None
-
- if method:
- method_obj = find_method(iface_obj, method)
- if method_obj == None:
- raise RuntimeError('No method %s on interface %s'%(method, iface))
- if arg:
- arg_obj = find_arg(method_obj.in_args, arg)
- if (arg_obj == None):
- arg_obj = find_arg(method_obj.out_args, arg)
- if (arg_obj == None):
- raise RuntimeError('No arg %s on method %s on interface %s'%(arg, method, iface))
- target_obj = arg_obj
- else:
- target_obj = method_obj
- elif signal:
- signal_obj = find_signal(iface_obj, signal)
- if signal_obj == None:
- raise RuntimeError('No signal %s on interface %s'%(signal, iface))
- if arg:
- arg_obj = find_arg(signal_obj.args, arg)
- if (arg_obj == None):
- raise RuntimeError('No arg %s on signal %s on interface %s'%(arg, signal, iface))
- target_obj = arg_obj
- else:
- target_obj = signal_obj
- elif prop:
- prop_obj = find_prop(iface_obj, prop)
- if prop_obj == None:
- raise RuntimeError('No property %s on interface %s'%(prop, iface))
- target_obj = prop_obj
- else:
- target_obj = iface_obj
- target_obj.annotations.insert(0, dbustypes.Annotation(key, value))
-
-
-def apply_annotations(iface_list, annotation_list):
- # apply annotations given on the command line
- for (what, key, value) in annotation_list:
- pos = what.find('::')
- if pos != -1:
- # signal
- iface = what[0:pos];
- signal = what[pos + 2:]
- pos = signal.find('[')
- if pos != -1:
- arg = signal[pos + 1:]
- signal = signal[0:pos]
- pos = arg.find(']')
- arg = arg[0:pos]
- apply_annotation(iface_list, iface, None, signal, None, arg, key, value)
- else:
- apply_annotation(iface_list, iface, None, signal, None, None, key, value)
- else:
- pos = what.find(':')
- if pos != -1:
- # property
- iface = what[0:pos];
- prop = what[pos + 1:]
- apply_annotation(iface_list, iface, None, None, prop, None, key, value)
- else:
- pos = what.find('()')
- if pos != -1:
- # method
- combined = what[0:pos]
- pos = combined.rfind('.')
- iface = combined[0:pos]
- method = combined[pos + 1:]
- pos = what.find('[')
- if pos != -1:
- arg = what[pos + 1:]
- pos = arg.find(']')
- arg = arg[0:pos]
- apply_annotation(iface_list, iface, method, None, None, arg, key, value)
- else:
- apply_annotation(iface_list, iface, method, None, None, None, key, value)
- else:
- # must be an interface
- iface = what
- apply_annotation(iface_list, iface, None, None, None, None, key, value)
-
-def codegen_main():
- arg_parser = argparse.ArgumentParser(description='GDBus Code Generator')
- arg_parser.add_argument('xml_files', metavar='FILE', type=file, nargs='+',
- help='D-Bus introspection XML file')
- arg_parser.add_argument('--namespace', nargs='?', metavar='NAMESPACE', default='',
- help='The namespace to use for generated code')
- arg_parser.add_argument('--generate-c-code', nargs='?', metavar='OUTFILE',
- help='Generate GDBus C code in OUTFILE.[ch]')
- arg_parser.add_argument('--strip-prefix', nargs='?', metavar='PREFIX', default='',
- help='String to strip from D-Bus names')
- arg_parser.add_argument('--annotate', nargs=3, action='append', metavar=('WHAT', 'KEY', 'VALUE'),
- help='Add annotation (may be used several times)')
- args = arg_parser.parse_args();
-
- all_ifaces = []
- for f in args.xml_files:
- xml_data = f.read()
- f.close()
- parsed_ifaces = parser.parse_dbus_xml(xml_data, args.strip_prefix, args.namespace)
- all_ifaces.extend(parsed_ifaces)
-
- if args.annotate != None:
- apply_annotations(all_ifaces, args.annotate)
-
- for i in parsed_ifaces:
- i.calculate_c_names(args.strip_prefix, args.namespace)
-
- c_code = args.generate_c_code
- if c_code:
- h = file(c_code + '.h', 'w')
- c = file(c_code + '.c', 'w')
- codegen = CodeGenerator(all_ifaces, args.namespace, args.strip_prefix, h, c);
- sys.exit(codegen.generate())
- else:
- raise RuntimeError('No code generator directives found.')
-
-if __name__ == "__main__":
- codegen_main()
diff --git a/src/codegen_docbook.py b/src/codegen_docbook.py
new file mode 100644
index 0000000..01a4421
--- /dev/null
+++ b/src/codegen_docbook.py
@@ -0,0 +1,242 @@
+# -*- Mode: Python -*-
+
+import sys
+import argparse
+
+import config
+import utils
+import dbustypes
+import parser
+
+# ----------------------------------------------------------------------------------------------------
+
+class DocbookCodeGenerator:
+ def __init__(self, ifaces, docbook_file):
+ self.ifaces = ifaces
+ self.out = docbook_file
+
+ def print_method_prototype(self, i, m, in_synopsis):
+ max_method_len = 0
+ if in_synopsis:
+ for _m in i.methods:
+ max_method_len = max(len(_m.name), max_method_len)
+ else:
+ max_method_len = max(len(m.name), max_method_len)
+
+ max_signature_len = 0
+ if in_synopsis:
+ for _m in i.methods:
+ for a in _m.in_args:
+ max_signature_len = max(len(a.signature), max_signature_len)
+ for a in _m.out_args:
+ max_signature_len = max(len(a.signature), max_signature_len)
+ else:
+ for a in m.in_args:
+ max_signature_len = max(len(a.signature), max_signature_len)
+ for a in m.out_args:
+ max_signature_len = max(len(a.signature), max_signature_len)
+
+ if in_synopsis:
+ self.out.write('<link linkend="gdbus-method-%s.%s">%s</link>%*s ('
+ %(i.name, m.name, m.name, max_method_len - len(m.name), ''))
+ else:
+ self.out.write('%s%*s ('
+ %(m.name, max_method_len - len(m.name), ''))
+ count = 0
+ for a in m.in_args:
+ if (count > 0):
+ self.out.write(',\n%*s'%(max_method_len + 2, ''))
+ self.out.write('IN %s%*s %s'%(a.signature, max_signature_len - len(a.signature), '', a.name))
+ count = count + 1
+ for a in m.out_args:
+ if (count > 0):
+ self.out.write(',\n%*s'%(max_method_len + 2, ''))
+ self.out.write('OUT %s%*s %s'%(a.signature, max_signature_len - len(a.signature), '', a.name))
+ count = count + 1
+ self.out.write(');\n')
+
+ def print_signal_prototype(self, i, s, in_synopsis):
+ max_signal_len = 0
+ if in_synopsis:
+ for _s in i.signals:
+ max_signal_len = max(len(_s.name), max_signal_len)
+ else:
+ max_signal_len = max(len(s.name), max_signal_len)
+
+ max_signature_len = 0
+ if in_synopsis:
+ for _s in i.signals:
+ for a in _s.args:
+ max_signature_len = max(len(a.signature), max_signature_len)
+ else:
+ for a in s.args:
+ max_signature_len = max(len(a.signature), max_signature_len)
+
+ if in_synopsis:
+ self.out.write('<link linkend="gdbus-signal-%s.%s">%s</link>%*s ('
+ %(i.name, s.name, s.name, max_signal_len - len(s.name), ''))
+ else:
+ self.out.write('%s%*s ('
+ %(s.name, max_signal_len - len(s.name), ''))
+ count = 0
+ for a in s.args:
+ if (count > 0):
+ self.out.write(',\n%*s'%(max_signal_len + 2, ''))
+ self.out.write('%s%*s %s'%(a.signature, max_signature_len - len(a.signature), '', a.name))
+ count = count + 1
+ self.out.write(');\n')
+
+ def print_property_prototype(self, i, p, in_synopsis):
+ max_property_len = 0
+ if in_synopsis:
+ for _p in i.properties:
+ max_property_len = max(len(_p.name), max_property_len)
+ else:
+ max_property_len = max(len(p.name), max_property_len)
+
+ max_signature_len = 0
+ if in_synopsis:
+ for _p in i.properties:
+ max_signature_len = max(len(_p.signature), max_signature_len)
+ else:
+ max_signature_len = max(len(p.signature), max_signature_len)
+
+ if in_synopsis:
+ self.out.write('<link linkend="gdbus-property-%s.%s">%s</link>%*s'
+ %(i.name, p.name, p.name, max_property_len - len(p.name), ''))
+ else:
+ self.out.write('%s%*s'
+ %(p.name, max_property_len - len(p.name), ''))
+ if p.readable and p.writable:
+ access = 'readwrite'
+ elif p.readable:
+ access = 'readable '
+ else:
+ access = 'writable '
+ self.out.write(' %s %s\n'%(access, p.signature))
+
+
+ def print_synopsis_methods(self, i):
+ self.out.write(' <refsynopsisdiv role="synopsis">\n'%())
+ self.out.write(' <title role="synopsis.title">Synopsis</title>\n'%())
+ self.out.write(' <synopsis>\n'%())
+ for m in i.methods:
+ self.print_method_prototype(i, m, in_synopsis=True)
+ self.out.write('</synopsis>\n'%())
+ self.out.write(' </refsynopsisdiv>\n'%())
+
+ def print_synopsis_signals(self, i):
+ self.out.write(' <refsect1 role="signal_proto">\n'%())
+ self.out.write(' <title role="signal_proto.title">Signals</title>\n'%())
+ self.out.write(' <synopsis>\n'%())
+ for s in i.signals:
+ self.print_signal_prototype(i, s, in_synopsis=True)
+ self.out.write('</synopsis>\n'%())
+ self.out.write(' </refsect1>\n'%())
+
+ def print_synopsis_properties(self, i):
+ self.out.write(' <refsect1 role="properties">\n'%())
+ self.out.write(' <title role="properties.title">Properties</title>\n'%())
+ self.out.write(' <synopsis>\n'%())
+ for p in i.properties:
+ self.print_property_prototype(i, p, in_synopsis=True)
+ self.out.write('</synopsis>\n'%())
+ self.out.write(' </refsect1>\n'%())
+
+ def print_method(self, i, m):
+ self.out.write('<refsect2 role="method" id="gdbus-method-%s.%s">\n'%(i.name, m.name))
+ self.out.write(' <title>%s</title>\n'%(m.name))
+ self.out.write('<programlisting>\n')
+ self.print_method_prototype(i, m, in_synopsis=False)
+ self.out.write('</programlisting>\n')
+ self.out.write('<para>%s</para>\n'%(m.doc_string))
+ self.out.write('<variablelist role="params">\n')
+ for a in m.in_args:
+ self.out.write('<varlistentry>\n'%())
+ self.out.write(' <term><literal>IN %s <parameter>%s</parameter></literal>:</term>\n'%(a.signature, a.name))
+ self.out.write(' <listitem><para>%s</para></listitem>\n'%(a.doc_string))
+ self.out.write('</varlistentry>\n'%())
+ for a in m.out_args:
+ self.out.write('<varlistentry>\n'%())
+ self.out.write(' <term><literal>OUT %s <parameter>%s</parameter></literal>:</term>\n'%(a.signature, a.name))
+ self.out.write(' <listitem><para>%s</para></listitem>\n'%(a.doc_string))
+ self.out.write('</varlistentry>\n'%())
+ self.out.write('</variablelist>\n')
+ self.out.write('</refsect2>\n')
+
+ def print_signal(self, i, s):
+ self.out.write('<refsect2 role="signal" id="gdbus-signal-%s.%s">\n'%(i.name, s.name))
+ self.out.write(' <title>%s</title>\n'%(s.name))
+ self.out.write('<programlisting>\n')
+ self.print_signal_prototype(i, s, in_synopsis=False)
+ self.out.write('</programlisting>\n')
+ self.out.write('<para>%s</para>\n'%(s.doc_string))
+ self.out.write('<variablelist role="params">\n')
+ for a in s.args:
+ self.out.write('<varlistentry>\n'%())
+ self.out.write(' <term><literal>%s <parameter>%s</parameter></literal>:</term>\n'%(a.signature, a.name))
+ self.out.write(' <listitem><para>%s</para></listitem>\n'%(a.doc_string))
+ self.out.write('</varlistentry>\n'%())
+ self.out.write('</variablelist>\n')
+ self.out.write('</refsect2>\n')
+
+ def print_property(self, i, p):
+ self.out.write('<refsect2 role="property" id="gdbus-property-%s.%s">\n'%(i.name, p.name))
+ self.out.write(' <title>%s</title>\n'%(p.name))
+ self.out.write('<programlisting>\n')
+ self.print_property_prototype(i, p, in_synopsis=False)
+ self.out.write('</programlisting>\n')
+ self.out.write('<para>%s</para>\n'%(p.doc_string))
+ self.out.write('</refsect2>\n')
+
+ def generate(self):
+ self.out.write('<reference>\n')
+ self.out.write('<title>D-Bus interfaces</title>\n')
+ for i in self.ifaces:
+ self.out.write('<refentry id="gdbus-%s">\n'%(i.name))
+ self.out.write(' <refmeta>'%())
+ self.out.write(' <refentrytitle role="top_of_page" id="gdbus-%s.top_of_page">%s</refentrytitle>\n'%(i.name, i.name))
+ self.out.write(' </refmeta>'%())
+
+ self.out.write(' <refnamediv>'%())
+ self.out.write(' <refname>%s</refname>'%(i.name))
+ self.out.write(' <refpurpose>%s</refpurpose>'%(i.doc_string_brief))
+ self.out.write(' </refnamediv>'%())
+
+ if len(i.methods) > 0:
+ self.print_synopsis_methods(i)
+ if len(i.signals) > 0:
+ self.print_synopsis_signals(i)
+ if len(i.properties) > 0:
+ self.print_synopsis_properties(i)
+
+ self.out.write('<refsect1 role="desc" id="gdbus-interface-%s">\n'%(i.name))
+ self.out.write(' <title role="desc.title">Description</title>\n'%())
+ self.out.write(' <para>%s</para>\n'%(i.doc_string))
+ self.out.write('</refsect1>\n'%())
+
+ if len(i.methods) > 0:
+ self.out.write('<refsect1 role="details" id="gdbus-methods-%s">\n'%(i.name))
+ self.out.write(' <title role="details.title">Method Details</title>\n'%())
+ for m in i.methods:
+ self.print_method(i, m)
+ self.out.write('</refsect1>\n'%())
+
+ if len(i.signals) > 0:
+ self.out.write('<refsect1 role="details" id="gdbus-signals-%s">\n'%(i.name))
+ self.out.write(' <title role="details.title">Signal Details</title>\n'%())
+ for s in i.signals:
+ self.print_signal(i, s)
+ self.out.write('</refsect1>\n'%())
+
+ if len(i.properties) > 0:
+ self.out.write('<refsect1 role="details" id="gdbus-properties-%s">\n'%(i.name))
+ self.out.write(' <title role="details.title">Property Details</title>\n'%())
+ for s in i.properties:
+ self.print_property(i, s)
+ self.out.write('</refsect1>\n'%())
+
+ self.out.write('</refentry>\n')
+ self.out.write('\n')
+ self.out.write('</reference>\n')
+
diff --git a/src/codegen_main.py b/src/codegen_main.py
new file mode 100644
index 0000000..dada60f
--- /dev/null
+++ b/src/codegen_main.py
@@ -0,0 +1,171 @@
+# -*- Mode: Python -*-
+
+import sys
+import argparse
+
+import config
+import utils
+import dbustypes
+import parser
+import codegen
+import codegen_docbook
+
+def find_arg(arg_list, arg_name):
+ for a in arg_list:
+ if a.name == arg_name:
+ return a
+ return None
+
+def find_method(iface, method):
+ for m in iface.methods:
+ if m.name == method:
+ return m
+ return None
+
+def find_signal(iface, signal):
+ for m in iface.signals:
+ if m.name == signal:
+ return m
+ return None
+
+def find_prop(iface, prop):
+ for m in iface.properties:
+ if m.name == prop:
+ return m
+ return None
+
+def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
+ for i in iface_list:
+ if i.name == iface:
+ iface_obj = i
+ break
+
+ if iface_obj == None:
+ raise RuntimeError('No interface %s'%iface)
+
+ target_obj = None
+
+ if method:
+ method_obj = find_method(iface_obj, method)
+ if method_obj == None:
+ raise RuntimeError('No method %s on interface %s'%(method, iface))
+ if arg:
+ arg_obj = find_arg(method_obj.in_args, arg)
+ if (arg_obj == None):
+ arg_obj = find_arg(method_obj.out_args, arg)
+ if (arg_obj == None):
+ raise RuntimeError('No arg %s on method %s on interface %s'%(arg, method, iface))
+ target_obj = arg_obj
+ else:
+ target_obj = method_obj
+ elif signal:
+ signal_obj = find_signal(iface_obj, signal)
+ if signal_obj == None:
+ raise RuntimeError('No signal %s on interface %s'%(signal, iface))
+ if arg:
+ arg_obj = find_arg(signal_obj.args, arg)
+ if (arg_obj == None):
+ raise RuntimeError('No arg %s on signal %s on interface %s'%(arg, signal, iface))
+ target_obj = arg_obj
+ else:
+ target_obj = signal_obj
+ elif prop:
+ prop_obj = find_prop(iface_obj, prop)
+ if prop_obj == None:
+ raise RuntimeError('No property %s on interface %s'%(prop, iface))
+ target_obj = prop_obj
+ else:
+ target_obj = iface_obj
+ target_obj.annotations.insert(0, dbustypes.Annotation(key, value))
+
+
+def apply_annotations(iface_list, annotation_list):
+ # apply annotations given on the command line
+ for (what, key, value) in annotation_list:
+ pos = what.find('::')
+ if pos != -1:
+ # signal
+ iface = what[0:pos];
+ signal = what[pos + 2:]
+ pos = signal.find('[')
+ if pos != -1:
+ arg = signal[pos + 1:]
+ signal = signal[0:pos]
+ pos = arg.find(']')
+ arg = arg[0:pos]
+ apply_annotation(iface_list, iface, None, signal, None, arg, key, value)
+ else:
+ apply_annotation(iface_list, iface, None, signal, None, None, key, value)
+ else:
+ pos = what.find(':')
+ if pos != -1:
+ # property
+ iface = what[0:pos];
+ prop = what[pos + 1:]
+ apply_annotation(iface_list, iface, None, None, prop, None, key, value)
+ else:
+ pos = what.find('()')
+ if pos != -1:
+ # method
+ combined = what[0:pos]
+ pos = combined.rfind('.')
+ iface = combined[0:pos]
+ method = combined[pos + 1:]
+ pos = what.find('[')
+ if pos != -1:
+ arg = what[pos + 1:]
+ pos = arg.find(']')
+ arg = arg[0:pos]
+ apply_annotation(iface_list, iface, method, None, None, arg, key, value)
+ else:
+ apply_annotation(iface_list, iface, method, None, None, None, key, value)
+ else:
+ # must be an interface
+ iface = what
+ apply_annotation(iface_list, iface, None, None, None, None, key, value)
+
+def codegen_main():
+ arg_parser = argparse.ArgumentParser(description='GDBus Code Generator')
+ arg_parser.add_argument('xml_files', metavar='FILE', type=file, nargs='+',
+ help='D-Bus introspection XML file')
+ arg_parser.add_argument('--c-namespace', nargs='?', metavar='NAMESPACE', default='',
+ help='The namespace to use for generated C code')
+ arg_parser.add_argument('--c-strip-prefix', nargs='?', metavar='PREFIX', default='',
+ help='String to strip from D-Bus names when generating C code')
+ arg_parser.add_argument('--generate-c-code', nargs='?', metavar='OUTFILE',
+ help='Generate C code in OUTFILE.[ch]')
+ arg_parser.add_argument('--generate-docbook', nargs='?', metavar='OUTFILE',
+ help='Generate Docbook docs in OUTFILE')
+ arg_parser.add_argument('--annotate', nargs=3, action='append', metavar=('WHAT', 'KEY', 'VALUE'),
+ help='Add annotation (may be used several times)')
+ args = arg_parser.parse_args();
+
+ all_ifaces = []
+ for f in args.xml_files:
+ xml_data = f.read()
+ f.close()
+ parsed_ifaces = parser.parse_dbus_xml(xml_data, args.c_strip_prefix, args.c_namespace)
+ all_ifaces.extend(parsed_ifaces)
+
+ if args.annotate != None:
+ apply_annotations(all_ifaces, args.annotate)
+
+ for i in parsed_ifaces:
+ i.post_process(args.c_strip_prefix, args.c_namespace)
+
+ c_code = args.generate_c_code
+ if c_code:
+ h = file(c_code + '.h', 'w')
+ c = file(c_code + '.c', 'w')
+ gen = codegen.CodeGenerator(all_ifaces, args.c_namespace, args.c_strip_prefix, h, c);
+ ret = gen.generate()
+
+ docbook = args.generate_docbook
+ if docbook:
+ gen = codegen_docbook.DocbookCodeGenerator(all_ifaces, file(docbook, 'w'));
+ ret = gen.generate()
+
+ sys.exit(0)
+
+if __name__ == "__main__":
+ codegen_main()
diff --git a/src/dbustypes.py b/src/dbustypes.py
index e04b4a7..e523917 100644
--- a/src/dbustypes.py
+++ b/src/dbustypes.py
@@ -13,8 +13,12 @@ class Arg:
self.name = name
self.signature = signature
self.annotations = []
+ self.doc_string = ''
+
+ def post_process(self, strip_prefix, namespace, arg_number):
+ if len(self.doc_string) == 0:
+ self.doc_string = utils.lookup_docs(self.annotations)
- def calculate_c_names(self, strip_prefix, namespace, arg_number):
if self.name == None:
self.name = 'unnamed_arg%d'%arg_number
# default to GVariant
@@ -155,8 +159,12 @@ class Method:
self.in_args = []
self.out_args = []
self.annotations = []
+ self.doc_string = ''
+
+ def post_process(self, strip_prefix, namespace):
+ if len(self.doc_string) == 0:
+ self.doc_string = utils.lookup_docs(self.annotations)
- def calculate_c_names(self, strip_prefix, namespace):
name = self.name
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.Name')
if overridden_name:
@@ -167,11 +175,11 @@ class Method:
arg_count = 0
for a in self.in_args:
- a.calculate_c_names(strip_prefix, namespace, arg_count)
+ a.post_process(strip_prefix, namespace, arg_count)
arg_count += 1
for a in self.out_args:
- a.calculate_c_names(strip_prefix, namespace, arg_count)
+ a.post_process(strip_prefix, namespace, arg_count)
arg_count += 1
#print ' --- method'
#print ' name: ' + self.name
@@ -192,8 +200,12 @@ class Signal:
self.name = name
self.args = []
self.annotations = []
+ self.doc_string = ''
+
+ def post_process(self, strip_prefix, namespace):
+ if len(self.doc_string) == 0:
+ self.doc_string = utils.lookup_docs(self.annotations)
- def calculate_c_names(self, strip_prefix, namespace):
name = self.name
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.Name')
if overridden_name:
@@ -207,7 +219,7 @@ class Signal:
arg_count = 0
for a in self.args:
- a.calculate_c_names(strip_prefix, namespace, arg_count)
+ a.post_process(strip_prefix, namespace, arg_count)
arg_count += 1
class Property:
@@ -229,8 +241,12 @@ class Property:
self.writable = True
else:
raise RuntimeError('Invalid access type %s'%self.access)
+ self.doc_string = ''
+
+ def post_process(self, strip_prefix, namespace):
+ if len(self.doc_string) == 0:
+ self.doc_string = utils.lookup_docs(self.annotations)
- def calculate_c_names(self, strip_prefix, namespace):
name = self.name
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.Name')
if overridden_name:
@@ -247,17 +263,24 @@ class Property:
# recalculate arg
self.arg.annotations = self.annotations
- self.arg.calculate_c_names(strip_prefix, namespace, 0)
+ self.arg.post_process(strip_prefix, namespace, 0)
class Interface:
- def __init__(self, name): #gi, strip_prefix, namespace):
+ def __init__(self, name):
self.name = name
self.methods = []
self.signals = []
self.properties = []
self.annotations = []
+ self.doc_string = ''
+ self.doc_string_brief = ''
+
+ def post_process(self, strip_prefix, 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)
- def calculate_c_names(self, strip_prefix, namespace):
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.Name')
if overridden_name:
name = overridden_name
@@ -278,13 +301,13 @@ class Interface:
self.name_upper = utils.camel_case_to_uscore(name).upper()
for m in self.methods:
- m.calculate_c_names(strip_prefix, namespace)
+ m.post_process(strip_prefix, namespace)
for s in self.signals:
- s.calculate_c_names(strip_prefix, namespace)
+ s.post_process(strip_prefix, namespace)
for p in self.properties:
- p.calculate_c_names(strip_prefix, namespace)
+ p.post_process(strip_prefix, namespace)
#print '---'
#print 'interface: ' + self.name
diff --git a/src/org.project.xml b/src/org.project.xml
index 7d04e30..11dc0e1 100644
--- a/src/org.project.xml
+++ b/src/org.project.xml
@@ -2,11 +2,19 @@
<node>
<!-- Fully Loaded Interface -->
<interface name="org.project.Bar">
+ <annotation name="org.gtk.GDBus.DocString" value="The org.project.Bar interface is very complicated."/>
+ <annotation name="org.gtk.GDBus.DocString.Short" value="Brief interface doc string"/>
+
<method name="HelloWorld">
+ <annotation name="org.gtk.GDBus.DocString" value="Method &lt;emphasis&gt;documentation&lt;/emphasis&gt; blablablabla."/>
<annotation name="ExistingAnnotation" value="blah"/>
<annotation name="org.gtk.GDBus.ExistingInternalAnnotation" value="booh"/>
- <arg name="greeting" direction="in" type="s"/>
- <arg name="response" direction="out" type="s"/>
+ <arg name="greeting" direction="in" type="s">
+ <annotation name="org.gtk.GDBus.DocString" value="in param doc"/>
+ </arg>
+ <arg name="response" direction="out" type="s">
+ <annotation name="org.gtk.GDBus.DocString" value="out param doc"/>
+ </arg>
</method>
<method name="TestPrimitiveTypes">
@@ -58,7 +66,10 @@
<method name="UnimplementedMethod"/>
<signal name="TestSignal">
- <arg type="i" name="val_int32" />
+ <annotation name="org.gtk.GDBus.DocString" value="Signal documentation."/>
+ <arg type="i" name="val_int32">
+ <annotation name="org.gtk.GDBus.DocString" value="Signal param docs"/>
+ </arg>
<arg type="as" name="array_of_strings" />
<arg type="aay" name="array_of_bytestrings" />
<arg type="a{s(ii)}" name="dict_s_to_pairs" />
@@ -68,7 +79,9 @@
<arg type="s" name="word" />
</signal>
- <property name="y" type="y" access="readwrite"/>
+ <property name="y" type="y" access="readwrite">
+ <annotation name="org.gtk.GDBus.DocString" value="&lt;para&gt;Property docs, yah...&lt;/para&gt;&lt;para&gt;Second paragraph.&lt;/para&gt;"/>
+ </property>
<property name="b" type="b" access="readwrite"/>
<property name="n" type="n" access="readwrite"/>
<property name="q" type="q" access="readwrite"/>
@@ -221,4 +234,104 @@
</method>
</interface> <!-- End org.project.MethodThreads -->
+ <!--
+ org.project.InlineDocs:
+ @short_description: The short description
+
+ Here is the <emphasis>longer</emphasis> description.
+
+ With lots of stuff.
+ -->
+ <interface name="org.project.InlineDocs">
+
+ <!--
+ FooMethod:
+ @greeting: The docs for greeting parameter.
+ @response: The docs for response parameter.
+
+ The docs for the actual method.
+
+ Multi-paragraph.
+ -->
+ <method name="FooMethod">
+ <arg name="greeting" direction="in" type="s"/>
+ <arg name="response" direction="out" type="s"/>
+ </method>
+
+ <!-- Method2: foo -->
+ <method name="Method2">
+ <arg name="greeting" direction="in" type="s"/>
+ <arg name="response" direction="out" type="s"/>
+ </method>
+
+ <!--
+ BarSignal:
+ @blah: The docs for blah parameter.
+ @boo: The docs for boo parameter.
+
+ The docs for the actual signal.
+ -->
+ <signal name="BarSignal">
+ <!-- Non-Doc comment -->
+ <arg name="blah" type="s"/>
+ <arg name="boo" type="s"/>
+ </signal>
+
+ <!-- BazProperty: The docs for the property. -->
+ <property name="BazProperty" type="s" access="read"/>
+
+ <!-- Property2: Another property
+ This should be a new paragraph.
+ -->
+ <property name="Property2" type="s" access="read"/>
+
+ <!-- Property3:
+ First line.
+ This should NOT be a new paragraph.
+ -->
+ <property name="Property3" type="s" access="read"/>
+
+ <!-- Property4:
+ First line. With trailing whitespace.
+ Second line (same paragraph).
+ -->
+ <property name="Property4" type="s" access="read"/>
+
+ <!-- Property5: Foo
+ First line (second paragraph). With trailing whitespace.
+ Second line (same paragraph).
+<programlisting>
+1 program listing
+ 2 should include indented space
+ 3
+ 4
+ 5
+</programlisting>
+ -->
+ <property name="Property5" type="s" access="read"/>
+
+
+ <!--
+ FancyProperty:
+ Here's some fancy use of XML inside the comment.
+ <variablelist>
+ <varlistentry>
+ <term><option>namespace</option></term>
+ <listitem>
+ <para>The namespace to use for generated code. In CamelCase format.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>output-prefix</option></term>
+ <listitem>
+ <para>
+ A prefix to use for all generated files. Defaults to <filename>generated</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ -->
+ <property name="FancyProperty" type="s" access="read"/>
+ </interface>
+
</node>
diff --git a/src/parser.py b/src/parser.py
index 7b2a854..2f0fcf8 100644
--- a/src/parser.py
+++ b/src/parser.py
@@ -17,6 +17,7 @@ class DBusXMLParser:
def __init__(self, xml_data, dbus_strip_prefix, dbus_namespace):
self._parser = xml.parsers.expat.ParserCreate()
+ self._parser.CommentHandler = self.handle_comment
self._parser.CharacterDataHandler = self.handle_char_data
self._parser.StartElementHandler = self.handle_start_element
self._parser.EndElementHandler = self.handle_end_element
@@ -31,8 +32,77 @@ class DBusXMLParser:
self._cur_object = None
self._cur_object_stack = []
+ self.doc_comment_last_symbol = ''
+
self._parser.Parse(xml_data)
+ COMMENT_STATE_BEGIN = 'begin'
+ COMMENT_STATE_PARAMS = 'params'
+ COMMENT_STATE_BODY = 'body'
+ COMMENT_STATE_SKIP = 'skip'
+ def handle_comment(self, data):
+ comment_state = DBusXMLParser.COMMENT_STATE_BEGIN;
+ lines = data.split('\n')
+ symbol = ''
+ body = ''
+ in_para = False
+ params = {}
+ for line in lines:
+ orig_line = line
+ line = line.lstrip()
+ if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN:
+ if len(line) > 0:
+ colon_index = line.find(': ')
+ if colon_index == -1:
+ if line.endswith(':'):
+ symbol = line[0:len(line)-1]
+ comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
+ else:
+ comment_state = DBusXMLParser.COMMENT_STATE_SKIP
+ else:
+ symbol = line[0:colon_index]
+ rest_of_line = line[colon_index+2:].strip()
+ if len(rest_of_line) > 0:
+ body += '<para>' + rest_of_line + '</para>'
+ comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
+ elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS:
+ if line.startswith('@'):
+ colon_index = line.find(': ')
+ if colon_index == -1:
+ comment_state = DBusXMLParser.COMMENT_STATE_BODY
+ if not in_para:
+ body += '<para>'
+ in_para = True
+ body += orig_line + '\n'
+ else:
+ param = line[1:colon_index]
+ docs = line[colon_index + 2:]
+ params[param] = docs
+ else:
+ comment_state = DBusXMLParser.COMMENT_STATE_BODY
+ if len(line) > 0:
+ if not in_para:
+ body += '<para>'
+ in_para = True
+ body += orig_line + '\n'
+ elif comment_state == DBusXMLParser.COMMENT_STATE_BODY:
+ if len(line) > 0:
+ if not in_para:
+ body += '<para>'
+ in_para = True
+ body += orig_line + '\n'
+ else:
+ if in_para:
+ body += '</para>'
+ in_para = False
+ if in_para:
+ body += '</para>'
+
+ if symbol != '':
+ self.doc_comment_last_symbol = symbol
+ self.doc_comment_params = params
+ self.doc_comment_body = body
+
def handle_char_data(self, data):
#print 'char_data=%s'%data
pass
@@ -58,6 +128,14 @@ class DBusXMLParser:
self._cur_object = anno
else:
raise RuntimeError('Cannot go from state "%s" to element with name "%s"'%(self.state, name))
+
+ # assign docs, if any
+ if self.doc_comment_last_symbol == attrs['name']:
+ self._cur_object.doc_string = self.doc_comment_body
+ 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
+
elif self.state == DBusXMLParser.STATE_INTERFACE:
if name == DBusXMLParser.STATE_METHOD:
self.state = DBusXMLParser.STATE_METHOD
@@ -81,6 +159,11 @@ class DBusXMLParser:
self._cur_object = anno
else:
raise RuntimeError('Cannot go from state "%s" to element with name "%s"'%(self.state, name))
+
+ # 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
+
elif self.state == DBusXMLParser.STATE_METHOD:
if name == DBusXMLParser.STATE_ARG:
self.state = DBusXMLParser.STATE_ARG
@@ -103,6 +186,14 @@ class DBusXMLParser:
self._cur_object = anno
else:
raise RuntimeError('Cannot go from state "%s" to element with name "%s"'%(self.state, name))
+
+ # assign docs, if any
+ if self.doc_comment_last_symbol == old_cur_object.name:
+ if attrs.has_key('name') and self.doc_comment_params.has_key(attrs['name']):
+ doc_string = self.doc_comment_params[attrs['name']]
+ if doc_string != None:
+ self._cur_object.doc_string = doc_string
+
elif self.state == DBusXMLParser.STATE_SIGNAL:
if name == DBusXMLParser.STATE_ARG:
self.state = DBusXMLParser.STATE_ARG
@@ -119,6 +210,14 @@ class DBusXMLParser:
self._cur_object = anno
else:
raise RuntimeError('Cannot go from state "%s" to element with name "%s"'%(self.state, name))
+
+ # assign docs, if any
+ if self.doc_comment_last_symbol == old_cur_object.name:
+ if attrs.has_key('name') and self.doc_comment_params.has_key(attrs['name']):
+ doc_string = self.doc_comment_params[attrs['name']]
+ if doc_string != None:
+ self._cur_object.doc_string = doc_string
+
elif self.state == DBusXMLParser.STATE_PROPERTY:
if name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
diff --git a/src/utils.py b/src/utils.py
index 07edc01..d58ef7f 100644
--- a/src/utils.py
+++ b/src/utils.py
@@ -37,3 +37,17 @@ def lookup_annotation(annotations, key):
if a.key == key:
return a.value
return None
+
+def lookup_docs(annotations):
+ s = lookup_annotation(annotations, 'org.gtk.GDBus.DocString')
+ if s == None:
+ return ''
+ else:
+ return s
+
+def lookup_brief_docs(annotations):
+ s = lookup_annotation(annotations, 'org.gtk.GDBus.DocString.Short')
+ if s == None:
+ return ''
+ else:
+ return s