2
# header.py - Generate C++ header files from IDL.
4
# ***** BEGIN LICENSE BLOCK *****
5
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
7
# The contents of this file are subject to the Mozilla Public License Version
8
# 1.1 (the "License"); you may not use this file except in compliance with
9
# the License. You may obtain a copy of the License at
10
# http://www.mozilla.org/MPL/
12
# Software distributed under the License is distributed on an "AS IS" basis,
13
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14
# for the specific language governing rights and limitations under the
17
# The Original Code is mozilla.org code.
19
# The Initial Developer of the Original Code is
21
# Portions created by the Initial Developer are Copyright (C) 2008
22
# the Initial Developer. All Rights Reserved.
25
# Benjamin Smedberg <benjamin@smedbergs.us>
27
# Alternatively, the contents of this file may be used under the terms of
28
# either of the GNU General Public License Version 2 or later (the "GPL"),
29
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30
# in which case the provisions of the GPL or the LGPL are applicable instead
31
# of those above. If you wish to allow use of your version of this file only
32
# under the terms of either the GPL or the LGPL, and not to allow others to
33
# use your version of this file under the terms of the MPL, indicate your
34
# decision by deleting the provisions above and replace them with the notice
35
# and other provisions required by the GPL or the LGPL. If you do not delete
36
# the provisions above, a recipient may use your version of this file under
37
# the terms of any one of the MPL, the GPL or the LGPL.
39
# ***** END LICENSE BLOCK *****
41
"""Print a C++ header file for the IDL files specified on the command line"""
43
import sys, os.path, re, xpidl
45
printdoccomments = False
48
def printComments(fd, clist, indent):
50
fd.write("%s%s\n" % (indent, c))
52
def printComments(fd, clist, indent):
56
return str[0].upper() + str[1:]
58
def attributeParamName(a):
59
return "a" + firstCap(a.name)
61
def attributeParamNames(a):
62
l = [attributeParamName(a)]
63
if a.implicit_jscontext:
67
def attributeNativeName(a, getter):
68
binaryname = a.binaryname is not None and a.binaryname or firstCap(a.name)
69
return "%s%s" % (getter and 'Get' or 'Set', binaryname)
71
def attributeReturnType(a, macro):
72
"""macro should be NS_IMETHOD or NS_IMETHODIMP"""
74
return macro == "NS_IMETHOD" and "virtual nsresult" or "nsresult"
78
def attributeParamlist(a, getter):
79
l = ["%s%s" % (a.realtype.nativeType(getter and 'out' or 'in'),
80
attributeParamName(a))]
81
if a.implicit_jscontext:
82
l.insert(0, "JSContext* cx")
86
def attributeAsNative(a, getter):
87
scriptable = a.isScriptable() and "NS_SCRIPTABLE " or ""
88
deprecated = a.deprecated and "NS_DEPRECATED " or ""
89
params = {'scriptable': scriptable,
90
'deprecated': deprecated,
91
'returntype': attributeReturnType(a, 'NS_IMETHOD'),
92
'binaryname': attributeNativeName(a, getter),
93
'paramlist': attributeParamlist(a, getter)}
94
return "%(deprecated)s%(scriptable)s%(returntype)s %(binaryname)s(%(paramlist)s)" % params
96
def methodNativeName(m):
97
return m.binaryname is not None and m.binaryname or firstCap(m.name)
99
def methodReturnType(m, macro):
100
"""macro should be NS_IMETHOD or NS_IMETHODIMP"""
101
if m.nostdcall and m.notxpcom:
102
return "%s%s" % (macro == "NS_IMETHOD" and "virtual " or "",
103
m.realtype.nativeType('in').strip())
105
return "%snsresult" % (macro == "NS_IMETHOD" and "virtual " or "")
107
return "%s_(%s)" % (macro, m.realtype.nativeType('in').strip())
111
def methodAsNative(m):
112
scriptable = m.isScriptable() and "NS_SCRIPTABLE " or ""
114
return "%s%s %s(%s)" % (scriptable,
115
methodReturnType(m, 'NS_IMETHOD'),
117
paramlistAsNative(m))
119
def paramlistAsNative(m, empty='void'):
120
l = [paramAsNative(p) for p in m.params]
122
if m.implicit_jscontext:
123
l.append("JSContext* cx")
126
l.append('PRUint8 _argc')
128
if not m.notxpcom and m.realtype.name != 'void':
129
l.append(paramAsNative(xpidl.Param(paramtype='out',
134
realtype=m.realtype)))
141
def paramAsNative(p):
142
if p.paramtype == 'in':
145
typeannotate = ' NS_%sPARAM' % p.paramtype.upper()
147
return "%s%s%s" % (p.nativeType(),
151
def paramlistNames(m):
152
names = [p.name for p in m.params]
154
if m.implicit_jscontext:
158
names.append('_argc')
160
if not m.notxpcom and m.realtype.name != 'void':
161
names.append('_retval')
165
return ', '.join(names)
168
* DO NOT EDIT. THIS FILE IS GENERATED FROM %(filename)s
171
#ifndef __gen_%(basename)s_h__
172
#define __gen_%(basename)s_h__
176
#ifndef __gen_%(basename)s_h__
177
#include "%(basename)s.h"
181
jspubtd_include = """
185
header_end = """/* For IDL files that don't want to include root IDL files. */
192
#endif /* __gen_%(basename)s_h__ */
195
forward_decl = """class %(name)s; /* forward declaration */
200
"""returns the base name of a file with the last extension stripped"""
201
return os.path.basename(f).rpartition('.')[0]
203
def print_header(idl, fd, filename):
204
fd.write(header % {'filename': filename,
205
'basename': idl_basename(filename)})
208
for inc in idl.includes():
212
fd.write(include % {'basename': idl_basename(inc.filename)})
214
if idl.needsJSTypes():
215
fd.write(jspubtd_include)
220
for p in idl.productions:
221
if p.kind == 'include': continue
222
if p.kind == 'cdata':
226
if p.kind == 'forward':
227
fd.write(forward_decl % {'name': p.name})
229
if p.kind == 'interface':
230
write_interface(p, fd)
232
if p.kind == 'typedef':
233
printComments(fd, p.doccomments, '')
234
fd.write("typedef %s %s;\n\n" % (p.realtype.nativeType('in'),
237
fd.write(footer % {'basename': idl_basename(filename)})
240
/* starting interface: %(name)s */
241
#define %(defname)s_IID_STR "%(iid)s"
243
#define %(defname)s_IID \
244
{0x%(m0)s, 0x%(m1)s, 0x%(m2)s, \
249
uuid_decoder = re.compile(r"""(?P<m0>[a-f0-9]{8})-
253
(?P<m4>[a-f0-9]{12})$""", re.X)
258
NS_DECLARE_STATIC_IID_ACCESSOR(%(defname)s_IID)
264
NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID)
266
/* Use this macro when declaring classes that implement this interface. */
267
#define NS_DECL_%(macroname)s """
272
/* Use this macro to declare functions that forward the behavior of this interface to another object. */
273
#define NS_FORWARD_%(macroname)s(_to) """
275
iface_forward_safe = """
277
/* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
278
#define NS_FORWARD_SAFE_%(macroname)s(_to) """
280
iface_template_prolog = """
283
/* Use the code below as a template for the implementation class for this interface. */
286
class %(implclass)s : public %(name)s
290
NS_DECL_%(macroname)s
298
/* additional members */
301
/* Implementation file */
302
NS_IMPL_ISUPPORTS1(%(implclass)s, %(name)s)
304
%(implclass)s::%(implclass)s()
306
/* member initializers and constructor code */
309
%(implclass)s::~%(implclass)s()
311
/* destructor code */
316
example_tmpl = """%(returntype)s %(implclass)s::%(nativeName)s(%(paramList)s)
318
return NS_ERROR_NOT_IMPLEMENTED;
322
iface_template_epilog = """/* End of implementation class template. */
327
def write_interface(iface, fd):
328
if iface.namemap is None:
329
raise Exception("Interface was not resolved.")
331
def write_const_decl(c):
332
printComments(fd, c.doccomments, ' ')
334
basetype = c.basetype
337
fd.write(" enum { %(name)s = %(value)s%(signed)s };\n\n" % {
340
'signed': (not basetype.signed) and 'U' or ''})
342
def write_method_decl(m):
343
printComments(fd, m.doccomments, ' ')
345
fd.write(" /* %s */\n" % m.toIDL())
346
fd.write(" %s = 0;\n\n" % methodAsNative(m))
348
def write_attr_decl(a):
349
printComments(fd, a.doccomments, ' ')
351
fd.write(" /* %s */\n" % a.toIDL());
353
fd.write(" %s = 0;\n" % attributeAsNative(a, True))
355
fd.write(" %s = 0;\n" % attributeAsNative(a, False))
358
defname = iface.name.upper()
359
if iface.name[0:2] == 'ns':
360
defname = 'NS_' + defname[2:]
362
names = uuid_decoder.match(iface.attributes.uuid).groupdict()
363
m3str = names['m3'] + names['m4']
364
names['m3joined'] = ", ".join(["0x%s" % m3str[i:i+2] for i in xrange(0, 16, 2)])
366
if iface.name[2] == 'I':
367
implclass = iface.name[:2] + iface.name[3:]
369
implclass = '_MYCLASS_'
371
names.update({'defname': defname,
372
'macroname': iface.name.upper(),
374
'iid': iface.attributes.uuid,
375
'implclass': implclass})
377
fd.write(iface_header % names)
379
printComments(fd, iface.doccomments, '')
383
for m in iface.members:
384
if isinstance(m, xpidl.CDATA):
388
fd.write("NS_NO_VTABLE ")
390
if iface.attributes.scriptable:
391
fd.write("NS_SCRIPTABLE ")
392
if iface.attributes.deprecated:
393
fd.write("MOZ_DEPRECATED ")
396
fd.write(" : public %s" % iface.base)
397
fd.write(iface_prolog % names)
398
for member in iface.members:
399
if isinstance(member, xpidl.ConstMember):
400
write_const_decl(member)
401
elif isinstance(member, xpidl.Attribute):
402
write_attr_decl(member)
403
elif isinstance(member, xpidl.Method):
404
write_method_decl(member)
405
elif isinstance(member, xpidl.CDATA):
406
fd.write(" %s" % member.data)
408
raise Exception("Unexpected interface member: %s" % member)
410
fd.write(iface_epilog % names)
412
for member in iface.members:
413
if isinstance(member, xpidl.Attribute):
414
fd.write("\\\n %s; " % attributeAsNative(member, True))
415
if not member.readonly:
416
fd.write("\\\n %s; " % attributeAsNative(member, False))
417
elif isinstance(member, xpidl.Method):
418
fd.write("\\\n %s; " % methodAsNative(member))
419
if len(iface.members) == 0:
420
fd.write('\\\n /* no methods! */')
421
elif not member.kind in ('attribute', 'method'):
424
fd.write(iface_forward % names)
426
def emitTemplate(tmpl):
427
for member in iface.members:
428
if isinstance(member, xpidl.Attribute):
429
fd.write(tmpl % {'asNative': attributeAsNative(member, True),
430
'nativeName': attributeNativeName(member, True),
431
'paramList': attributeParamNames(member)})
432
if not member.readonly:
433
fd.write(tmpl % {'asNative': attributeAsNative(member, False),
434
'nativeName': attributeNativeName(member, False),
435
'paramList': attributeParamNames(member)})
436
elif isinstance(member, xpidl.Method):
437
fd.write(tmpl % {'asNative': methodAsNative(member),
438
'nativeName': methodNativeName(member),
439
'paramList': paramlistNames(member)})
440
if len(iface.members) == 0:
441
fd.write('\\\n /* no methods! */')
442
elif not member.kind in ('attribute', 'method'):
445
emitTemplate("\\\n %(asNative)s { return _to %(nativeName)s(%(paramList)s); } ")
447
fd.write(iface_forward_safe % names)
449
emitTemplate("\\\n %(asNative)s { return !_to ? NS_ERROR_NULL_POINTER : _to->%(nativeName)s(%(paramList)s); } ")
451
fd.write(iface_template_prolog % names)
453
for member in iface.members:
454
if isinstance(member, xpidl.ConstMember) or isinstance(member, xpidl.CDATA): continue
455
fd.write("/* %s */\n" % member.toIDL())
456
if isinstance(member, xpidl.Attribute):
457
fd.write(example_tmpl % {'implclass': implclass,
458
'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
459
'nativeName': attributeNativeName(member, True),
460
'paramList': attributeParamlist(member, True)})
461
if not member.readonly:
462
fd.write(example_tmpl % {'implclass': implclass,
463
'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
464
'nativeName': attributeNativeName(member, False),
465
'paramList': attributeParamlist(member, False)})
466
elif isinstance(member, xpidl.Method):
467
fd.write(example_tmpl % {'implclass': implclass,
468
'returntype': methodReturnType(member, 'NS_IMETHODIMP'),
469
'nativeName': methodNativeName(member),
470
'paramList': paramlistAsNative(member, empty='')})
473
fd.write(iface_template_epilog)
475
if __name__ == '__main__':
476
from optparse import OptionParser
478
o.add_option('-I', action='append', dest='incdirs', default=['.'],
479
help="Directory to search for imported files")
480
o.add_option('--cachedir', dest='cachedir', default=None,
481
help="Directory in which to cache lex/parse tables.")
482
o.add_option('-o', dest='outfile', default=None,
483
help="Output file (default is stdout)")
484
o.add_option('-d', dest='depfile', default=None,
485
help="Generate a make dependency file")
486
o.add_option('--regen', action='store_true', dest='regen', default=False,
487
help="Regenerate IDL Parser cache")
488
options, args = o.parse_args()
491
if options.cachedir is not None:
492
if not os.path.isdir(options.cachedir):
493
os.mkdir(options.cachedir)
494
sys.path.append(options.cachedir)
497
if options.cachedir is None:
498
print >>sys.stderr, "--regen requires --cachedir"
501
p = xpidl.IDLParser(outputdir=options.cachedir, regen=True)
504
if options.depfile is not None and options.outfile is None:
505
print >>sys.stderr, "-d requires -o"
508
if options.outfile is not None:
509
outfd = open(options.outfile, 'w')
515
p = xpidl.IDLParser(outputdir=options.cachedir)
516
idl = p.parse(open(file).read(), filename=file)
517
idl.resolve(options.incdirs, p)
518
print_header(idl, outfd, file)
523
if options.depfile is not None:
524
depfd = open(options.depfile, 'w')
525
deps = [dep.replace('\\', '/') for dep in idl.deps]
527
print >>depfd, "%s: %s" % (options.outfile, " ".join(deps))