2
# header.py - Generate C++ header files from IDL.
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/.
8
"""Print a C++ header file for the IDL files specified on the command line"""
10
import sys, os.path, re, xpidl, itertools, glob
12
printdoccomments = False
15
def printComments(fd, clist, indent):
17
fd.write("%s%s\n" % (indent, c))
19
def printComments(fd, clist, indent):
23
return str[0].upper() + str[1:]
25
def attributeParamName(a):
26
return "a" + firstCap(a.name)
28
def attributeParamNames(a):
29
l = [attributeParamName(a)]
30
if a.implicit_jscontext:
34
def attributeNativeName(a, getter):
35
binaryname = a.binaryname is not None and a.binaryname or firstCap(a.name)
36
return "%s%s" % (getter and 'Get' or 'Set', binaryname)
38
def attributeReturnType(a, macro):
39
"""macro should be NS_IMETHOD or NS_IMETHODIMP"""
41
return macro == "NS_IMETHOD" and "virtual nsresult" or "nsresult"
45
def attributeParamlist(a, getter):
46
l = ["%s%s" % (a.realtype.nativeType(getter and 'out' or 'in'),
47
attributeParamName(a))]
48
if a.implicit_jscontext:
49
l.insert(0, "JSContext* cx")
53
def attributeAsNative(a, getter):
54
deprecated = a.deprecated and "NS_DEPRECATED " or ""
55
params = {'deprecated': deprecated,
56
'returntype': attributeReturnType(a, 'NS_IMETHOD'),
57
'binaryname': attributeNativeName(a, getter),
58
'paramlist': attributeParamlist(a, getter)}
59
return "%(deprecated)s%(returntype)s %(binaryname)s(%(paramlist)s)" % params
61
def methodNativeName(m):
62
return m.binaryname is not None and m.binaryname or firstCap(m.name)
64
def methodReturnType(m, macro):
65
"""macro should be NS_IMETHOD or NS_IMETHODIMP"""
66
if m.nostdcall and m.notxpcom:
67
return "%s%s" % (macro == "NS_IMETHOD" and "virtual " or "",
68
m.realtype.nativeType('in').strip())
70
return "%snsresult" % (macro == "NS_IMETHOD" and "virtual " or "")
72
return "%s_(%s)" % (macro, m.realtype.nativeType('in').strip())
76
def methodAsNative(m):
77
return "%s %s(%s)" % (methodReturnType(m, 'NS_IMETHOD'),
81
def paramlistAsNative(m, empty='void'):
82
l = [paramAsNative(p) for p in m.params]
84
if m.implicit_jscontext:
85
l.append("JSContext* cx")
88
l.append('uint8_t _argc')
90
if not m.notxpcom and m.realtype.name != 'void':
91
l.append(paramAsNative(xpidl.Param(paramtype='out',
96
realtype=m.realtype)))
103
def paramAsNative(p):
104
return "%s%s" % (p.nativeType(),
107
def paramlistNames(m):
108
names = [p.name for p in m.params]
110
if m.implicit_jscontext:
114
names.append('_argc')
116
if not m.notxpcom and m.realtype.name != 'void':
117
names.append('_retval')
121
return ', '.join(names)
124
* DO NOT EDIT. THIS FILE IS GENERATED FROM %(filename)s
127
#ifndef __gen_%(basename)s_h__
128
#define __gen_%(basename)s_h__
132
#ifndef __gen_%(basename)s_h__
133
#include "%(basename)s.h"
137
jspubtd_include = """
141
infallible_includes = """
142
#include "mozilla/Assertions.h"
143
#include "mozilla/Util.h"
146
header_end = """/* For IDL files that don't want to include root IDL files. */
153
#endif /* __gen_%(basename)s_h__ */
156
forward_decl = """class %(name)s; /* forward declaration */
161
"""returns the base name of a file with the last extension stripped"""
162
return os.path.basename(f).rpartition('.')[0]
164
def print_header(idl, fd, filename):
165
fd.write(header % {'filename': filename,
166
'basename': idl_basename(filename)})
169
for inc in idl.includes():
173
fd.write(include % {'basename': idl_basename(inc.filename)})
175
if idl.needsJSTypes():
176
fd.write(jspubtd_include)
178
# Include some extra files if any attributes are infallible.
179
for iface in [p for p in idl.productions if p.kind == 'interface']:
180
for attr in [m for m in iface.members if isinstance(m, xpidl.Attribute)]:
182
fd.write(infallible_includes)
188
for p in idl.productions:
189
if p.kind == 'include': continue
190
if p.kind == 'cdata':
194
if p.kind == 'forward':
195
fd.write(forward_decl % {'name': p.name})
197
if p.kind == 'interface':
198
write_interface(p, fd)
200
if p.kind == 'typedef':
201
printComments(fd, p.doccomments, '')
202
fd.write("typedef %s %s;\n\n" % (p.realtype.nativeType('in'),
205
fd.write(footer % {'basename': idl_basename(filename)})
208
/* starting interface: %(name)s */
209
#define %(defname)s_IID_STR "%(iid)s"
211
#define %(defname)s_IID \
212
{0x%(m0)s, 0x%(m1)s, 0x%(m2)s, \
217
uuid_decoder = re.compile(r"""(?P<m0>[a-f0-9]{8})-
221
(?P<m4>[a-f0-9]{12})$""", re.X)
226
NS_DECLARE_STATIC_IID_ACCESSOR(%(defname)s_IID)
232
NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID)
234
/* Use this macro when declaring classes that implement this interface. */
235
#define NS_DECL_%(macroname)s """
240
/* Use this macro to declare functions that forward the behavior of this interface to another object. */
241
#define NS_FORWARD_%(macroname)s(_to) """
243
iface_forward_safe = """
245
/* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
246
#define NS_FORWARD_SAFE_%(macroname)s(_to) """
248
iface_template_prolog = """
251
/* Use the code below as a template for the implementation class for this interface. */
254
class %(implclass)s : public %(name)s
258
NS_DECL_%(macroname)s
266
/* additional members */
269
/* Implementation file */
270
NS_IMPL_ISUPPORTS1(%(implclass)s, %(name)s)
272
%(implclass)s::%(implclass)s()
274
/* member initializers and constructor code */
277
%(implclass)s::~%(implclass)s()
279
/* destructor code */
284
example_tmpl = """%(returntype)s %(implclass)s::%(nativeName)s(%(paramList)s)
286
return NS_ERROR_NOT_IMPLEMENTED;
290
iface_template_epilog = """/* End of implementation class template. */
295
attr_infallible_tmpl = """\
296
inline %(realtype)s%(nativename)s(%(args)s)
299
mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result);
300
MOZ_ASSERT(NS_SUCCEEDED(rv));
305
def write_interface(iface, fd):
306
if iface.namemap is None:
307
raise Exception("Interface was not resolved.")
309
def write_const_decls(g):
310
fd.write(" enum {\n")
313
printComments(fd, c.doccomments, ' ')
314
basetype = c.basetype
316
enums.append(" %(name)s = %(value)s%(signed)s" % {
319
'signed': (not basetype.signed) and 'U' or ''})
320
fd.write(",\n".join(enums))
321
fd.write("\n };\n\n")
323
def write_method_decl(m):
324
printComments(fd, m.doccomments, ' ')
326
fd.write(" /* %s */\n" % m.toIDL())
327
fd.write(" %s = 0;\n\n" % methodAsNative(m))
329
def write_attr_decl(a):
330
printComments(fd, a.doccomments, ' ')
332
fd.write(" /* %s */\n" % a.toIDL());
334
fd.write(" %s = 0;\n" % attributeAsNative(a, True))
336
fd.write(attr_infallible_tmpl %
337
{'realtype': a.realtype.nativeType('in'),
338
'nativename': attributeNativeName(a, getter=True),
339
'args': '' if not a.implicit_jscontext else 'JSContext* cx',
340
'argnames': '' if not a.implicit_jscontext else 'cx, '})
343
fd.write(" %s = 0;\n" % attributeAsNative(a, False))
346
defname = iface.name.upper()
347
if iface.name[0:2] == 'ns':
348
defname = 'NS_' + defname[2:]
350
names = uuid_decoder.match(iface.attributes.uuid).groupdict()
351
m3str = names['m3'] + names['m4']
352
names['m3joined'] = ", ".join(["0x%s" % m3str[i:i+2] for i in xrange(0, 16, 2)])
354
if iface.name[2] == 'I':
355
implclass = iface.name[:2] + iface.name[3:]
357
implclass = '_MYCLASS_'
359
names.update({'defname': defname,
360
'macroname': iface.name.upper(),
362
'iid': iface.attributes.uuid,
363
'implclass': implclass})
365
fd.write(iface_header % names)
367
printComments(fd, iface.doccomments, '')
371
for m in iface.members:
372
if isinstance(m, xpidl.CDATA):
376
fd.write("NS_NO_VTABLE ")
378
if iface.attributes.deprecated:
379
fd.write("MOZ_DEPRECATED ")
382
fd.write(" : public %s" % iface.base)
383
fd.write(iface_prolog % names)
385
for key, group in itertools.groupby(iface.members, key=type):
386
if key == xpidl.ConstMember:
387
write_const_decls(group) # iterator of all the consts
390
if key == xpidl.Attribute:
391
write_attr_decl(member)
392
elif key == xpidl.Method:
393
write_method_decl(member)
394
elif key == xpidl.CDATA:
395
fd.write(" %s" % member.data)
397
raise Exception("Unexpected interface member: %s" % member)
399
fd.write(iface_epilog % names)
401
for member in iface.members:
402
if isinstance(member, xpidl.Attribute):
403
fd.write("\\\n %s; " % attributeAsNative(member, True))
404
if not member.readonly:
405
fd.write("\\\n %s; " % attributeAsNative(member, False))
406
elif isinstance(member, xpidl.Method):
407
fd.write("\\\n %s; " % methodAsNative(member))
408
if len(iface.members) == 0:
409
fd.write('\\\n /* no methods! */')
410
elif not member.kind in ('attribute', 'method'):
413
fd.write(iface_forward % names)
415
def emitTemplate(tmpl):
416
for member in iface.members:
417
if isinstance(member, xpidl.Attribute):
418
fd.write(tmpl % {'asNative': attributeAsNative(member, True),
419
'nativeName': attributeNativeName(member, True),
420
'paramList': attributeParamNames(member)})
421
if not member.readonly:
422
fd.write(tmpl % {'asNative': attributeAsNative(member, False),
423
'nativeName': attributeNativeName(member, False),
424
'paramList': attributeParamNames(member)})
425
elif isinstance(member, xpidl.Method):
426
fd.write(tmpl % {'asNative': methodAsNative(member),
427
'nativeName': methodNativeName(member),
428
'paramList': paramlistNames(member)})
429
if len(iface.members) == 0:
430
fd.write('\\\n /* no methods! */')
431
elif not member.kind in ('attribute', 'method'):
434
emitTemplate("\\\n %(asNative)s { return _to %(nativeName)s(%(paramList)s); } ")
436
fd.write(iface_forward_safe % names)
438
emitTemplate("\\\n %(asNative)s { return !_to ? NS_ERROR_NULL_POINTER : _to->%(nativeName)s(%(paramList)s); } ")
440
fd.write(iface_template_prolog % names)
442
for member in iface.members:
443
if isinstance(member, xpidl.ConstMember) or isinstance(member, xpidl.CDATA): continue
444
fd.write("/* %s */\n" % member.toIDL())
445
if isinstance(member, xpidl.Attribute):
446
fd.write(example_tmpl % {'implclass': implclass,
447
'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
448
'nativeName': attributeNativeName(member, True),
449
'paramList': attributeParamlist(member, True)})
450
if not member.readonly:
451
fd.write(example_tmpl % {'implclass': implclass,
452
'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
453
'nativeName': attributeNativeName(member, False),
454
'paramList': attributeParamlist(member, False)})
455
elif isinstance(member, xpidl.Method):
456
fd.write(example_tmpl % {'implclass': implclass,
457
'returntype': methodReturnType(member, 'NS_IMETHODIMP'),
458
'nativeName': methodNativeName(member),
459
'paramList': paramlistAsNative(member, empty='')})
462
fd.write(iface_template_epilog)
464
if __name__ == '__main__':
465
from optparse import OptionParser
467
o.add_option('-I', action='append', dest='incdirs', default=['.'],
468
help="Directory to search for imported files")
469
o.add_option('--cachedir', dest='cachedir', default=None,
470
help="Directory in which to cache lex/parse tables.")
471
o.add_option('-o', dest='outfile', default=None,
472
help="Output file (default is stdout)")
473
o.add_option('-d', dest='depfile', default=None,
474
help="Generate a make dependency file")
475
o.add_option('--regen', action='store_true', dest='regen', default=False,
476
help="Regenerate IDL Parser cache")
477
options, args = o.parse_args()
478
file = args[0] if args else None
480
if options.cachedir is not None:
481
if not os.path.isdir(options.cachedir):
482
os.mkdir(options.cachedir)
483
sys.path.append(options.cachedir)
485
# The only thing special about a regen is that there are no input files.
487
if options.cachedir is None:
488
print >>sys.stderr, "--regen useless without --cachedir"
489
# Delete the lex/yacc files. Ply is too stupid to regenerate them
491
for fileglobs in [os.path.join(options.cachedir, f) for f in ["xpidllex.py*", "xpidlyacc.py*"]]:
492
for filename in glob.glob(fileglobs):
495
# Instantiate the parser.
496
p = xpidl.IDLParser(outputdir=options.cachedir)
501
if options.depfile is not None and options.outfile is None:
502
print >>sys.stderr, "-d requires -o"
505
if options.outfile is not None:
506
outfd = open(options.outfile, 'w')
512
idl = p.parse(open(file).read(), filename=file)
513
idl.resolve(options.incdirs, p)
514
print_header(idl, outfd, file)
519
if options.depfile is not None:
520
dirname = os.path.dirname(options.depfile)
526
depfd = open(options.depfile, 'w')
527
deps = [dep.replace('\\', '/') for dep in idl.deps]
529
print >>depfd, "%s: %s" % (options.outfile, " ".join(deps))