ipc/ipdl/ipdl.py

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rwxr-xr-x

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 import optparse, os, re, sys
michael@0 6 from cStringIO import StringIO
michael@0 7 from mozbuild.pythonutil import iter_modules_in_path
michael@0 8 import mozpack.path as mozpath
michael@0 9 import itertools
michael@0 10
michael@0 11 import ipdl
michael@0 12
michael@0 13 def log(minv, fmt, *args):
michael@0 14 if _verbosity >= minv:
michael@0 15 print fmt % args
michael@0 16
michael@0 17 # process command line
michael@0 18
michael@0 19 op = optparse.OptionParser(usage='ipdl.py [options] IPDLfiles...')
michael@0 20 op.add_option('-I', '--include', dest='includedirs', default=[ ],
michael@0 21 action='append',
michael@0 22 help='Additional directory to search for included protocol specifications')
michael@0 23 op.add_option('-v', '--verbose', dest='verbosity', default=1, action='count',
michael@0 24 help='Verbose logging (specify -vv or -vvv for very verbose logging)')
michael@0 25 op.add_option('-q', '--quiet', dest='verbosity', action='store_const', const=0,
michael@0 26 help="Suppress logging output")
michael@0 27 op.add_option('-d', '--outheaders-dir', dest='headersdir', default='.',
michael@0 28 help="""Directory into which C++ headers will be generated.
michael@0 29 A protocol Foo in the namespace bar will cause the headers
michael@0 30 dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h
michael@0 31 to be generated""")
michael@0 32 op.add_option('-o', '--outcpp-dir', dest='cppdir', default='.',
michael@0 33 help="""Directory into which C++ sources will be generated
michael@0 34 A protocol Foo in the namespace bar will cause the sources
michael@0 35 cppdir/FooParent.cpp, cppdir/FooChild.cpp
michael@0 36 to be generated""")
michael@0 37
michael@0 38
michael@0 39 options, files = op.parse_args()
michael@0 40 _verbosity = options.verbosity
michael@0 41 headersdir = options.headersdir
michael@0 42 cppdir = options.cppdir
michael@0 43 includedirs = [ os.path.abspath(incdir) for incdir in options.includedirs ]
michael@0 44
michael@0 45 if not len(files):
michael@0 46 op.error("No IPDL files specified")
michael@0 47
michael@0 48 ipcmessagestartpath = os.path.join(headersdir, 'IPCMessageStart.h')
michael@0 49
michael@0 50 # Compiling the IPDL files can take a long time, even on a fast machine.
michael@0 51 # Check to see whether we need to do any work.
michael@0 52 latestipdlmod = max(os.stat(f).st_mtime
michael@0 53 for f in itertools.chain(files,
michael@0 54 iter_modules_in_path(mozpath.dirname(__file__))))
michael@0 55
michael@0 56 def outputModTime(f):
michael@0 57 # A non-existant file is newer than everything.
michael@0 58 if not os.path.exists(f):
michael@0 59 return 0
michael@0 60 return os.stat(f).st_mtime
michael@0 61
michael@0 62 # Because the IPDL headers are placed into directories reflecting their
michael@0 63 # namespace, collect a list here so we can easily map output names without
michael@0 64 # parsing the actual IPDL files themselves.
michael@0 65 headersmap = {}
michael@0 66 for (path, dirs, headers) in os.walk(headersdir):
michael@0 67 for h in headers:
michael@0 68 base = os.path.basename(h)
michael@0 69 if base in headersmap:
michael@0 70 root, ext = os.path.splitext(base)
michael@0 71 print >>sys.stderr, 'A protocol named', root, 'exists in multiple namespaces'
michael@0 72 sys.exit(1)
michael@0 73 headersmap[base] = os.path.join(path, h)
michael@0 74
michael@0 75 def outputfiles(f):
michael@0 76 base = os.path.basename(f)
michael@0 77 root, ext = os.path.splitext(base)
michael@0 78
michael@0 79 suffixes = ['']
michael@0 80 if ext == '.ipdl':
michael@0 81 suffixes += ['Child', 'Parent']
michael@0 82
michael@0 83 for suffix in suffixes:
michael@0 84 yield os.path.join(cppdir, "%s%s.cpp" % (root, suffix))
michael@0 85 header = "%s%s.h" % (root, suffix)
michael@0 86 # If the header already exists on disk, use that. Otherwise,
michael@0 87 # just claim that the header is found in headersdir.
michael@0 88 if header in headersmap:
michael@0 89 yield headersmap[header]
michael@0 90 else:
michael@0 91 yield os.path.join(headersdir, header)
michael@0 92
michael@0 93 def alloutputfiles():
michael@0 94 for f in files:
michael@0 95 for s in outputfiles(f):
michael@0 96 yield s
michael@0 97 yield ipcmessagestartpath
michael@0 98
michael@0 99 earliestoutputmod = min(outputModTime(f) for f in alloutputfiles())
michael@0 100
michael@0 101 if latestipdlmod < earliestoutputmod:
michael@0 102 sys.exit(0)
michael@0 103
michael@0 104 log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir)
michael@0 105 log(2, 'Generated C++ sources will be generated in "%s"', cppdir)
michael@0 106
michael@0 107 allprotocols = []
michael@0 108
michael@0 109 def normalizedFilename(f):
michael@0 110 if f == '-':
michael@0 111 return '<stdin>'
michael@0 112 return f
michael@0 113
michael@0 114 # First pass: parse and type-check all protocols
michael@0 115 for f in files:
michael@0 116 log(2, os.path.basename(f))
michael@0 117 filename = normalizedFilename(f)
michael@0 118 if f == '-':
michael@0 119 fd = sys.stdin
michael@0 120 else:
michael@0 121 fd = open(f)
michael@0 122
michael@0 123 specstring = fd.read()
michael@0 124 fd.close()
michael@0 125
michael@0 126 ast = ipdl.parse(specstring, filename, includedirs=includedirs)
michael@0 127 if ast is None:
michael@0 128 print >>sys.stderr, 'Specification could not be parsed.'
michael@0 129 sys.exit(1)
michael@0 130
michael@0 131 log(2, 'checking types')
michael@0 132 if not ipdl.typecheck(ast):
michael@0 133 print >>sys.stderr, 'Specification is not well typed.'
michael@0 134 sys.exit(1)
michael@0 135
michael@0 136 if _verbosity > 2:
michael@0 137 log(3, ' pretty printed code:')
michael@0 138 ipdl.genipdl(ast, codedir)
michael@0 139
michael@0 140 # Second pass: generate code
michael@0 141 for f in files:
michael@0 142 # Read from parser cache
michael@0 143 filename = normalizedFilename(f)
michael@0 144 ast = ipdl.parse(None, filename, includedirs=includedirs)
michael@0 145 ipdl.gencxx(filename, ast, headersdir, cppdir)
michael@0 146
michael@0 147 if ast.protocol:
michael@0 148 allprotocols.append('%sMsgStart' % ast.protocol.name)
michael@0 149
michael@0 150
michael@0 151 allprotocols.sort()
michael@0 152
michael@0 153 ipcmsgstart = StringIO()
michael@0 154
michael@0 155 print >>ipcmsgstart, """
michael@0 156 // CODE GENERATED by ipdl.py. Do not edit.
michael@0 157
michael@0 158 #ifndef IPCMessageStart_h
michael@0 159 #define IPCMessageStart_h
michael@0 160
michael@0 161 enum IPCMessageStart {
michael@0 162 """
michael@0 163
michael@0 164 for name in allprotocols:
michael@0 165 print >>ipcmsgstart, " %s," % name
michael@0 166 print >>ipcmsgstart, " %sChild," % name
michael@0 167
michael@0 168 print >>ipcmsgstart, """
michael@0 169 LastMsgIndex
michael@0 170 };
michael@0 171
michael@0 172 static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO");
michael@0 173
michael@0 174 #endif // ifndef IPCMessageStart_h
michael@0 175 """
michael@0 176
michael@0 177 ipdl.writeifmodified(ipcmsgstart.getvalue(), ipcmessagestartpath)

mercurial