ipc/ipdl/ipdl.py

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

mercurial