Back to index

enigmail  1.4.3
header.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # header.py - Generate C++ header files from IDL.
00003 #
00004 # ***** BEGIN LICENSE BLOCK *****
00005 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006 #
00007 # The contents of this file are subject to the Mozilla Public License Version
00008 # 1.1 (the "License"); you may not use this file except in compliance with
00009 # the License. You may obtain a copy of the License at
00010 # http://www.mozilla.org/MPL/
00011 #
00012 # Software distributed under the License is distributed on an "AS IS" basis,
00013 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014 # for the specific language governing rights and limitations under the
00015 # License.
00016 #
00017 # The Original Code is mozilla.org code.
00018 #
00019 # The Initial Developer of the Original Code is
00020 #   Mozilla Foundation.
00021 # Portions created by the Initial Developer are Copyright (C) 2008
00022 # the Initial Developer. All Rights Reserved.
00023 #
00024 # Contributor(s):
00025 #   Benjamin Smedberg <benjamin@smedbergs.us>
00026 #
00027 # Alternatively, the contents of this file may be used under the terms of
00028 # either of the GNU General Public License Version 2 or later (the "GPL"),
00029 # or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030 # in which case the provisions of the GPL or the LGPL are applicable instead
00031 # of those above. If you wish to allow use of your version of this file only
00032 # under the terms of either the GPL or the LGPL, and not to allow others to
00033 # use your version of this file under the terms of the MPL, indicate your
00034 # decision by deleting the provisions above and replace them with the notice
00035 # and other provisions required by the GPL or the LGPL. If you do not delete
00036 # the provisions above, a recipient may use your version of this file under
00037 # the terms of any one of the MPL, the GPL or the LGPL.
00038 #
00039 # ***** END LICENSE BLOCK *****
00040 
00041 """Print a C++ header file for the IDL files specified on the command line"""
00042 
00043 import sys, os.path, re, xpidl, itertools, glob
00044 
00045 printdoccomments = False
00046 
00047 if printdoccomments:
00048     def printComments(fd, clist, indent):
00049         for c in clist:
00050             fd.write("%s%s\n" % (indent, c))
00051 else:
00052     def printComments(fd, clist, indent):
00053         pass
00054 
00055 def firstCap(str):
00056     return str[0].upper() + str[1:]
00057 
00058 def attributeParamName(a):
00059     return "a" + firstCap(a.name)
00060 
00061 def attributeParamNames(a):
00062     l = [attributeParamName(a)]
00063     if a.implicit_jscontext:
00064         l.insert(0, "cx")
00065     return ", ".join(l)
00066 
00067 def attributeNativeName(a, getter):
00068     binaryname = a.binaryname is not None and a.binaryname or firstCap(a.name)
00069     return "%s%s" % (getter and 'Get' or 'Set', binaryname)
00070 
00071 def attributeReturnType(a, macro):
00072     """macro should be NS_IMETHOD or NS_IMETHODIMP"""
00073     if (a.nostdcall):
00074         return macro == "NS_IMETHOD" and "virtual nsresult" or "nsresult"
00075     else:
00076         return macro
00077 
00078 def attributeParamlist(a, getter):
00079     l = ["%s%s" % (a.realtype.nativeType(getter and 'out' or 'in'),
00080                    attributeParamName(a))]
00081     if a.implicit_jscontext:
00082         l.insert(0, "JSContext* cx")
00083 
00084     return ", ".join(l)
00085 
00086 def attributeAsNative(a, getter):
00087         scriptable = a.isScriptable() and "NS_SCRIPTABLE " or ""
00088         deprecated = a.deprecated and "NS_DEPRECATED " or ""
00089         params = {'scriptable': scriptable,
00090                   'deprecated': deprecated,
00091                   'returntype': attributeReturnType(a, 'NS_IMETHOD'),
00092                   'binaryname': attributeNativeName(a, getter),
00093                   'paramlist': attributeParamlist(a, getter)}
00094         return "%(deprecated)s%(scriptable)s%(returntype)s %(binaryname)s(%(paramlist)s)" % params
00095 
00096 def methodNativeName(m):
00097     return m.binaryname is not None and m.binaryname or firstCap(m.name)
00098 
00099 def methodReturnType(m, macro):
00100     """macro should be NS_IMETHOD or NS_IMETHODIMP"""
00101     if m.nostdcall and m.notxpcom:
00102         return "%s%s" % (macro == "NS_IMETHOD" and "virtual " or "",
00103                          m.realtype.nativeType('in').strip())
00104     elif m.nostdcall:
00105         return "%snsresult" % (macro == "NS_IMETHOD" and "virtual " or "")
00106     elif m.notxpcom:
00107         return "%s_(%s)" % (macro, m.realtype.nativeType('in').strip())
00108     else:
00109         return macro
00110 
00111 def methodAsNative(m):
00112     scriptable = m.isScriptable() and "NS_SCRIPTABLE " or ""
00113 
00114     return "%s%s %s(%s)" % (scriptable,
00115                             methodReturnType(m, 'NS_IMETHOD'),
00116                             methodNativeName(m),
00117                             paramlistAsNative(m))
00118 
00119 def paramlistAsNative(m, empty='void'):
00120     l = [paramAsNative(p) for p in m.params]
00121 
00122     if m.implicit_jscontext:
00123         l.append("JSContext* cx")
00124 
00125     if m.optional_argc:
00126         l.append('PRUint8 _argc')
00127 
00128     if not m.notxpcom and m.realtype.name != 'void':
00129         l.append(paramAsNative(xpidl.Param(paramtype='out',
00130                                            type=None,
00131                                            name='_retval',
00132                                            attlist=[],
00133                                            location=None,
00134                                            realtype=m.realtype)))
00135 
00136     if len(l) == 0:
00137         return empty
00138 
00139     return ", ".join(l)
00140 
00141 def paramAsNative(p):
00142     if p.paramtype == 'in':
00143         typeannotate = ''
00144     else:
00145         typeannotate = ' NS_%sPARAM' % p.paramtype.upper()
00146 
00147     return "%s%s%s" % (p.nativeType(),
00148                        p.name,
00149                        typeannotate)
00150 
00151 def paramlistNames(m):
00152     names = [p.name for p in m.params]
00153 
00154     if m.implicit_jscontext:
00155         names.append('cx')
00156 
00157     if m.optional_argc:
00158         names.append('_argc')
00159 
00160     if not m.notxpcom and m.realtype.name != 'void':
00161         names.append('_retval')
00162 
00163     if len(names) == 0:
00164         return ''
00165     return ', '.join(names)
00166 
00167 header = """/*
00168  * DO NOT EDIT.  THIS FILE IS GENERATED FROM %(filename)s
00169  */
00170 
00171 #ifndef __gen_%(basename)s_h__
00172 #define __gen_%(basename)s_h__
00173 """
00174 
00175 include = """
00176 #ifndef __gen_%(basename)s_h__
00177 #include "%(basename)s.h"
00178 #endif
00179 """
00180 
00181 jspubtd_include = """
00182 #include "jspubtd.h"
00183 """
00184 
00185 header_end = """/* For IDL files that don't want to include root IDL files. */
00186 #ifndef NS_NO_VTABLE
00187 #define NS_NO_VTABLE
00188 #endif
00189 """
00190 
00191 footer = """
00192 #endif /* __gen_%(basename)s_h__ */
00193 """
00194 
00195 forward_decl = """class %(name)s; /* forward declaration */
00196 
00197 """
00198 
00199 def idl_basename(f):
00200     """returns the base name of a file with the last extension stripped"""
00201     return os.path.basename(f).rpartition('.')[0]
00202 
00203 def print_header(idl, fd, filename):
00204     fd.write(header % {'filename': filename,
00205                        'basename': idl_basename(filename)})
00206 
00207     foundinc = False
00208     for inc in idl.includes():
00209         if not foundinc:
00210             foundinc = True
00211             fd.write('\n')
00212         fd.write(include % {'basename': idl_basename(inc.filename)})
00213 
00214     if idl.needsJSTypes():
00215         fd.write(jspubtd_include)
00216 
00217     fd.write('\n')
00218     fd.write(header_end)
00219 
00220     for p in idl.productions:
00221         if p.kind == 'include': continue
00222         if p.kind == 'cdata':
00223             fd.write(p.data)
00224             continue
00225 
00226         if p.kind == 'forward':
00227             fd.write(forward_decl % {'name': p.name})
00228             continue
00229         if p.kind == 'interface':
00230             write_interface(p, fd)
00231             continue
00232         if p.kind == 'typedef':
00233             printComments(fd, p.doccomments, '')
00234             fd.write("typedef %s %s;\n\n" % (p.realtype.nativeType('in'),
00235                                              p.name))
00236 
00237     fd.write(footer % {'basename': idl_basename(filename)})
00238 
00239 iface_header = r"""
00240 /* starting interface:    %(name)s */
00241 #define %(defname)s_IID_STR "%(iid)s"
00242 
00243 #define %(defname)s_IID \
00244   {0x%(m0)s, 0x%(m1)s, 0x%(m2)s, \
00245     { %(m3joined)s }}
00246 
00247 """
00248 
00249 uuid_decoder = re.compile(r"""(?P<m0>[a-f0-9]{8})-
00250                               (?P<m1>[a-f0-9]{4})-
00251                               (?P<m2>[a-f0-9]{4})-
00252                               (?P<m3>[a-f0-9]{4})-
00253                               (?P<m4>[a-f0-9]{12})$""", re.X)
00254 
00255 iface_prolog = """ {
00256  public: 
00257 
00258   NS_DECLARE_STATIC_IID_ACCESSOR(%(defname)s_IID)
00259 
00260 """
00261 
00262 iface_epilog = """};
00263 
00264   NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID)
00265 
00266 /* Use this macro when declaring classes that implement this interface. */
00267 #define NS_DECL_%(macroname)s """
00268 
00269 
00270 iface_forward = """
00271 
00272 /* Use this macro to declare functions that forward the behavior of this interface to another object. */
00273 #define NS_FORWARD_%(macroname)s(_to) """
00274 
00275 iface_forward_safe = """
00276 
00277 /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
00278 #define NS_FORWARD_SAFE_%(macroname)s(_to) """
00279 
00280 iface_template_prolog = """
00281 
00282 #if 0
00283 /* Use the code below as a template for the implementation class for this interface. */
00284 
00285 /* Header file */
00286 class %(implclass)s : public %(name)s
00287 {
00288 public:
00289   NS_DECL_ISUPPORTS
00290   NS_DECL_%(macroname)s
00291 
00292   %(implclass)s();
00293 
00294 private:
00295   ~%(implclass)s();
00296 
00297 protected:
00298   /* additional members */
00299 };
00300 
00301 /* Implementation file */
00302 NS_IMPL_ISUPPORTS1(%(implclass)s, %(name)s)
00303 
00304 %(implclass)s::%(implclass)s()
00305 {
00306   /* member initializers and constructor code */
00307 }
00308 
00309 %(implclass)s::~%(implclass)s()
00310 {
00311   /* destructor code */
00312 }
00313 
00314 """
00315 
00316 example_tmpl = """%(returntype)s %(implclass)s::%(nativeName)s(%(paramList)s)
00317 {
00318     return NS_ERROR_NOT_IMPLEMENTED;
00319 }
00320 """
00321 
00322 iface_template_epilog = """/* End of implementation class template. */
00323 #endif
00324 
00325 """
00326 
00327 def write_interface(iface, fd):
00328     if iface.namemap is None:
00329         raise Exception("Interface was not resolved.")
00330 
00331     def write_const_decls(g):
00332         fd.write("  enum {\n")
00333         enums = []
00334         for c in g:
00335             printComments(fd, c.doccomments, '  ')
00336             basetype = c.basetype
00337             value = c.getValue()
00338             enums.append("    %(name)s = %(value)s%(signed)s" % {
00339                          'name': c.name,
00340                          'value': value,
00341                          'signed': (not basetype.signed) and 'U' or ''})
00342         fd.write(",\n".join(enums))
00343         fd.write("\n  };\n\n")
00344 
00345     def write_method_decl(m):
00346         printComments(fd, m.doccomments, '  ')
00347 
00348         fd.write("  /* %s */\n" % m.toIDL())
00349         fd.write("  %s = 0;\n\n" % methodAsNative(m))
00350                                                                            
00351     def write_attr_decl(a):
00352         printComments(fd, a.doccomments, '  ')
00353 
00354         fd.write("  /* %s */\n" % a.toIDL());
00355 
00356         fd.write("  %s = 0;\n" % attributeAsNative(a, True))
00357         if not a.readonly:
00358             fd.write("  %s = 0;\n" % attributeAsNative(a, False))
00359         fd.write("\n")
00360 
00361     defname = iface.name.upper()
00362     if iface.name[0:2] == 'ns':
00363         defname = 'NS_' + defname[2:]
00364 
00365     names = uuid_decoder.match(iface.attributes.uuid).groupdict()
00366     m3str = names['m3'] + names['m4']
00367     names['m3joined'] = ", ".join(["0x%s" % m3str[i:i+2] for i in xrange(0, 16, 2)])
00368 
00369     if iface.name[2] == 'I':
00370         implclass = iface.name[:2] + iface.name[3:]
00371     else:
00372         implclass = '_MYCLASS_'
00373 
00374     names.update({'defname': defname,
00375                   'macroname': iface.name.upper(),
00376                   'name': iface.name,
00377                   'iid': iface.attributes.uuid,
00378                   'implclass': implclass})
00379 
00380     fd.write(iface_header % names)
00381 
00382     printComments(fd, iface.doccomments, '')
00383 
00384     fd.write("class ")
00385     foundcdata = False
00386     for m in iface.members:
00387         if isinstance(m, xpidl.CDATA):
00388             foundcdata = True
00389 
00390     if not foundcdata:
00391         fd.write("NS_NO_VTABLE ")
00392 
00393     if iface.attributes.scriptable:
00394         fd.write("NS_SCRIPTABLE ")
00395     if iface.attributes.deprecated:
00396         fd.write("MOZ_DEPRECATED ")
00397     fd.write(iface.name)
00398     if iface.base:
00399         fd.write(" : public %s" % iface.base)
00400     fd.write(iface_prolog % names)
00401 
00402     for key, group in itertools.groupby(iface.members, key=type):
00403         if key == xpidl.ConstMember:
00404             write_const_decls(group) # iterator of all the consts
00405         else:
00406             for member in group:
00407                 if key == xpidl.Attribute:
00408                     write_attr_decl(member)
00409                 elif key == xpidl.Method:
00410                     write_method_decl(member)
00411                 elif key == xpidl.CDATA:
00412                     fd.write(" %s" % member.data)
00413                 else:
00414                     raise Exception("Unexpected interface member: %s" % member)
00415 
00416     fd.write(iface_epilog % names)
00417 
00418     for member in iface.members:
00419         if isinstance(member, xpidl.Attribute):
00420             fd.write("\\\n  %s; " % attributeAsNative(member, True))
00421             if not member.readonly:
00422                 fd.write("\\\n  %s; " % attributeAsNative(member, False))
00423         elif isinstance(member, xpidl.Method):
00424             fd.write("\\\n  %s; " % methodAsNative(member))
00425     if len(iface.members) == 0:
00426         fd.write('\\\n  /* no methods! */')
00427     elif not member.kind in ('attribute', 'method'):
00428        fd.write('\\')
00429 
00430     fd.write(iface_forward % names)
00431 
00432     def emitTemplate(tmpl):
00433         for member in iface.members:
00434             if isinstance(member, xpidl.Attribute):
00435                 fd.write(tmpl % {'asNative': attributeAsNative(member, True),
00436                                  'nativeName': attributeNativeName(member, True),
00437                                  'paramList': attributeParamNames(member)})
00438                 if not member.readonly:
00439                     fd.write(tmpl % {'asNative': attributeAsNative(member, False),
00440                                      'nativeName': attributeNativeName(member, False),
00441                                      'paramList': attributeParamNames(member)})
00442             elif isinstance(member, xpidl.Method):
00443                 fd.write(tmpl % {'asNative': methodAsNative(member),
00444                                  'nativeName': methodNativeName(member),
00445                                  'paramList': paramlistNames(member)})
00446         if len(iface.members) == 0:
00447             fd.write('\\\n  /* no methods! */')
00448         elif not member.kind in ('attribute', 'method'):
00449             fd.write('\\')
00450 
00451     emitTemplate("\\\n  %(asNative)s { return _to %(nativeName)s(%(paramList)s); } ")
00452 
00453     fd.write(iface_forward_safe % names)
00454 
00455     emitTemplate("\\\n  %(asNative)s { return !_to ? NS_ERROR_NULL_POINTER : _to->%(nativeName)s(%(paramList)s); } ")
00456 
00457     fd.write(iface_template_prolog % names)
00458 
00459     for member in iface.members:
00460         if isinstance(member, xpidl.ConstMember) or isinstance(member, xpidl.CDATA): continue
00461         fd.write("/* %s */\n" % member.toIDL())
00462         if isinstance(member, xpidl.Attribute):
00463             fd.write(example_tmpl % {'implclass': implclass,
00464                                      'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
00465                                      'nativeName': attributeNativeName(member, True),
00466                                      'paramList': attributeParamlist(member, True)})
00467             if not member.readonly:
00468                 fd.write(example_tmpl % {'implclass': implclass,
00469                                          'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
00470                                          'nativeName': attributeNativeName(member, False),
00471                                          'paramList': attributeParamlist(member, False)})
00472         elif isinstance(member, xpidl.Method):
00473             fd.write(example_tmpl % {'implclass': implclass,
00474                                      'returntype': methodReturnType(member, 'NS_IMETHODIMP'),
00475                                      'nativeName': methodNativeName(member),
00476                                      'paramList': paramlistAsNative(member, empty='')})
00477         fd.write('\n')
00478 
00479     fd.write(iface_template_epilog)
00480 
00481 if __name__ == '__main__':
00482     from optparse import OptionParser
00483     o = OptionParser()
00484     o.add_option('-I', action='append', dest='incdirs', default=['.'],
00485                  help="Directory to search for imported files")
00486     o.add_option('--cachedir', dest='cachedir', default=None,
00487                  help="Directory in which to cache lex/parse tables.")
00488     o.add_option('-o', dest='outfile', default=None,
00489                  help="Output file (default is stdout)")
00490     o.add_option('-d', dest='depfile', default=None,
00491                  help="Generate a make dependency file")
00492     o.add_option('--regen', action='store_true', dest='regen', default=False,
00493                  help="Regenerate IDL Parser cache")
00494     options, args = o.parse_args()
00495     file = args[0] if args else None
00496 
00497     if options.cachedir is not None:
00498         if not os.path.isdir(options.cachedir):
00499             os.mkdir(options.cachedir)
00500         sys.path.append(options.cachedir)
00501 
00502     # The only thing special about a regen is that there are no input files.
00503     if options.regen:
00504         if options.cachedir is None:
00505             print >>sys.stderr, "--regen useless without --cachedir"
00506         # Delete the lex/yacc files.  Ply is too stupid to regenerate them
00507         # properly
00508         deadfiles = os.path.join(options.cachedir, "*.py*")
00509         for filename in glob.glob(deadfiles):
00510             print filename
00511             os.remove(filename)
00512 
00513     # Instantiate the parser.
00514     p = xpidl.IDLParser(outputdir=options.cachedir)
00515 
00516     if options.regen:
00517         sys.exit(0)
00518 
00519     if options.depfile is not None and options.outfile is None:
00520         print >>sys.stderr, "-d requires -o"
00521         sys.exit(1)
00522 
00523     if options.outfile is not None:
00524         outfd = open(options.outfile, 'w')
00525         closeoutfd = True
00526     else:
00527         outfd = sys.stdout
00528         closeoutfd = False
00529 
00530     idl = p.parse(open(file).read(), filename=file)
00531     idl.resolve(options.incdirs, p)
00532     print_header(idl, outfd, file)
00533 
00534     if closeoutfd:
00535         outfd.close()
00536 
00537     if options.depfile is not None:
00538         dirname = os.path.dirname(options.depfile)
00539         if dirname:
00540             try:
00541                 os.makedirs(dirname)
00542             except:
00543                 pass
00544         depfd = open(options.depfile, 'w')
00545         deps = [dep.replace('\\', '/') for dep in idl.deps]
00546 
00547         print >>depfd, "%s: %s" % (options.outfile, " ".join(deps))