michael@0: #!/usr/bin/env python michael@0: # typelib.py - Generate XPCOM typelib files from IDL. michael@0: # michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: """Generate an XPIDL typelib for the IDL files specified on the command line""" michael@0: michael@0: import os michael@0: import sys michael@0: import xpidl, xpt michael@0: michael@0: # A map of xpidl.py types to xpt.py types michael@0: TypeMap = { michael@0: # nsresult is not strictly an xpidl.py type, but it's useful here michael@0: 'nsresult': xpt.Type.Tags.uint32, michael@0: # builtins michael@0: 'boolean': xpt.Type.Tags.boolean, michael@0: 'void': xpt.Type.Tags.void, michael@0: 'int16_t': xpt.Type.Tags.int16, michael@0: 'int32_t': xpt.Type.Tags.int32, michael@0: 'int64_t': xpt.Type.Tags.int64, michael@0: 'uint8_t': xpt.Type.Tags.uint8, michael@0: 'uint16_t': xpt.Type.Tags.uint16, michael@0: 'uint32_t': xpt.Type.Tags.uint32, michael@0: 'uint64_t': xpt.Type.Tags.uint64, michael@0: 'octet': xpt.Type.Tags.uint8, michael@0: 'short': xpt.Type.Tags.int16, michael@0: 'long': xpt.Type.Tags.int32, michael@0: 'long long': xpt.Type.Tags.int64, michael@0: 'unsigned short': xpt.Type.Tags.uint16, michael@0: 'unsigned long': xpt.Type.Tags.uint32, michael@0: 'unsigned long long': xpt.Type.Tags.uint64, michael@0: 'float': xpt.Type.Tags.float, michael@0: 'double': xpt.Type.Tags.double, michael@0: 'char': xpt.Type.Tags.char, michael@0: 'string': xpt.Type.Tags.char_ptr, michael@0: 'wchar': xpt.Type.Tags.wchar_t, michael@0: 'wstring': xpt.Type.Tags.wchar_t_ptr, michael@0: # special types michael@0: 'nsid': xpt.Type.Tags.nsIID, michael@0: 'domstring': xpt.Type.Tags.DOMString, michael@0: 'astring': xpt.Type.Tags.AString, michael@0: 'utf8string': xpt.Type.Tags.UTF8String, michael@0: 'cstring': xpt.Type.Tags.CString, michael@0: 'jsval': xpt.Type.Tags.jsval michael@0: } michael@0: michael@0: # XXXkhuey dipper types should go away (bug 677784) michael@0: def isDipperType(type): michael@0: return type == xpt.Type.Tags.DOMString or type == xpt.Type.Tags.AString or type == xpt.Type.Tags.CString or type == xpt.Type.Tags.UTF8String michael@0: michael@0: def build_interface(iface, ifaces): michael@0: def get_type(type, calltype, iid_is=None, size_is=None): michael@0: """ Return the appropriate xpt.Type object for this param """ michael@0: michael@0: while isinstance(type, xpidl.Typedef): michael@0: type = type.realtype michael@0: michael@0: if isinstance(type, xpidl.Builtin): michael@0: if type.name == 'string' and size_is != None: michael@0: return xpt.StringWithSizeType(size_is, size_is) michael@0: elif type.name == 'wstring' and size_is != None: michael@0: return xpt.WideStringWithSizeType(size_is, size_is) michael@0: else: michael@0: tag = TypeMap[type.name] michael@0: isPtr = (tag == xpt.Type.Tags.char_ptr or tag == xpt.Type.Tags.wchar_t_ptr) michael@0: return xpt.SimpleType(tag, michael@0: pointer=isPtr, michael@0: reference=False) michael@0: michael@0: if isinstance(type, xpidl.Array): michael@0: # NB: For an Array we pass down the iid_is to get the type of T. michael@0: # This allows Arrays of InterfaceIs types to work. michael@0: return xpt.ArrayType(get_type(type.type, calltype, iid_is), size_is, michael@0: #XXXkhuey length_is duplicates size_is (bug 677788), michael@0: size_is) michael@0: michael@0: if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward): michael@0: xptiface = None michael@0: for i in ifaces: michael@0: if i.name == type.name: michael@0: xptiface = i michael@0: michael@0: if not xptiface: michael@0: xptiface = xpt.Interface(name=type.name) michael@0: ifaces.append(xptiface) michael@0: michael@0: return xpt.InterfaceType(xptiface) michael@0: michael@0: if isinstance(type, xpidl.Native): michael@0: if type.specialtype: michael@0: # XXXkhuey jsval is marked differently in the typelib and in the headers :-( michael@0: isPtr = (type.isPtr(calltype) or type.isRef(calltype)) and not type.specialtype == 'jsval' michael@0: isRef = type.isRef(calltype) and not type.specialtype == 'jsval' michael@0: return xpt.SimpleType(TypeMap[type.specialtype], michael@0: pointer=isPtr, michael@0: reference=isRef) michael@0: elif iid_is != None: michael@0: return xpt.InterfaceIsType(iid_is) michael@0: else: michael@0: # void ptr michael@0: return xpt.SimpleType(TypeMap['void'], michael@0: pointer=True, michael@0: reference=False) michael@0: michael@0: raise Exception("Unknown type!") michael@0: michael@0: def get_nsresult(): michael@0: return xpt.SimpleType(TypeMap['nsresult']) michael@0: michael@0: def build_nsresult_param(): michael@0: return xpt.Param(get_nsresult()) michael@0: michael@0: def get_result_type(m): michael@0: if not m.notxpcom: michael@0: return get_nsresult() michael@0: michael@0: return get_type(m.realtype, '') michael@0: michael@0: def build_result_param(m): michael@0: return xpt.Param(get_result_type(m)) michael@0: michael@0: def build_retval_param(m): michael@0: type = get_type(m.realtype, 'out') michael@0: if isDipperType(type.tag): michael@0: # NB: The retval bit needs to be set here, contrary to what the michael@0: # xpt spec says. michael@0: return xpt.Param(type, in_=True, retval=True, dipper=True) michael@0: return xpt.Param(type, in_=False, out=True, retval=True) michael@0: michael@0: def build_attr_param(a, getter=False, setter=False): michael@0: if not (getter or setter): michael@0: raise Exception("Attribute param must be for a getter or a setter!") michael@0: michael@0: type = get_type(a.realtype, getter and 'out' or 'in') michael@0: if setter: michael@0: return xpt.Param(type) michael@0: else: michael@0: if isDipperType(type.tag): michael@0: # NB: The retval bit needs to be set here, contrary to what the michael@0: # xpt spec says. michael@0: return xpt.Param(type, in_=True, retval=True, dipper=True) michael@0: return xpt.Param(type, in_=False, out=True, retval=True) michael@0: michael@0: if iface.namemap is None: michael@0: raise Exception("Interface was not resolved.") michael@0: michael@0: consts = [] michael@0: methods = [] michael@0: michael@0: def build_const(c): michael@0: consts.append(xpt.Constant(c.name, get_type(c.basetype, ''), c.getValue())) michael@0: michael@0: def build_method(m): michael@0: params = [] michael@0: michael@0: def build_param(p): michael@0: def findattr(p, attr): michael@0: if hasattr(p, attr) and getattr(p, attr): michael@0: for i, param in enumerate(m.params): michael@0: if param.name == getattr(p, attr): michael@0: return i michael@0: return None michael@0: michael@0: iid_is = findattr(p, 'iid_is') michael@0: size_is = findattr(p, 'size_is') michael@0: michael@0: in_ = p.paramtype.count("in") michael@0: out = p.paramtype.count("out") michael@0: dipper = False michael@0: type = get_type(p.realtype, p.paramtype, iid_is=iid_is, size_is=size_is) michael@0: if out and isDipperType(type.tag): michael@0: out = False michael@0: dipper = True michael@0: michael@0: return xpt.Param(type, in_, out, p.retval, p.shared, dipper, p.optional) michael@0: michael@0: for p in m.params: michael@0: params.append(build_param(p)) michael@0: michael@0: if not m.notxpcom and m.realtype.name != 'void': michael@0: params.append(build_retval_param(m)) michael@0: michael@0: methods.append(xpt.Method(m.name, build_result_param(m), params, michael@0: getter=False, setter=False, notxpcom=m.notxpcom, michael@0: constructor=False, hidden=m.noscript, michael@0: optargc=m.optional_argc, michael@0: implicit_jscontext=m.implicit_jscontext)) michael@0: michael@0: def build_attr(a): michael@0: # Write the getter michael@0: methods.append(xpt.Method(a.name, build_nsresult_param(), michael@0: [build_attr_param(a, getter=True)], michael@0: getter=True, setter=False, michael@0: constructor=False, hidden=a.noscript, michael@0: optargc=False, michael@0: implicit_jscontext=a.implicit_jscontext)) michael@0: michael@0: # And maybe the setter michael@0: if not a.readonly: michael@0: methods.append(xpt.Method(a.name, build_nsresult_param(), michael@0: [build_attr_param(a, setter=True)], michael@0: getter=False, setter=True, michael@0: constructor=False, hidden=a.noscript, michael@0: optargc=False, michael@0: implicit_jscontext=a.implicit_jscontext)) michael@0: michael@0: for member in iface.members: michael@0: if isinstance(member, xpidl.ConstMember): michael@0: build_const(member) michael@0: elif isinstance(member, xpidl.Attribute): michael@0: build_attr(member) michael@0: elif isinstance(member, xpidl.Method): michael@0: build_method(member) michael@0: elif isinstance(member, xpidl.CDATA): michael@0: pass michael@0: else: michael@0: raise Exception("Unexpected interface member: %s" % member) michael@0: michael@0: parent = None michael@0: if iface.base: michael@0: for i in ifaces: michael@0: if i.name == iface.base: michael@0: parent = i michael@0: if not parent: michael@0: parent = xpt.Interface(name=iface.base) michael@0: ifaces.append(parent) michael@0: michael@0: return xpt.Interface(iface.name, iface.attributes.uuid, methods=methods, michael@0: constants=consts, resolved=True, parent=parent, michael@0: scriptable=iface.attributes.scriptable, michael@0: function=iface.attributes.function, michael@0: builtinclass=iface.attributes.builtinclass) michael@0: michael@0: def write_typelib(idl, fd, filename): michael@0: """ Generate the typelib. """ michael@0: michael@0: # We only care about interfaces michael@0: ifaces = [] michael@0: for p in idl.productions: michael@0: if p.kind == 'interface': michael@0: ifaces.append(build_interface(p, ifaces)) michael@0: michael@0: typelib = xpt.Typelib(interfaces=ifaces) michael@0: typelib.writefd(fd) michael@0: michael@0: if __name__ == '__main__': michael@0: from optparse import OptionParser michael@0: o = OptionParser() michael@0: o.add_option('-I', action='append', dest='incdirs', default=['.'], michael@0: help="Directory to search for imported files") michael@0: o.add_option('--cachedir', dest='cachedir', default=None, michael@0: help="Directory in which to cache lex/parse tables.") michael@0: o.add_option('-o', dest='outfile', default=None, michael@0: help="Output file") michael@0: o.add_option('-d', dest='depfile', default=None, michael@0: help="Generate a make dependency file") michael@0: o.add_option('--regen', action='store_true', dest='regen', default=False, michael@0: help="Regenerate IDL Parser cache") michael@0: options, args = o.parse_args() michael@0: file = args[0] if args else None michael@0: michael@0: if options.cachedir is not None: michael@0: if not os.path.isdir(options.cachedir): michael@0: os.mkdir(options.cachedir) michael@0: sys.path.append(options.cachedir) michael@0: michael@0: if options.regen: michael@0: if options.cachedir is None: michael@0: print >>sys.stderr, "--regen requires --cachedir" michael@0: sys.exit(1) michael@0: michael@0: p = xpidl.IDLParser(outputdir=options.cachedir, regen=True) michael@0: sys.exit(0) michael@0: michael@0: if options.depfile is not None and options.outfile is None: michael@0: print >>sys.stderr, "-d requires -o" michael@0: sys.exit(1) michael@0: michael@0: if options.outfile is not None: michael@0: outfd = open(options.outfile, 'wb') michael@0: closeoutfd = True michael@0: else: michael@0: raise "typelib generation requires an output file" michael@0: michael@0: p = xpidl.IDLParser(outputdir=options.cachedir) michael@0: idl = p.parse(open(file).read(), filename=file) michael@0: idl.resolve(options.incdirs, p) michael@0: write_typelib(idl, outfd, file) michael@0: michael@0: if closeoutfd: michael@0: outfd.close() michael@0: michael@0: if options.depfile is not None: michael@0: depfd = open(options.depfile, 'w') michael@0: deps = [dep.replace('\\', '/') for dep in idl.deps] michael@0: michael@0: print >>depfd, "%s: %s" % (options.outfile, " ".join(deps)) michael@0: for dep in deps: michael@0: print >>depfd, "%s:" % dep