|
1 #!/usr/bin/env python |
|
2 # typelib.py - Generate XPCOM typelib files from IDL. |
|
3 # |
|
4 # This Source Code Form is subject to the terms of the Mozilla Public |
|
5 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
7 |
|
8 """Generate an XPIDL typelib for the IDL files specified on the command line""" |
|
9 |
|
10 import os |
|
11 import sys |
|
12 import xpidl, xpt |
|
13 |
|
14 # A map of xpidl.py types to xpt.py types |
|
15 TypeMap = { |
|
16 # nsresult is not strictly an xpidl.py type, but it's useful here |
|
17 'nsresult': xpt.Type.Tags.uint32, |
|
18 # builtins |
|
19 'boolean': xpt.Type.Tags.boolean, |
|
20 'void': xpt.Type.Tags.void, |
|
21 'int16_t': xpt.Type.Tags.int16, |
|
22 'int32_t': xpt.Type.Tags.int32, |
|
23 'int64_t': xpt.Type.Tags.int64, |
|
24 'uint8_t': xpt.Type.Tags.uint8, |
|
25 'uint16_t': xpt.Type.Tags.uint16, |
|
26 'uint32_t': xpt.Type.Tags.uint32, |
|
27 'uint64_t': xpt.Type.Tags.uint64, |
|
28 'octet': xpt.Type.Tags.uint8, |
|
29 'short': xpt.Type.Tags.int16, |
|
30 'long': xpt.Type.Tags.int32, |
|
31 'long long': xpt.Type.Tags.int64, |
|
32 'unsigned short': xpt.Type.Tags.uint16, |
|
33 'unsigned long': xpt.Type.Tags.uint32, |
|
34 'unsigned long long': xpt.Type.Tags.uint64, |
|
35 'float': xpt.Type.Tags.float, |
|
36 'double': xpt.Type.Tags.double, |
|
37 'char': xpt.Type.Tags.char, |
|
38 'string': xpt.Type.Tags.char_ptr, |
|
39 'wchar': xpt.Type.Tags.wchar_t, |
|
40 'wstring': xpt.Type.Tags.wchar_t_ptr, |
|
41 # special types |
|
42 'nsid': xpt.Type.Tags.nsIID, |
|
43 'domstring': xpt.Type.Tags.DOMString, |
|
44 'astring': xpt.Type.Tags.AString, |
|
45 'utf8string': xpt.Type.Tags.UTF8String, |
|
46 'cstring': xpt.Type.Tags.CString, |
|
47 'jsval': xpt.Type.Tags.jsval |
|
48 } |
|
49 |
|
50 # XXXkhuey dipper types should go away (bug 677784) |
|
51 def isDipperType(type): |
|
52 return type == xpt.Type.Tags.DOMString or type == xpt.Type.Tags.AString or type == xpt.Type.Tags.CString or type == xpt.Type.Tags.UTF8String |
|
53 |
|
54 def build_interface(iface, ifaces): |
|
55 def get_type(type, calltype, iid_is=None, size_is=None): |
|
56 """ Return the appropriate xpt.Type object for this param """ |
|
57 |
|
58 while isinstance(type, xpidl.Typedef): |
|
59 type = type.realtype |
|
60 |
|
61 if isinstance(type, xpidl.Builtin): |
|
62 if type.name == 'string' and size_is != None: |
|
63 return xpt.StringWithSizeType(size_is, size_is) |
|
64 elif type.name == 'wstring' and size_is != None: |
|
65 return xpt.WideStringWithSizeType(size_is, size_is) |
|
66 else: |
|
67 tag = TypeMap[type.name] |
|
68 isPtr = (tag == xpt.Type.Tags.char_ptr or tag == xpt.Type.Tags.wchar_t_ptr) |
|
69 return xpt.SimpleType(tag, |
|
70 pointer=isPtr, |
|
71 reference=False) |
|
72 |
|
73 if isinstance(type, xpidl.Array): |
|
74 # NB: For an Array<T> we pass down the iid_is to get the type of T. |
|
75 # This allows Arrays of InterfaceIs types to work. |
|
76 return xpt.ArrayType(get_type(type.type, calltype, iid_is), size_is, |
|
77 #XXXkhuey length_is duplicates size_is (bug 677788), |
|
78 size_is) |
|
79 |
|
80 if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward): |
|
81 xptiface = None |
|
82 for i in ifaces: |
|
83 if i.name == type.name: |
|
84 xptiface = i |
|
85 |
|
86 if not xptiface: |
|
87 xptiface = xpt.Interface(name=type.name) |
|
88 ifaces.append(xptiface) |
|
89 |
|
90 return xpt.InterfaceType(xptiface) |
|
91 |
|
92 if isinstance(type, xpidl.Native): |
|
93 if type.specialtype: |
|
94 # XXXkhuey jsval is marked differently in the typelib and in the headers :-( |
|
95 isPtr = (type.isPtr(calltype) or type.isRef(calltype)) and not type.specialtype == 'jsval' |
|
96 isRef = type.isRef(calltype) and not type.specialtype == 'jsval' |
|
97 return xpt.SimpleType(TypeMap[type.specialtype], |
|
98 pointer=isPtr, |
|
99 reference=isRef) |
|
100 elif iid_is != None: |
|
101 return xpt.InterfaceIsType(iid_is) |
|
102 else: |
|
103 # void ptr |
|
104 return xpt.SimpleType(TypeMap['void'], |
|
105 pointer=True, |
|
106 reference=False) |
|
107 |
|
108 raise Exception("Unknown type!") |
|
109 |
|
110 def get_nsresult(): |
|
111 return xpt.SimpleType(TypeMap['nsresult']) |
|
112 |
|
113 def build_nsresult_param(): |
|
114 return xpt.Param(get_nsresult()) |
|
115 |
|
116 def get_result_type(m): |
|
117 if not m.notxpcom: |
|
118 return get_nsresult() |
|
119 |
|
120 return get_type(m.realtype, '') |
|
121 |
|
122 def build_result_param(m): |
|
123 return xpt.Param(get_result_type(m)) |
|
124 |
|
125 def build_retval_param(m): |
|
126 type = get_type(m.realtype, 'out') |
|
127 if isDipperType(type.tag): |
|
128 # NB: The retval bit needs to be set here, contrary to what the |
|
129 # xpt spec says. |
|
130 return xpt.Param(type, in_=True, retval=True, dipper=True) |
|
131 return xpt.Param(type, in_=False, out=True, retval=True) |
|
132 |
|
133 def build_attr_param(a, getter=False, setter=False): |
|
134 if not (getter or setter): |
|
135 raise Exception("Attribute param must be for a getter or a setter!") |
|
136 |
|
137 type = get_type(a.realtype, getter and 'out' or 'in') |
|
138 if setter: |
|
139 return xpt.Param(type) |
|
140 else: |
|
141 if isDipperType(type.tag): |
|
142 # NB: The retval bit needs to be set here, contrary to what the |
|
143 # xpt spec says. |
|
144 return xpt.Param(type, in_=True, retval=True, dipper=True) |
|
145 return xpt.Param(type, in_=False, out=True, retval=True) |
|
146 |
|
147 if iface.namemap is None: |
|
148 raise Exception("Interface was not resolved.") |
|
149 |
|
150 consts = [] |
|
151 methods = [] |
|
152 |
|
153 def build_const(c): |
|
154 consts.append(xpt.Constant(c.name, get_type(c.basetype, ''), c.getValue())) |
|
155 |
|
156 def build_method(m): |
|
157 params = [] |
|
158 |
|
159 def build_param(p): |
|
160 def findattr(p, attr): |
|
161 if hasattr(p, attr) and getattr(p, attr): |
|
162 for i, param in enumerate(m.params): |
|
163 if param.name == getattr(p, attr): |
|
164 return i |
|
165 return None |
|
166 |
|
167 iid_is = findattr(p, 'iid_is') |
|
168 size_is = findattr(p, 'size_is') |
|
169 |
|
170 in_ = p.paramtype.count("in") |
|
171 out = p.paramtype.count("out") |
|
172 dipper = False |
|
173 type = get_type(p.realtype, p.paramtype, iid_is=iid_is, size_is=size_is) |
|
174 if out and isDipperType(type.tag): |
|
175 out = False |
|
176 dipper = True |
|
177 |
|
178 return xpt.Param(type, in_, out, p.retval, p.shared, dipper, p.optional) |
|
179 |
|
180 for p in m.params: |
|
181 params.append(build_param(p)) |
|
182 |
|
183 if not m.notxpcom and m.realtype.name != 'void': |
|
184 params.append(build_retval_param(m)) |
|
185 |
|
186 methods.append(xpt.Method(m.name, build_result_param(m), params, |
|
187 getter=False, setter=False, notxpcom=m.notxpcom, |
|
188 constructor=False, hidden=m.noscript, |
|
189 optargc=m.optional_argc, |
|
190 implicit_jscontext=m.implicit_jscontext)) |
|
191 |
|
192 def build_attr(a): |
|
193 # Write the getter |
|
194 methods.append(xpt.Method(a.name, build_nsresult_param(), |
|
195 [build_attr_param(a, getter=True)], |
|
196 getter=True, setter=False, |
|
197 constructor=False, hidden=a.noscript, |
|
198 optargc=False, |
|
199 implicit_jscontext=a.implicit_jscontext)) |
|
200 |
|
201 # And maybe the setter |
|
202 if not a.readonly: |
|
203 methods.append(xpt.Method(a.name, build_nsresult_param(), |
|
204 [build_attr_param(a, setter=True)], |
|
205 getter=False, setter=True, |
|
206 constructor=False, hidden=a.noscript, |
|
207 optargc=False, |
|
208 implicit_jscontext=a.implicit_jscontext)) |
|
209 |
|
210 for member in iface.members: |
|
211 if isinstance(member, xpidl.ConstMember): |
|
212 build_const(member) |
|
213 elif isinstance(member, xpidl.Attribute): |
|
214 build_attr(member) |
|
215 elif isinstance(member, xpidl.Method): |
|
216 build_method(member) |
|
217 elif isinstance(member, xpidl.CDATA): |
|
218 pass |
|
219 else: |
|
220 raise Exception("Unexpected interface member: %s" % member) |
|
221 |
|
222 parent = None |
|
223 if iface.base: |
|
224 for i in ifaces: |
|
225 if i.name == iface.base: |
|
226 parent = i |
|
227 if not parent: |
|
228 parent = xpt.Interface(name=iface.base) |
|
229 ifaces.append(parent) |
|
230 |
|
231 return xpt.Interface(iface.name, iface.attributes.uuid, methods=methods, |
|
232 constants=consts, resolved=True, parent=parent, |
|
233 scriptable=iface.attributes.scriptable, |
|
234 function=iface.attributes.function, |
|
235 builtinclass=iface.attributes.builtinclass) |
|
236 |
|
237 def write_typelib(idl, fd, filename): |
|
238 """ Generate the typelib. """ |
|
239 |
|
240 # We only care about interfaces |
|
241 ifaces = [] |
|
242 for p in idl.productions: |
|
243 if p.kind == 'interface': |
|
244 ifaces.append(build_interface(p, ifaces)) |
|
245 |
|
246 typelib = xpt.Typelib(interfaces=ifaces) |
|
247 typelib.writefd(fd) |
|
248 |
|
249 if __name__ == '__main__': |
|
250 from optparse import OptionParser |
|
251 o = OptionParser() |
|
252 o.add_option('-I', action='append', dest='incdirs', default=['.'], |
|
253 help="Directory to search for imported files") |
|
254 o.add_option('--cachedir', dest='cachedir', default=None, |
|
255 help="Directory in which to cache lex/parse tables.") |
|
256 o.add_option('-o', dest='outfile', default=None, |
|
257 help="Output file") |
|
258 o.add_option('-d', dest='depfile', default=None, |
|
259 help="Generate a make dependency file") |
|
260 o.add_option('--regen', action='store_true', dest='regen', default=False, |
|
261 help="Regenerate IDL Parser cache") |
|
262 options, args = o.parse_args() |
|
263 file = args[0] if args else None |
|
264 |
|
265 if options.cachedir is not None: |
|
266 if not os.path.isdir(options.cachedir): |
|
267 os.mkdir(options.cachedir) |
|
268 sys.path.append(options.cachedir) |
|
269 |
|
270 if options.regen: |
|
271 if options.cachedir is None: |
|
272 print >>sys.stderr, "--regen requires --cachedir" |
|
273 sys.exit(1) |
|
274 |
|
275 p = xpidl.IDLParser(outputdir=options.cachedir, regen=True) |
|
276 sys.exit(0) |
|
277 |
|
278 if options.depfile is not None and options.outfile is None: |
|
279 print >>sys.stderr, "-d requires -o" |
|
280 sys.exit(1) |
|
281 |
|
282 if options.outfile is not None: |
|
283 outfd = open(options.outfile, 'wb') |
|
284 closeoutfd = True |
|
285 else: |
|
286 raise "typelib generation requires an output file" |
|
287 |
|
288 p = xpidl.IDLParser(outputdir=options.cachedir) |
|
289 idl = p.parse(open(file).read(), filename=file) |
|
290 idl.resolve(options.incdirs, p) |
|
291 write_typelib(idl, outfd, file) |
|
292 |
|
293 if closeoutfd: |
|
294 outfd.close() |
|
295 |
|
296 if options.depfile is not None: |
|
297 depfd = open(options.depfile, 'w') |
|
298 deps = [dep.replace('\\', '/') for dep in idl.deps] |
|
299 |
|
300 print >>depfd, "%s: %s" % (options.outfile, " ".join(deps)) |
|
301 for dep in deps: |
|
302 print >>depfd, "%s:" % dep |