|
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) |