Bug 554854: The --typelib-xml and --inject options should reuse giscanner
authorLucas Rocha <lucasr@gnome.org>
Sat, 11 Oct 2008 18:33:43 +0000 (18:33 +0000)
committerLucas Almeida Rocha <lucasr@src.gnome.org>
Sat, 11 Oct 2008 18:33:43 +0000 (18:33 +0000)
2008-10-11  Lucas Rocha  <lucasr@gnome.org>

Bug 554854: The --typelib-xml and --inject options should reuse
giscanner parser/writer.

* giscanner/ast.py: add constructor list to Struct and Union.
Add new param in Return's contructor to define transfer.
* giscanner/girparser.py: several additions to the parser in order
to have parsing all nodes of the gir xml files.
* tools/g-ir-scanner (typelib_xml_strip, inject): use gir parser
and writer in --inject and --typelib-xml options.
* giscanner/xmlwriter.py: ignore empty attributes instead of
raising an error as this basically exposes a bug in GIRParser.
This should be reverted as soon as the parser is fixed.

svn path=/trunk/; revision=665

ChangeLog
giscanner/ast.py
giscanner/girparser.py
giscanner/girwriter.py
giscanner/transformer.py
giscanner/xmlwriter.py
tools/g-ir-scanner

index fbae0bdfe8dc034345d939d6e0611c8f3ea5489e..d1804587fe76bb2c541dd02f2c4a1c8003d7bd01 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2008-10-11  Lucas Rocha  <lucasr@gnome.org>
+
+       Bug 554854: The --typelib-xml and --inject options should reuse
+       giscanner parser/writer.
+
+       * giscanner/ast.py: add constructor list to Struct and Union.
+       Add new param in Return's contructor to define transfer. 
+       * giscanner/girparser.py: several additions to the parser in order
+       to have parsing all nodes of the gir xml files.
+       * tools/g-ir-scanner (typelib_xml_strip, inject): use gir parser
+       and writer in --inject and --typelib-xml options.
+       * giscanner/xmlwriter.py: ignore empty attributes instead of
+       raising an error as this basically exposes a bug in GIRParser.
+       This should be reverted as soon as the parser is fixed.
+
 2008-10-11  Lucas Rocha  <lucasr@gnome.org>
 
        Bug 552376: scanner generates wrong names for enum members when
index 368f02e005b4be54339cf0f222743d42971efbad..0c82623a0862c6955f60bb0526289395c65298af 100644 (file)
@@ -260,6 +260,7 @@ class Struct(Node):
     def __init__(self, name, symbol):
         Node.__init__(self, name)
         self.fields = []
+        self.constructors = []
         self.symbol = symbol
 
 
@@ -276,11 +277,11 @@ class Field(Node):
 
 class Return(Node):
 
-    def __init__(self, rtype):
+    def __init__(self, rtype, transfer=False):
         Node.__init__(self)
         self.type = rtype
         self.transfer = isinstance(rtype, (List, Map, Array)) or \
-            rtype.name in ('utf8', 'filename')
+            rtype.name in ('utf8', 'filename') or transfer
 
     def __repr__(self):
         return 'Return(%r)' % (self.type, )
@@ -369,6 +370,7 @@ class Union(Node):
     def __init__(self, name, symbol):
         Node.__init__(self, name)
         self.fields = []
+        self.constructors = []
         self.symbol = symbol
 
     def __repr__(self):
index 91416f0e047d8c9833d8858d9d279710e9a04fd4..0d318c36f0e3ff93ecf6984c446d21b69bb6799f 100644 (file)
@@ -20,8 +20,8 @@
 
 from xml.etree.cElementTree import parse
 
-from .ast import (Alias, Callback, Function, Parameter, Return, Union,
-                  Struct, Type, Array)
+from .ast import (Alias, Callback, Function, Field, Parameter, Property,
+                  Return, Union, Struct, Type, Array, Namespace, Varargs)
 from .glibast import (GLibEnum, GLibEnumMember, GLibFlags,
                       GLibInterface, GLibObject, GLibBoxedStruct,
                       GLibBoxedUnion, GLibBoxedOther)
