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/. 4from __future__ import print_function 5 6from io import StringIO 7import optparse 8import os 9import sys 10from configparser import RawConfigParser 11 12import ipdl 13 14 15def log(minv, fmt, *args): 16 if _verbosity >= minv: 17 print(fmt % args) 18 19 20# process command line 21 22 23op = optparse.OptionParser(usage="ipdl.py [options] IPDLfiles...") 24op.add_option( 25 "-I", 26 "--include", 27 dest="includedirs", 28 default=[], 29 action="append", 30 help="Additional directory to search for included protocol specifications", 31) 32op.add_option( 33 "-s", 34 "--sync-msg-list", 35 dest="syncMsgList", 36 default="sync-messages.ini", 37 help="Config file listing allowed sync messages", 38) 39op.add_option( 40 "-m", 41 "--msg-metadata", 42 dest="msgMetadata", 43 default="message-metadata.ini", 44 help="Predicted message sizes for reducing serialization malloc overhead.", 45) 46op.add_option( 47 "-v", 48 "--verbose", 49 dest="verbosity", 50 default=1, 51 action="count", 52 help="Verbose logging (specify -vv or -vvv for very verbose logging)", 53) 54op.add_option( 55 "-q", 56 "--quiet", 57 dest="verbosity", 58 action="store_const", 59 const=0, 60 help="Suppress logging output", 61) 62op.add_option( 63 "-d", 64 "--outheaders-dir", 65 dest="headersdir", 66 default=".", 67 help="""Directory into which C++ headers will be generated. 68A protocol Foo in the namespace bar will cause the headers 69 dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h 70to be generated""", 71) 72op.add_option( 73 "-o", 74 "--outcpp-dir", 75 dest="cppdir", 76 default=".", 77 help="""Directory into which C++ sources will be generated 78A protocol Foo in the namespace bar will cause the sources 79 cppdir/FooParent.cpp, cppdir/FooChild.cpp 80to be generated""", 81) 82 83options, files = op.parse_args() 84_verbosity = options.verbosity 85syncMsgList = options.syncMsgList 86msgMetadata = options.msgMetadata 87headersdir = options.headersdir 88cppdir = options.cppdir 89includedirs = [os.path.abspath(incdir) for incdir in options.includedirs] 90 91if not len(files): 92 op.error("No IPDL files specified") 93 94ipcmessagestartpath = os.path.join(headersdir, "IPCMessageStart.h") 95ipc_msgtype_name_path = os.path.join(cppdir, "IPCMessageTypeName.cpp") 96 97log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir) 98log(2, 'Generated C++ sources will be generated in "%s"', cppdir) 99 100allmessages = {} 101allmessageprognames = [] 102allprotocols = [] 103 104 105def normalizedFilename(f): 106 if f == "-": 107 return "<stdin>" 108 return f 109 110 111log(2, "Reading sync message list") 112parser = RawConfigParser() 113parser.read_file(open(options.syncMsgList)) 114syncMsgList = parser.sections() 115 116for section in syncMsgList: 117 if not parser.get(section, "description"): 118 print("Error: Sync message %s lacks a description" % section, file=sys.stderr) 119 sys.exit(1) 120 121# Read message metadata. Right now we only have 'segment_capacity' 122# for the standard segment size used for serialization. 123log(2, "Reading message metadata...") 124msgMetadataConfig = RawConfigParser() 125msgMetadataConfig.read_file(open(options.msgMetadata)) 126 127segmentCapacityDict = {} 128for msgName in msgMetadataConfig.sections(): 129 if msgMetadataConfig.has_option(msgName, "segment_capacity"): 130 capacity = msgMetadataConfig.get(msgName, "segment_capacity") 131 segmentCapacityDict[msgName] = capacity 132 133# First pass: parse and type-check all protocols 134for f in files: 135 log(2, os.path.basename(f)) 136 filename = normalizedFilename(f) 137 if f == "-": 138 fd = sys.stdin 139 else: 140 fd = open(f) 141 142 specstring = fd.read() 143 fd.close() 144 145 ast = ipdl.parse(specstring, filename, includedirs=includedirs) 146 if ast is None: 147 print("Specification could not be parsed.", file=sys.stderr) 148 sys.exit(1) 149 150 log(2, "checking types") 151 if not ipdl.typecheck(ast): 152 print("Specification is not well typed.", file=sys.stderr) 153 sys.exit(1) 154 155 if not ipdl.checkSyncMessage(ast, syncMsgList): 156 print( 157 "Error: New sync IPC messages must be reviewed by an IPC peer and recorded in %s" 158 % options.syncMsgList, 159 file=sys.stderr, 160 ) # NOQA: E501 161 sys.exit(1) 162 163if not ipdl.checkFixedSyncMessages(parser): 164 # Errors have alraedy been printed to stderr, just exit 165 sys.exit(1) 166 167# Second pass: generate code 168for f in files: 169 # Read from parser cache 170 filename = normalizedFilename(f) 171 ast = ipdl.parse(None, filename, includedirs=includedirs) 172 ipdl.gencxx(filename, ast, headersdir, cppdir, segmentCapacityDict) 173 174 if ast.protocol: 175 allmessages[ast.protocol.name] = ipdl.genmsgenum(ast) 176 allprotocols.append(ast.protocol.name) 177 # e.g. PContent::RequestMemoryReport (not prefixed or suffixed.) 178 for md in ast.protocol.messageDecls: 179 allmessageprognames.append("%s::%s" % (md.namespace, md.decl.progname)) 180 181allprotocols.sort() 182 183# Check if we have undefined message names in segmentCapacityDict. 184# This is a fool-proof of the 'message-metadata.ini' file. 185undefinedMessages = set(segmentCapacityDict.keys()) - set(allmessageprognames) 186if len(undefinedMessages) > 0: 187 print("Error: Undefined message names in message-metadata.ini:", file=sys.stderr) 188 print(undefinedMessages, file=sys.stderr) 189 sys.exit(1) 190 191ipcmsgstart = StringIO() 192 193print( 194 """ 195// CODE GENERATED by ipdl.py. Do not edit. 196 197#ifndef IPCMessageStart_h 198#define IPCMessageStart_h 199 200enum IPCMessageStart { 201""", 202 file=ipcmsgstart, 203) 204 205for name in allprotocols: 206 print(" %sMsgStart," % name, file=ipcmsgstart) 207 208print( 209 """ 210 LastMsgIndex 211}; 212 213static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO"); 214 215#endif // ifndef IPCMessageStart_h 216""", 217 file=ipcmsgstart, 218) 219 220ipc_msgtype_name = StringIO() 221print( 222 """ 223// CODE GENERATED by ipdl.py. Do not edit. 224#include <cstdint> 225 226#include "mozilla/ipc/ProtocolUtils.h" 227#include "IPCMessageStart.h" 228 229using std::uint32_t; 230 231namespace { 232 233enum IPCMessages { 234""", 235 file=ipc_msgtype_name, 236) 237 238for protocol in sorted(allmessages.keys()): 239 for (msg, num) in allmessages[protocol].idnums: 240 if num: 241 print(" %s = %s," % (msg, num), file=ipc_msgtype_name) 242 elif not msg.endswith("End"): 243 print(" %s__%s," % (protocol, msg), file=ipc_msgtype_name) 244 245print( 246 """ 247}; 248 249} // anonymous namespace 250 251namespace IPC { 252 253const char* StringFromIPCMessageType(uint32_t aMessageType) 254{ 255 switch (aMessageType) { 256""", 257 file=ipc_msgtype_name, 258) 259 260for protocol in sorted(allmessages.keys()): 261 for (msg, num) in allmessages[protocol].idnums: 262 if num or msg.endswith("End"): 263 continue 264 print( 265 """ 266 case %s__%s: 267 return "%s::%s";""" 268 % (protocol, msg, protocol, msg), 269 file=ipc_msgtype_name, 270 ) 271 272print( 273 """ 274 case ACCEPT_INVITE_MESSAGE_TYPE: 275 return "ACCEPT_INVITE_MESSAGE"; 276 case REQUEST_INTRODUCTION_MESSAGE_TYPE: 277 return "REQUEST_INTRODUCTION_MESSAGE"; 278 case INTRODUCE_MESSAGE_TYPE: 279 return "INTRODUCE_MESSAGE"; 280 case BROADCAST_MESSAGE_TYPE: 281 return "BROADCAST_MESSAGE"; 282 case EVENT_MESSAGE_TYPE: 283 return "EVENT_MESSAGE"; 284 case IMPENDING_SHUTDOWN_MESSAGE_TYPE: 285 return "IMPENDING_SHUTDOWN"; 286 case BUILD_IDS_MATCH_MESSAGE_TYPE: 287 return "BUILD_IDS_MATCH_MESSAGE"; 288 case BUILD_ID_MESSAGE_TYPE: 289 return "BUILD_ID_MESSAGE"; 290 case CHANNEL_OPENED_MESSAGE_TYPE: 291 return "CHANNEL_OPENED_MESSAGE"; 292 case SHMEM_DESTROYED_MESSAGE_TYPE: 293 return "SHMEM_DESTROYED_MESSAGE"; 294 case SHMEM_CREATED_MESSAGE_TYPE: 295 return "SHMEM_CREATED_MESSAGE"; 296 case GOODBYE_MESSAGE_TYPE: 297 return "GOODBYE_MESSAGE"; 298 case CANCEL_MESSAGE_TYPE: 299 return "CANCEL_MESSAGE"; 300 default: 301 return "<unknown IPC msg name>"; 302 } 303} 304 305} // namespace IPC 306 307namespace mozilla { 308namespace ipc { 309 310const char* ProtocolIdToName(IPCMessageStart aId) { 311 switch (aId) { 312""", 313 file=ipc_msgtype_name, 314) 315 316for name in allprotocols: 317 print(" case %sMsgStart:" % name, file=ipc_msgtype_name) 318 print(' return "%s";' % name, file=ipc_msgtype_name) 319 320print( 321 """ 322 default: 323 return "<unknown protocol id>"; 324 } 325} 326 327} // namespace ipc 328} // namespace mozilla 329""", 330 file=ipc_msgtype_name, 331) 332 333ipdl.writeifmodified(ipcmsgstart.getvalue(), ipcmessagestartpath) 334ipdl.writeifmodified(ipc_msgtype_name.getvalue(), ipc_msgtype_name_path) 335