no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / ipc / ipdl / ipdl.py
blob8e5dd5db3d2dc4ff54db0cd35deca6d701e20e24
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 import optparse
5 import os
6 import sys
7 from configparser import RawConfigParser
8 from io import StringIO
10 import ipdl
11 from ipdl.ast import SYNC
14 def log(minv, fmt, *args):
15 if _verbosity >= minv:
16 print(fmt % args)
19 # process command line
22 op = optparse.OptionParser(usage="ipdl.py [options] IPDLfiles...")
23 op.add_option(
24 "-I",
25 "--include",
26 dest="includedirs",
27 default=[],
28 action="append",
29 help="Additional directory to search for included protocol specifications",
31 op.add_option(
32 "-s",
33 "--sync-msg-list",
34 dest="syncMsgList",
35 default="sync-messages.ini",
36 help="Config file listing allowed sync messages",
38 op.add_option(
39 "-m",
40 "--msg-metadata",
41 dest="msgMetadata",
42 default="message-metadata.ini",
43 help="Predicted message sizes for reducing serialization malloc overhead.",
45 op.add_option(
46 "-v",
47 "--verbose",
48 dest="verbosity",
49 default=1,
50 action="count",
51 help="Verbose logging (specify -vv or -vvv for very verbose logging)",
53 op.add_option(
54 "-q",
55 "--quiet",
56 dest="verbosity",
57 action="store_const",
58 const=0,
59 help="Suppress logging output",
61 op.add_option(
62 "-d",
63 "--outheaders-dir",
64 dest="headersdir",
65 default=".",
66 help="""Directory into which C++ headers will be generated.
67 A protocol Foo in the namespace bar will cause the headers
68 dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h
69 to be generated""",
71 op.add_option(
72 "-o",
73 "--outcpp-dir",
74 dest="cppdir",
75 default=".",
76 help="""Directory into which C++ sources will be generated
77 A protocol Foo in the namespace bar will cause the sources
78 cppdir/FooParent.cpp, cppdir/FooChild.cpp
79 to be generated""",
81 op.add_option(
82 "-F",
83 "--file-list",
84 dest="file_list_file",
85 default=None,
86 help="""A file containing IPDL files to parse. This will be
87 merged with files provided on the commandline.""",
90 options, cmdline_files = op.parse_args()
91 _verbosity = options.verbosity
92 syncMsgList = options.syncMsgList
93 msgMetadata = options.msgMetadata
94 headersdir = options.headersdir
95 cppdir = options.cppdir
96 includedirs = [os.path.abspath(incdir) for incdir in options.includedirs]
98 files = []
100 if options.file_list_file is not None:
101 with open(options.file_list_file) as f:
102 files.extend(f.read().splitlines())
104 files.extend(cmdline_files)
106 if not len(files):
107 op.error("No IPDL files specified")
109 ipcmessagestartpath = os.path.join(headersdir, "IPCMessageStart.h")
110 ipc_msgtype_name_path = os.path.join(cppdir, "IPCMessageTypeName.cpp")
112 log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir)
113 log(2, 'Generated C++ sources will be generated in "%s"', cppdir)
115 allmessages = {}
116 allsyncmessages = []
117 allmessageprognames = []
118 allprotocols = []
121 def normalizedFilename(f):
122 if f == "-":
123 return "<stdin>"
124 return f
127 log(2, "Reading sync message list")
128 parser = RawConfigParser()
129 parser.read_file(open(options.syncMsgList))
130 syncMsgList = parser.sections()
132 for section in syncMsgList:
133 if not parser.get(section, "description"):
134 print("Error: Sync message %s lacks a description" % section, file=sys.stderr)
135 sys.exit(1)
137 # Read message metadata. Right now we only have 'segment_capacity'
138 # for the standard segment size used for serialization.
139 log(2, "Reading message metadata...")
140 msgMetadataConfig = RawConfigParser()
141 msgMetadataConfig.read_file(open(options.msgMetadata))
143 segmentCapacityDict = {}
144 for msgName in msgMetadataConfig.sections():
145 if msgMetadataConfig.has_option(msgName, "segment_capacity"):
146 capacity = msgMetadataConfig.get(msgName, "segment_capacity")
147 segmentCapacityDict[msgName] = capacity
149 # First pass: parse and type-check all protocols
150 for f in files:
151 log(2, os.path.basename(f))
152 filename = normalizedFilename(f)
153 if f == "-":
154 fd = sys.stdin
155 else:
156 fd = open(f)
158 specstring = fd.read()
159 fd.close()
161 ast = ipdl.parse(specstring, filename, includedirs=includedirs)
162 if ast is None:
163 print("Specification could not be parsed.", file=sys.stderr)
164 sys.exit(1)
166 log(2, "checking types")
167 if not ipdl.typecheck(ast):
168 print("Specification is not well typed.", file=sys.stderr)
169 sys.exit(1)
171 if not ipdl.checkSyncMessage(ast, syncMsgList):
172 print(
173 "Error: New sync IPC messages must be reviewed by an IPC peer and recorded in %s"
174 % options.syncMsgList,
175 file=sys.stderr,
176 ) # NOQA: E501
177 sys.exit(1)
179 if not ipdl.checkFixedSyncMessages(parser):
180 # Errors have alraedy been printed to stderr, just exit
181 sys.exit(1)
183 # Second pass: generate code
184 for f in files:
185 # Read from parser cache
186 filename = normalizedFilename(f)
187 ast = ipdl.parse(None, filename, includedirs=includedirs)
188 ipdl.gencxx(filename, ast, headersdir, cppdir, segmentCapacityDict)
190 if ast.protocol:
191 allmessages[ast.protocol.name] = ipdl.genmsgenum(ast)
192 allprotocols.append(ast.protocol.name)
194 # e.g. PContent::RequestMemoryReport (not prefixed or suffixed.)
195 for md in ast.protocol.messageDecls:
196 allmessageprognames.append("%s::%s" % (md.namespace, md.decl.progname))
198 if md.sendSemantics is SYNC:
199 allsyncmessages.append(
200 "%s__%s" % (ast.protocol.name, md.prettyMsgName())
203 allprotocols.sort()
205 # Check if we have undefined message names in segmentCapacityDict.
206 # This is a fool-proof of the 'message-metadata.ini' file.
207 undefinedMessages = set(segmentCapacityDict.keys()) - set(allmessageprognames)
208 if len(undefinedMessages) > 0:
209 print("Error: Undefined message names in message-metadata.ini:", file=sys.stderr)
210 print(undefinedMessages, file=sys.stderr)
211 sys.exit(1)
213 ipcmsgstart = StringIO()
215 print(
217 // CODE GENERATED by ipdl.py. Do not edit.
219 #ifndef IPCMessageStart_h
220 #define IPCMessageStart_h
222 enum IPCMessageStart {
223 """,
224 file=ipcmsgstart,
227 for name in allprotocols:
228 print(" %sMsgStart," % name, file=ipcmsgstart)
230 print(
232 LastMsgIndex
235 static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO");
237 #endif // ifndef IPCMessageStart_h
238 """,
239 file=ipcmsgstart,
242 ipc_msgtype_name = StringIO()
243 print(
245 // CODE GENERATED by ipdl.py. Do not edit.
246 #include <cstdint>
248 #include "mozilla/ipc/ProtocolUtils.h"
249 #include "IPCMessageStart.h"
251 using std::uint32_t;
253 namespace {
255 enum IPCMessages {
256 """,
257 file=ipc_msgtype_name,
260 for protocol in sorted(allmessages.keys()):
261 for msg, num in allmessages[protocol].idnums:
262 if num:
263 print(" %s = %s," % (msg, num), file=ipc_msgtype_name)
264 elif not msg.endswith("End"):
265 print(" %s__%s," % (protocol, msg), file=ipc_msgtype_name)
267 print(
271 } // anonymous namespace
273 namespace IPC {
275 bool IPCMessageTypeIsSync(uint32_t aMessageType)
277 switch (aMessageType) {
278 """,
279 file=ipc_msgtype_name,
282 for msg in allsyncmessages:
283 print(" case %s:" % msg, file=ipc_msgtype_name)
285 print(
286 """ return true;
287 default:
288 return false;
292 const char* StringFromIPCMessageType(uint32_t aMessageType)
294 switch (aMessageType) {
295 """,
296 file=ipc_msgtype_name,
299 for protocol in sorted(allmessages.keys()):
300 for msg, num in allmessages[protocol].idnums:
301 if num or msg.endswith("End"):
302 continue
303 print(
305 case %s__%s:
306 return "%s::%s";"""
307 % (protocol, msg, protocol, msg),
308 file=ipc_msgtype_name,
311 print(
313 case DATA_PIPE_CLOSED_MESSAGE_TYPE:
314 return "DATA_PIPE_CLOSED_MESSAGE";
315 case DATA_PIPE_BYTES_CONSUMED_MESSAGE_TYPE:
316 return "DATA_PIPE_BYTES_CONSUMED_MESSAGE";
317 case ACCEPT_INVITE_MESSAGE_TYPE:
318 return "ACCEPT_INVITE_MESSAGE";
319 case REQUEST_INTRODUCTION_MESSAGE_TYPE:
320 return "REQUEST_INTRODUCTION_MESSAGE";
321 case INTRODUCE_MESSAGE_TYPE:
322 return "INTRODUCE_MESSAGE";
323 case BROADCAST_MESSAGE_TYPE:
324 return "BROADCAST_MESSAGE";
325 case EVENT_MESSAGE_TYPE:
326 return "EVENT_MESSAGE";
327 case IMPENDING_SHUTDOWN_MESSAGE_TYPE:
328 return "IMPENDING_SHUTDOWN";
329 case BUILD_IDS_MATCH_MESSAGE_TYPE:
330 return "BUILD_IDS_MATCH_MESSAGE";
331 case BUILD_ID_MESSAGE_TYPE:
332 return "BUILD_ID_MESSAGE";
333 case CHANNEL_OPENED_MESSAGE_TYPE:
334 return "CHANNEL_OPENED_MESSAGE";
335 case SHMEM_DESTROYED_MESSAGE_TYPE:
336 return "SHMEM_DESTROYED_MESSAGE";
337 case SHMEM_CREATED_MESSAGE_TYPE:
338 return "SHMEM_CREATED_MESSAGE";
339 case GOODBYE_MESSAGE_TYPE:
340 return "GOODBYE_MESSAGE";
341 case CANCEL_MESSAGE_TYPE:
342 return "CANCEL_MESSAGE";
343 default:
344 return "<unknown IPC msg name>";
348 } // namespace IPC
350 namespace mozilla {
351 namespace ipc {
353 const char* ProtocolIdToName(IPCMessageStart aId) {
354 switch (aId) {
355 """,
356 file=ipc_msgtype_name,
359 for name in allprotocols:
360 print(" case %sMsgStart:" % name, file=ipc_msgtype_name)
361 print(' return "%s";' % name, file=ipc_msgtype_name)
363 print(
365 default:
366 return "<unknown protocol id>";
370 } // namespace ipc
371 } // namespace mozilla
372 """,
373 file=ipc_msgtype_name,
376 ipdl.writeifmodified(ipcmsgstart.getvalue(), ipcmessagestartpath)
377 ipdl.writeifmodified(ipc_msgtype_name.getvalue(), ipc_msgtype_name_path)