@@ -45,29 +45,40 @@ def _cns(tag):
 
 class GIRParser(object):
 
-    def __init__(self, filename):
-        self._nodes = []
+    def __init__(self, filename, initial_parse=True):
         self._includes = set()
-        self._namespace_name = None
+        self._namespace = None
+        self._shared_libraries = []
+        self._tree = parse(filename)
 
-        tree = parse(filename)
-        self._parse_api(tree.getroot())
+        if (initial_parse):
+            self.parse()
 
     # Public API
 
-    def get_namespace_name(self):
-        return self._namespace_name
+    def parse(self):
+        self._includes.clear()
+        del self._namespace
+        del self._shared_libraries[:]
+
+        self._parse_api(self._tree.getroot())
+
+    def get_namespace(self):
+        return self._namespace
+
+    def get_shared_libraries(self):
+        return self._shared_libraries
 
     def get_includes(self):
         return self._includes
 
-    def get_nodes(self):
-        return self._nodes
+    def get_doc(self):
+        return self._tree
 
     # Private
 
     def _add_node(self, node):
-        self._nodes.append(node)
+        self._namespace.nodes.append(node)
 
     def _parse_api(self, root):
         assert root.tag == _corens('repository')
@@ -76,7 +87,8 @@ class GIRParser(object):
                 self._includes.add(node.attrib['name'])
         ns = root.find(_corens('namespace'))
         assert ns is not None
-        self._namespace_name = ns.attrib['name']
+        self._namespace = Namespace(ns.attrib['name'])
+        self._shared_libraries.extend(ns.attrib['shared-library'].split(','))
         for child in ns.getchildren():
             self._parse_node(child)
 
@@ -118,12 +130,20 @@ class GIRParser(object):
                     node.attrib[_glibns('type-name')],
                     node.attrib[_glibns('get-type')],
                     node.attrib.get(_cns('type')))
+        for iface in node.findall(_corens('implements')):
+            obj.interfaces.append(iface.attrib['name'])
         for method in node.findall(_corens('method')):
             obj.methods.append(self._parse_function(method, Function))
         for ctor in node.findall(_corens('constructor')):
             obj.constructors.append(self._parse_function(ctor, Function))
         for callback in node.findall(_corens('callback')):
             obj.fields.append(self._parse_function(callback, Callback))
+        for field in node.findall(_corens('field')):
+            obj.fields.append(self._parse_field(field))
+        for property in node.findall(_corens('property')):
+            obj.properties.append(self._parse_property(property))
+        for signal in node.findall(_glibns('signal')):
+            obj.signals.append(self._parse_function(signal, Function))
         self._add_node(obj)
 
     def _parse_function(self, node, klass):
@@ -131,11 +151,17 @@ class GIRParser(object):
         returnnode = node.find(_corens('return-value'))
         if not returnnode:
             raise ValueError('node %r has no return-value' % (name, ))
-        retval = Return(self._parse_type(returnnode))
+        transfer = False
+        transfer_param = returnnode.attrib.get('transfer-ownership')
+        if transfer_param is not None:
+            transfer = (transfer_param == '1')
+        retval = Return(self._parse_type(returnnode), transfer)
+        parameters_node = node.find(_corens('parameters'))
         parameters = []
-        for paramnode in node.findall('parameter'):
-            parameters.append(Parameter(paramnode.attrib['name'],
-                                        self._parse_type(paramnode)))
+        if (parameters_node is not None):
+            for paramnode in parameters_node.findall(_corens('parameter')):
+                parameters.append(Parameter(paramnode.attrib.get('name'),
+                                  self._parse_type(paramnode)))
         if klass is Callback:
             return klass(name, retval, parameters,
                          node.attrib.get(_cns('type')))
@@ -151,7 +177,15 @@ class GIRParser(object):
                                      node.attrib.get(_cns('type')))
         else:
             struct = Struct(node.attrib['name'],
-                            node.attrib[_cns('type')])
+                            node.attrib.get(_cns('type')))
+        for field in node.findall(_corens('field')):
+            struct.fields.append(self._parse_field(field))
+        for callback in node.findall(_corens('callback')):
+            struct.fields.append(self._parse_function(callback, Callback))
+        for method in node.findall(_corens('method')):
+            struct.fields.append(self._parse_function(method, Function))
+        for ctor in node.findall(_corens('constructor')):
+            struct.constructors.append(self._parse_function(ctor, Function))
         self._add_node(struct)
 
     def _parse_union(self, node):
