diff options
author | David Zeuthen <davidz@redhat.com> | 2011-03-31 12:07:41 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2011-03-31 12:07:41 -0400 |
commit | 579547ce5fd3acabb6cae81feb76154e1184167d (patch) | |
tree | 9076a2e9b627c358ffa60849aeb42f26fad4ab51 /src | |
parent | 064d552013646c2c772517f5bd04c2c90b810349 (diff) |
Add a --generate-docbook option
Signed-off-by: David Zeuthen <davidz@redhat.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 9 | ||||
-rw-r--r-- | src/codegen.py | 153 | ||||
-rw-r--r-- | src/codegen_docbook.py | 242 | ||||
-rw-r--r-- | src/codegen_main.py | 171 | ||||
-rw-r--r-- | src/dbustypes.py | 49 | ||||
-rw-r--r-- | src/org.project.xml | 121 | ||||
-rw-r--r-- | src/parser.py | 99 | ||||
-rw-r--r-- | src/utils.py | 14 |
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 <emphasis>documentation</emphasis> 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="<para>Property docs, yah...</para><para>Second paragraph.</para>"/> + </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 |