ipc/ipdl/ipdl.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ipc/ipdl/ipdl.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,177 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +import optparse, os, re, sys
     1.9 +from cStringIO import StringIO
    1.10 +from mozbuild.pythonutil import iter_modules_in_path
    1.11 +import mozpack.path as mozpath
    1.12 +import itertools
    1.13 +
    1.14 +import ipdl
    1.15 +
    1.16 +def log(minv, fmt, *args):
    1.17 +    if _verbosity >= minv:
    1.18 +        print fmt % args
    1.19 +
    1.20 +# process command line
    1.21 +
    1.22 +op = optparse.OptionParser(usage='ipdl.py [options] IPDLfiles...')
    1.23 +op.add_option('-I', '--include', dest='includedirs', default=[ ],
    1.24 +              action='append',
    1.25 +              help='Additional directory to search for included protocol specifications')
    1.26 +op.add_option('-v', '--verbose', dest='verbosity', default=1, action='count',
    1.27 +              help='Verbose logging (specify -vv or -vvv for very verbose logging)')
    1.28 +op.add_option('-q', '--quiet', dest='verbosity', action='store_const', const=0,
    1.29 +              help="Suppress logging output")
    1.30 +op.add_option('-d', '--outheaders-dir', dest='headersdir', default='.',
    1.31 +              help="""Directory into which C++ headers will be generated.
    1.32 +A protocol Foo in the namespace bar will cause the headers
    1.33 +  dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h
    1.34 +to be generated""")
    1.35 +op.add_option('-o', '--outcpp-dir', dest='cppdir', default='.',
    1.36 +              help="""Directory into which C++ sources will be generated
    1.37 +A protocol Foo in the namespace bar will cause the sources
    1.38 +  cppdir/FooParent.cpp, cppdir/FooChild.cpp
    1.39 +to be generated""")
    1.40 +
    1.41 +
    1.42 +options, files = op.parse_args()
    1.43 +_verbosity = options.verbosity
    1.44 +headersdir = options.headersdir
    1.45 +cppdir = options.cppdir
    1.46 +includedirs = [ os.path.abspath(incdir) for incdir in options.includedirs ]
    1.47 +
    1.48 +if not len(files):
    1.49 +    op.error("No IPDL files specified")
    1.50 +
    1.51 +ipcmessagestartpath = os.path.join(headersdir, 'IPCMessageStart.h')
    1.52 +
    1.53 +# Compiling the IPDL files can take a long time, even on a fast machine.
    1.54 +# Check to see whether we need to do any work.
    1.55 +latestipdlmod = max(os.stat(f).st_mtime
    1.56 +                    for f in itertools.chain(files,
    1.57 +                                             iter_modules_in_path(mozpath.dirname(__file__))))
    1.58 +
    1.59 +def outputModTime(f):
    1.60 +    # A non-existant file is newer than everything.
    1.61 +    if not os.path.exists(f):
    1.62 +        return 0
    1.63 +    return os.stat(f).st_mtime
    1.64 +
    1.65 +# Because the IPDL headers are placed into directories reflecting their
    1.66 +# namespace, collect a list here so we can easily map output names without
    1.67 +# parsing the actual IPDL files themselves.
    1.68 +headersmap = {}
    1.69 +for (path, dirs, headers) in os.walk(headersdir):
    1.70 +    for h in headers:
    1.71 +        base = os.path.basename(h)
    1.72 +        if base in headersmap:
    1.73 +            root, ext = os.path.splitext(base)
    1.74 +            print >>sys.stderr, 'A protocol named', root, 'exists in multiple namespaces'
    1.75 +            sys.exit(1)
    1.76 +        headersmap[base] = os.path.join(path, h)
    1.77 +
    1.78 +def outputfiles(f):
    1.79 +    base = os.path.basename(f)
    1.80 +    root, ext = os.path.splitext(base)
    1.81 +
    1.82 +    suffixes = ['']
    1.83 +    if ext == '.ipdl':
    1.84 +        suffixes += ['Child', 'Parent']
    1.85 +
    1.86 +    for suffix in suffixes:
    1.87 +        yield os.path.join(cppdir, "%s%s.cpp" % (root, suffix))
    1.88 +        header = "%s%s.h" % (root, suffix)
    1.89 +        # If the header already exists on disk, use that.  Otherwise,
    1.90 +        # just claim that the header is found in headersdir.
    1.91 +        if header in headersmap:
    1.92 +            yield headersmap[header]
    1.93 +        else:
    1.94 +            yield os.path.join(headersdir, header)
    1.95 +
    1.96 +def alloutputfiles():
    1.97 +    for f in files:
    1.98 +        for s in outputfiles(f):
    1.99 +            yield s
   1.100 +    yield ipcmessagestartpath
   1.101 +
   1.102 +earliestoutputmod = min(outputModTime(f) for f in alloutputfiles())
   1.103 +
   1.104 +if latestipdlmod < earliestoutputmod:
   1.105 +    sys.exit(0)
   1.106 +
   1.107 +log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir)
   1.108 +log(2, 'Generated C++ sources will be generated in "%s"', cppdir)
   1.109 +
   1.110 +allprotocols = []
   1.111 +
   1.112 +def normalizedFilename(f):
   1.113 +    if f == '-':
   1.114 +        return '<stdin>'
   1.115 +    return f
   1.116 +
   1.117 +# First pass: parse and type-check all protocols
   1.118 +for f in files:
   1.119 +    log(2, os.path.basename(f))
   1.120 +    filename = normalizedFilename(f)
   1.121 +    if f == '-':
   1.122 +        fd = sys.stdin
   1.123 +    else:
   1.124 +        fd = open(f)
   1.125 +
   1.126 +    specstring = fd.read()
   1.127 +    fd.close()
   1.128 +
   1.129 +    ast = ipdl.parse(specstring, filename, includedirs=includedirs)
   1.130 +    if ast is None:
   1.131 +        print >>sys.stderr, 'Specification could not be parsed.'
   1.132 +        sys.exit(1)
   1.133 +
   1.134 +    log(2, 'checking types')
   1.135 +    if not ipdl.typecheck(ast):
   1.136 +        print >>sys.stderr, 'Specification is not well typed.'
   1.137 +        sys.exit(1)
   1.138 +
   1.139 +    if _verbosity > 2:
   1.140 +        log(3, '  pretty printed code:')
   1.141 +        ipdl.genipdl(ast, codedir)
   1.142 +
   1.143 +# Second pass: generate code
   1.144 +for f in files:
   1.145 +    # Read from parser cache
   1.146 +    filename = normalizedFilename(f)
   1.147 +    ast = ipdl.parse(None, filename, includedirs=includedirs)
   1.148 +    ipdl.gencxx(filename, ast, headersdir, cppdir)
   1.149 +    
   1.150 +    if ast.protocol:
   1.151 +        allprotocols.append('%sMsgStart' % ast.protocol.name)
   1.152 +
   1.153 +
   1.154 +allprotocols.sort()
   1.155 +
   1.156 +ipcmsgstart = StringIO()
   1.157 +
   1.158 +print >>ipcmsgstart, """
   1.159 +// CODE GENERATED by ipdl.py. Do not edit.
   1.160 +
   1.161 +#ifndef IPCMessageStart_h
   1.162 +#define IPCMessageStart_h
   1.163 +
   1.164 +enum IPCMessageStart {
   1.165 +"""
   1.166 +
   1.167 +for name in allprotocols:
   1.168 +    print >>ipcmsgstart, "  %s," % name
   1.169 +    print >>ipcmsgstart, "  %sChild," % name
   1.170 +
   1.171 +print >>ipcmsgstart, """
   1.172 +  LastMsgIndex
   1.173 +};
   1.174 +
   1.175 +static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO");
   1.176 +
   1.177 +#endif // ifndef IPCMessageStart_h
   1.178 +"""
   1.179 +
   1.180 +ipdl.writeifmodified(ipcmsgstart.getvalue(), ipcmessagestartpath)

mercurial