Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
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) |