summaryrefslogtreecommitdiffstats
path: root/ipc/ipdl/ipdl.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/ipdl/ipdl.py')
-rwxr-xr-xipc/ipdl/ipdl.py232
1 files changed, 232 insertions, 0 deletions
diff --git a/ipc/ipdl/ipdl.py b/ipc/ipdl/ipdl.py
new file mode 100755
index 000000000..2220b4f83
--- /dev/null
+++ b/ipc/ipdl/ipdl.py
@@ -0,0 +1,232 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import optparse, os, re, sys
+from cStringIO import StringIO
+from mozbuild.pythonutil import iter_modules_in_path
+import mozpack.path as mozpath
+import itertools
+
+import ipdl
+
+def log(minv, fmt, *args):
+ if _verbosity >= minv:
+ print fmt % args
+
+# process command line
+
+op = optparse.OptionParser(usage='ipdl.py [options] IPDLfiles...')
+op.add_option('-I', '--include', dest='includedirs', default=[ ],
+ action='append',
+ help='Additional directory to search for included protocol specifications')
+op.add_option('-v', '--verbose', dest='verbosity', default=1, action='count',
+ help='Verbose logging (specify -vv or -vvv for very verbose logging)')
+op.add_option('-q', '--quiet', dest='verbosity', action='store_const', const=0,
+ help="Suppress logging output")
+op.add_option('-d', '--outheaders-dir', dest='headersdir', default='.',
+ help="""Directory into which C++ headers will be generated.
+A protocol Foo in the namespace bar will cause the headers
+ dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h
+to be generated""")
+op.add_option('-o', '--outcpp-dir', dest='cppdir', default='.',
+ help="""Directory into which C++ sources will be generated
+A protocol Foo in the namespace bar will cause the sources
+ cppdir/FooParent.cpp, cppdir/FooChild.cpp
+to be generated""")
+
+
+options, files = op.parse_args()
+_verbosity = options.verbosity
+headersdir = options.headersdir
+cppdir = options.cppdir
+includedirs = [ os.path.abspath(incdir) for incdir in options.includedirs ]
+
+if not len(files):
+ op.error("No IPDL files specified")
+
+ipcmessagestartpath = os.path.join(headersdir, 'IPCMessageStart.h')
+ipc_msgtype_name_path = os.path.join(cppdir, 'IPCMessageTypeName.cpp')
+
+# Compiling the IPDL files can take a long time, even on a fast machine.
+# Check to see whether we need to do any work.
+latestipdlmod = max(os.stat(f).st_mtime
+ for f in itertools.chain(files,
+ iter_modules_in_path(mozpath.dirname(__file__))))
+
+def outputModTime(f):
+ # A non-existant file is newer than everything.
+ if not os.path.exists(f):
+ return 0
+ return os.stat(f).st_mtime
+
+# Because the IPDL headers are placed into directories reflecting their
+# namespace, collect a list here so we can easily map output names without
+# parsing the actual IPDL files themselves.
+headersmap = {}
+for (path, dirs, headers) in os.walk(headersdir):
+ for h in headers:
+ base = os.path.basename(h)
+ if base in headersmap:
+ root, ext = os.path.splitext(base)
+ print >>sys.stderr, 'A protocol named', root, 'exists in multiple namespaces'
+ sys.exit(1)
+ headersmap[base] = os.path.join(path, h)
+
+def outputfiles(f):
+ base = os.path.basename(f)
+ root, ext = os.path.splitext(base)
+
+ suffixes = ['']
+ if ext == '.ipdl':
+ suffixes += ['Child', 'Parent']
+
+ for suffix in suffixes:
+ yield os.path.join(cppdir, "%s%s.cpp" % (root, suffix))
+ header = "%s%s.h" % (root, suffix)
+ # If the header already exists on disk, use that. Otherwise,
+ # just claim that the header is found in headersdir.
+ if header in headersmap:
+ yield headersmap[header]
+ else:
+ yield os.path.join(headersdir, header)
+
+def alloutputfiles():
+ for f in files:
+ for s in outputfiles(f):
+ yield s
+ yield ipcmessagestartpath
+
+earliestoutputmod = min(outputModTime(f) for f in alloutputfiles())
+
+if latestipdlmod < earliestoutputmod:
+ sys.exit(0)
+
+log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir)
+log(2, 'Generated C++ sources will be generated in "%s"', cppdir)
+
+allmessages = {}
+allprotocols = []
+
+def normalizedFilename(f):
+ if f == '-':
+ return '<stdin>'
+ return f
+
+# First pass: parse and type-check all protocols
+for f in files:
+ log(2, os.path.basename(f))
+ filename = normalizedFilename(f)
+ if f == '-':
+ fd = sys.stdin
+ else:
+ fd = open(f)
+
+ specstring = fd.read()
+ fd.close()
+
+ ast = ipdl.parse(specstring, filename, includedirs=includedirs)
+ if ast is None:
+ print >>sys.stderr, 'Specification could not be parsed.'
+ sys.exit(1)
+
+ log(2, 'checking types')
+ if not ipdl.typecheck(ast):
+ print >>sys.stderr, 'Specification is not well typed.'
+ sys.exit(1)
+
+ if _verbosity > 2:
+ log(3, ' pretty printed code:')
+ ipdl.genipdl(ast, codedir)
+
+# Second pass: generate code
+for f in files:
+ # Read from parser cache
+ filename = normalizedFilename(f)
+ ast = ipdl.parse(None, filename, includedirs=includedirs)
+ ipdl.gencxx(filename, ast, headersdir, cppdir)
+
+ if ast.protocol:
+ allmessages[ast.protocol.name] = ipdl.genmsgenum(ast)
+ allprotocols.append('%sMsgStart' % ast.protocol.name)
+
+allprotocols.sort()
+
+ipcmsgstart = StringIO()
+
+print >>ipcmsgstart, """
+// CODE GENERATED by ipdl.py. Do not edit.
+
+#ifndef IPCMessageStart_h
+#define IPCMessageStart_h
+
+enum IPCMessageStart {
+"""
+
+for name in allprotocols:
+ print >>ipcmsgstart, " %s," % name
+ print >>ipcmsgstart, " %sChild," % name
+
+print >>ipcmsgstart, """
+ LastMsgIndex
+};
+
+static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO");
+
+#endif // ifndef IPCMessageStart_h
+"""
+
+ipc_msgtype_name = StringIO()
+print >>ipc_msgtype_name, """
+// CODE GENERATED by ipdl.py. Do not edit.
+#include <cstdint>
+
+#include "IPCMessageStart.h"
+
+using std::uint32_t;
+
+namespace {
+
+enum IPCMessages {
+"""
+
+for protocol in sorted(allmessages.keys()):
+ for (msg, num) in allmessages[protocol].idnums:
+ if num:
+ print >>ipc_msgtype_name, " %s = %s," % (msg, num)
+ elif not msg.endswith('End'):
+ print >>ipc_msgtype_name, " %s__%s," % (protocol, msg)
+
+print >>ipc_msgtype_name, """
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace ipc {
+
+const char* StringFromIPCMessageType(uint32_t aMessageType)
+{
+ switch (aMessageType) {
+"""
+
+for protocol in sorted(allmessages.keys()):
+ for (msg, num) in allmessages[protocol].idnums:
+ if num or msg.endswith('End'):
+ continue
+ print >>ipc_msgtype_name, """
+ case %s__%s:
+ return "%s::%s";""" % (protocol, msg, protocol, msg)
+
+print >>ipc_msgtype_name, """
+ default:
+ return "???";
+ }
+}
+
+} // namespace ipc
+} // namespace mozilla
+"""
+
+ipdl.writeifmodified(ipcmsgstart.getvalue(), ipcmessagestartpath)
+ipdl.writeifmodified(ipc_msgtype_name.getvalue(), ipc_msgtype_name_path)