@@ -162,7 +196,15 @@ class GIRParser(object):
                                     node.attrib.get(_cns('type')))
         else:
             struct = Union(node.attrib['name'],
-                           node.attrib[_cns('type')])
+                           node.attrib.get(_cns('type')))
+        for callback in node.findall(_corens('callback')):
+            struct.fields.append(self._parse_function(callback, Callback))
+        for field in node.findall(_corens('field')):
+            struct.fields.append(self._parse_field(field))
+        for method in node.findall(_corens('method')):
+            struct.fields.append(self._parse_function(method, Function))
+        for ctor in node.findall(_corens('constructor')):
+            struct.constructors.append(self._parse_function(ctor, Function))
         self._add_node(struct)
 
     def _parse_type(self, node):
@@ -172,8 +214,11 @@ class GIRParser(object):
                         typenode.attrib.get(_cns('type')))
         typenode = node.find(_corens('array'))
         if typenode is not None:
-            return Array(typenode.attrib[_cns('type')],
+            return Array(typenode.attrib.get(_cns('type')),
                          self._parse_type(typenode))
+        typenode = node.find(_corens('varargs'))
+        if typenode is not None:
+            return Varargs()
         raise ValueError("Couldn't parse type of node %r; children=%r",
                          node, list(node))
 
@@ -189,10 +234,26 @@ class GIRParser(object):
             obj.fields.append(self._parse_function(callback, Callback))
         self._add_node(obj)
 
+    def _parse_field(self, node):
+        type_node = self._parse_type(node)
+        return Field(node.attrib['name'],
+                     type_node,
+                     type_node.ctype)
+
+    def _parse_property(self, node):
+        type_node = self._parse_type(node)
+        return Property(node.attrib['name'],
+                        type_node.name,
+                        node.attrib.get('readable') != '0',
+                        node.attrib.get('writable') == '1',
+                        node.attrib.get('construct') == '1',
+                        node.attrib.get('construct-only') == '1',
+                        type_node.ctype)
+
     def _parse_member(self, node):
         return GLibEnumMember(node.attrib['name'],
                               node.attrib['value'],
-                              node.attrib[_cns('identifier')],
+                              node.attrib.get(_cns('identifier')),
                               node.attrib.get(_glibns('nick')))
 
     def _parse_enumeration_bitfield(self, node):
index ed4e596a9459532effc9ac96623036a359f132b5..43678581943525f379190111ccae2715e9d9d19f 100644 (file)
@@ -189,16 +189,18 @@ class GIRWriter(XMLWriter):
         self.write_tag('type', attrs)
 
     def _write_enum(self, enum):
-        attrs = [('name', enum.name),
-                 ('c:type', enum.symbol)]
+        attrs = [('name', enum.name)]
         self._append_deprecated(enum, attrs)
-        tag_name = 'enumeration'
-        if isinstance(enum, GLibEnum):
+        if isinstance(enum, GLibFlags):
+            tag_name = 'bitfield'
+        else:
+            tag_name = 'enumeration'
+        if isinstance(enum, GLibEnum) or isinstance(enum, GLibFlags):
             attrs.extend([('glib:type-name', enum.type_name),
-                          ('glib:get-type', enum.get_type)])
-            if isinstance(enum, GLibFlags):
-                tag_name = 'bitfield'
-
+                          ('glib:get-type', enum.get_type),
+                          ('c:type', enum.ctype)])
+        else:
+            attrs.append(('c:type', enum.symbol))
         with self.tagcontext(tag_name, attrs):
             for member in enum.members:
                 self._write_member(member)
@@ -305,8 +307,11 @@ class GIRWriter(XMLWriter):
                 self._write_boxed_ctors_methods(union)
 
     def _write_field(self, field):
-        # FIXME: Just function
-        if isinstance(field, (Callback, Function)):
+        if isinstance(field, Function):
+            self._write_method(field)
+            return
+
+        if isinstance(field, Callback):
             self._write_callback(field)
             return
 
