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 from __future__
import print_function
9 from cStringIO
import StringIO
10 from ConfigParser
import RawConfigParser
15 def log(minv
, fmt
, *args
):
16 if _verbosity
>= minv
:
19 # process command line
22 op
= optparse
.OptionParser(usage
='ipdl.py [options] IPDLfiles...')
23 op
.add_option('-I', '--include', dest
='includedirs', default
=[],
25 help='Additional directory to search for included protocol specifications')
26 op
.add_option('-s', '--sync-msg-list', dest
='syncMsgList', default
='sync-messages.ini',
27 help="Config file listing allowed sync messages")
28 op
.add_option('-m', '--msg-metadata', dest
='msgMetadata', default
='message-metadata.ini',
29 help="Predicted message sizes for reducing serialization malloc overhead.")
30 op
.add_option('-v', '--verbose', dest
='verbosity', default
=1, action
='count',
31 help='Verbose logging (specify -vv or -vvv for very verbose logging)')
32 op
.add_option('-q', '--quiet', dest
='verbosity', action
='store_const', const
=0,
33 help="Suppress logging output")
34 op
.add_option('-d', '--outheaders-dir', dest
='headersdir', default
='.',
35 help="""Directory into which C++ headers will be generated.
36 A protocol Foo in the namespace bar will cause the headers
37 dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h
39 op
.add_option('-o', '--outcpp-dir', dest
='cppdir', default
='.',
40 help="""Directory into which C++ sources will be generated
41 A protocol Foo in the namespace bar will cause the sources
42 cppdir/FooParent.cpp, cppdir/FooChild.cpp
45 options
, files
= op
.parse_args()
46 _verbosity
= options
.verbosity
47 syncMsgList
= options
.syncMsgList
48 msgMetadata
= options
.msgMetadata
49 headersdir
= options
.headersdir
50 cppdir
= options
.cppdir
51 includedirs
= [os
.path
.abspath(incdir
) for incdir
in options
.includedirs
]
54 op
.error("No IPDL files specified")
56 ipcmessagestartpath
= os
.path
.join(headersdir
, 'IPCMessageStart.h')
57 ipc_msgtype_name_path
= os
.path
.join(cppdir
, 'IPCMessageTypeName.cpp')
59 log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir
)
60 log(2, 'Generated C++ sources will be generated in "%s"', cppdir
)
63 allmessageprognames
= []
67 def normalizedFilename(f
):
73 log(2, 'Reading sync message list')
74 parser
= RawConfigParser()
75 parser
.readfp(open(options
.syncMsgList
))
76 syncMsgList
= parser
.sections()
78 for section
in syncMsgList
:
79 if not parser
.get(section
, "description"):
80 print('Error: Sync message %s lacks a description' % section
, file=sys
.stderr
)
83 # Read message metadata. Right now we only have 'segment_capacity'
84 # for the standard segment size used for serialization.
85 log(2, 'Reading message metadata...')
86 msgMetadataConfig
= RawConfigParser()
87 msgMetadataConfig
.readfp(open(options
.msgMetadata
))
89 segmentCapacityDict
= {}
90 for msgName
in msgMetadataConfig
.sections():
91 if msgMetadataConfig
.has_option(msgName
, 'segment_capacity'):
92 capacity
= msgMetadataConfig
.get(msgName
, 'segment_capacity')
93 segmentCapacityDict
[msgName
] = capacity
95 # First pass: parse and type-check all protocols
97 log(2, os
.path
.basename(f
))
98 filename
= normalizedFilename(f
)
104 specstring
= fd
.read()
107 ast
= ipdl
.parse(specstring
, filename
, includedirs
=includedirs
)
109 print('Specification could not be parsed.', file=sys
.stderr
)
112 log(2, 'checking types')
113 if not ipdl
.typecheck(ast
):
114 print('Specification is not well typed.', file=sys
.stderr
)
117 if not ipdl
.checkSyncMessage(ast
, syncMsgList
):
118 print('Error: New sync IPC messages must be reviewed by an IPC peer and recorded in %s' % options
.syncMsgList
, file=sys
.stderr
) # NOQA: E501
121 if not ipdl
.checkFixedSyncMessages(parser
):
122 # Errors have alraedy been printed to stderr, just exit
125 # Second pass: generate code
127 # Read from parser cache
128 filename
= normalizedFilename(f
)
129 ast
= ipdl
.parse(None, filename
, includedirs
=includedirs
)
130 ipdl
.gencxx(filename
, ast
, headersdir
, cppdir
, segmentCapacityDict
)
133 allmessages
[ast
.protocol
.name
] = ipdl
.genmsgenum(ast
)
134 allprotocols
.append(ast
.protocol
.name
)
135 # e.g. PContent::RequestMemoryReport (not prefixed or suffixed.)
136 for md
in ast
.protocol
.messageDecls
:
137 allmessageprognames
.append('%s::%s' % (md
.namespace
, md
.decl
.progname
))
141 # Check if we have undefined message names in segmentCapacityDict.
142 # This is a fool-proof of the 'message-metadata.ini' file.
143 undefinedMessages
= set(segmentCapacityDict
.keys()) - set(allmessageprognames
)
144 if len(undefinedMessages
) > 0:
145 print('Error: Undefined message names in message-metadata.ini:', file=sys
.stderr
)
146 print(undefinedMessages
, file=sys
.stderr
)
149 ipcmsgstart
= StringIO()
152 // CODE GENERATED by ipdl.py. Do not edit.
154 #ifndef IPCMessageStart_h
155 #define IPCMessageStart_h
157 enum IPCMessageStart {
158 """, file=ipcmsgstart
)
160 for name
in allprotocols
:
161 print(" %sMsgStart," % name
, file=ipcmsgstart
)
167 static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO");
169 #endif // ifndef IPCMessageStart_h
170 """, file=ipcmsgstart
)
172 ipc_msgtype_name
= StringIO()
174 // CODE GENERATED by ipdl.py. Do not edit.
177 #include "mozilla/ipc/ProtocolUtils.h"
178 #include "IPCMessageStart.h"
185 """, file=ipc_msgtype_name
)
187 for protocol
in sorted(allmessages
.keys()):
188 for (msg
, num
) in allmessages
[protocol
].idnums
:
190 print(" %s = %s," % (msg
, num
), file=ipc_msgtype_name
)
191 elif not msg
.endswith('End'):
192 print(" %s__%s," % (protocol
, msg
), file=ipc_msgtype_name
)
197 } // anonymous namespace
201 const char* StringFromIPCMessageType(uint32_t aMessageType)
203 switch (aMessageType) {
204 """, file=ipc_msgtype_name
)
206 for protocol
in sorted(allmessages
.keys()):
207 for (msg
, num
) in allmessages
[protocol
].idnums
:
208 if num
or msg
.endswith('End'):
212 return "%s::%s";""" % (protocol
, msg
, protocol
, msg
), file=ipc_msgtype_name
)
215 case CHANNEL_OPENED_MESSAGE_TYPE:
216 return "CHANNEL_OPENED_MESSAGE";
217 case SHMEM_DESTROYED_MESSAGE_TYPE:
218 return "SHMEM_DESTROYED_MESSAGE";
219 case SHMEM_CREATED_MESSAGE_TYPE:
220 return "SHMEM_CREATED_MESSAGE";
221 case GOODBYE_MESSAGE_TYPE:
222 return "GOODBYE_MESSAGE";
223 case CANCEL_MESSAGE_TYPE:
224 return "CANCEL_MESSAGE";
226 return "<unknown IPC msg name>";
235 const char* ProtocolIdToName(IPCMessageStart aId) {
237 """, file=ipc_msgtype_name
)
239 for name
in allprotocols
:
240 print(" case %sMsgStart:" % name
, file=ipc_msgtype_name
)
241 print(" return \"%s\";" % name
, file=ipc_msgtype_name
)
245 return "<unknown protocol id>";
250 } // namespace mozilla
251 """, file=ipc_msgtype_name
)
253 ipdl
.writeifmodified(ipcmsgstart
.getvalue(), ipcmessagestartpath
)
254 ipdl
.writeifmodified(ipc_msgtype_name
.getvalue(), ipc_msgtype_name_path
)