index df62bf3ba372c2866d572d42b104dcb35c9ceacc..4c1cf17b8d29e7ab46dfc2bcd7c6e61eea8571d5 100644 (file)
@@ -126,8 +126,8 @@ class Transformer(object):
             raise NotImplementedError(filename)
         for include in parser.get_includes():
             self.register_include(include)
-        nsname = parser.get_namespace_name()
-        for node in parser.get_nodes():
+        nsname = parser.get_namespace().name
+        for node in parser.get_namespace().nodes:
             if isinstance(node, Alias):
                 self._names.aliases[node.name] = (nsname, node)
             elif isinstance(node, (GLibBoxed, Interface, Class)):
index eb17c6198ca7ac53ddd1a726e247b51d6349e4e4..5176499dabfbeabc075e1185ab7ec4b840296fac 100644 (file)
@@ -40,9 +40,12 @@ class XMLWriter(object):
             return -1
         attr_length = 0
         for attr, value in attributes:
+            # FIXME: actually, if we have attributes with None as value this
+            # should be considered a bug and raise an error. We are just
+            # ignoring them here while we fix GIRParser to create the right
+            # ast with the correct attributes.
             if value is None:
-                raise ValueError(
-                    "value for attribute %r cannot be None" % (attr, ))
+                continue
             attr_length += 2 + len(attr) + len(quoteattr(value))
         return attr_length + indent + self._indent
 
@@ -56,11 +59,14 @@ class XMLWriter(object):
         first = True
         attr_value = ''
         for attr, value in attributes:
+            # FIXME: actually, if we have attributes with None as value this
+            # should be considered a bug and raise an error. We are just
+            # ignoring them here while we fix GIRParser to create the right
+            # ast with the correct attributes.
+            if value is None:
+                continue
             if indent_len and not first:
                 attr_value += '\n%s' % (self._indent_char * indent_len)
-            if value is None:
-                raise ValueError(
-                    "value for attribute %r cannot be None" % (attr, ))
             attr_value += ' %s=%s' % (attr, quoteattr(value))
             if first:
                 first = False
index 4eb5a6f9b4bce56edb59a5120548f4ec015359aa..88800d700136895b64095875860241ebaf5e777d 100755 (executable)
@@ -104,22 +104,31 @@ def _error(msg):
     raise SystemExit('ERROR: %s' % (msg, ))
 
 def typelib_xml_strip(path):
+    from giscanner.girparser import GIRParser
+    from giscanner.girwriter import GIRWriter
     from giscanner.girparser import C_NS
     c_ns_key = '{%s}' % (C_NS, )
 
-    from xml.etree.cElementTree import parse
-    doc = parse(open(path))
+    parser = GIRParser(path, initial_parse=False)
+    doc = parser.get_doc()
     for node in doc.getiterator():
         for attrib in list(node.attrib):
             if attrib.startswith(c_ns_key):
                 del node.attrib[attrib]
-    doc.write(sys.stdout)
+    parser.parse()
+    writer = GIRWriter(parser.get_namespace(),
+                       parser.get_shared_libraries(),
+                       parser.get_includes())
+    sys.stdout.write(writer.get_xml())
     return 0
 
 def inject(path, additions, outpath):
+    from giscanner.girparser import GIRParser
+    from giscanner.girwriter import GIRWriter
     from xml.etree.cElementTree import parse
-    doc = parse(open(path))
-    root = doc.getroot()
+
+    parser = GIRParser(path, initial_parse=False)
+    root = parser.get_doc().getroot()
     injectDoc = parse(open(additions))
     for node in injectDoc.getroot():
         injectPath = node.attrib['path']
@@ -128,8 +137,12 @@ def inject(path, additions, outpath):
             raise ValueError("Couldn't find path %r" % (injectPath, ))
         for child in node:
             target.append(child)
+    parser.parse()
+    writer = GIRWriter(parser.get_namespace(),
+                       parser.get_shared_libraries(),
+                       parser.get_includes())
     outf = open(outpath, 'w')
-    doc.write(outf)
+    outf.write(writer.get_xml())
     outf.close()
     return 0