blob: f4c8eac8f6cad66ed7932f454360ab71720bd6ca [file] [log] [blame]
Michael Roth0f923be2011-07-19 14:50:39 -05001#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
Eric Blakee4083112016-01-29 06:48:41 -07005# Copyright (c) 2013-2016 Red Hat Inc.
Michael Roth0f923be2011-07-19 14:50:39 -05006#
7# Authors:
8# Anthony Liguori <aliguori@us.ibm.com>
Markus Armbrusterc7a3f252013-07-27 17:41:55 +02009# Markus Armbruster <armbru@redhat.com>
Michael Roth0f923be2011-07-19 14:50:39 -050010#
Markus Armbruster678e48a2014-03-01 08:40:34 +010011# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
Michael Roth0f923be2011-07-19 14:50:39 -050013
Lluís Vilanovaa719a272014-05-07 20:46:15 +020014import re
Michael Roth0f923be2011-07-19 14:50:39 -050015from ordereddict import OrderedDict
Markus Armbruster12f8e1b2015-04-02 14:46:39 +020016import errno
Markus Armbruster2114f5a2015-04-02 13:12:21 +020017import getopt
Lluís Vilanova33aaad52014-05-02 15:52:35 +020018import os
Markus Armbruster2caba362013-07-27 17:41:56 +020019import sys
Markus Armbruster47299262015-05-14 06:50:47 -060020import string
Michael Roth0f923be2011-07-19 14:50:39 -050021
Eric Blakeb52c4b92015-05-04 09:05:00 -060022builtin_types = {
Kevin Wolf69dd62d2013-07-08 16:14:21 +020023 'str': 'QTYPE_QSTRING',
24 'int': 'QTYPE_QINT',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
27 'int8': 'QTYPE_QINT',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
Eric Blakecb17f792015-05-04 09:05:01 -060035 'size': 'QTYPE_QINT',
Eric Blake1310a3d2015-12-01 22:20:46 -070036 'any': None, # any QType possible, actually
Eric Blake7264f5c2015-12-01 22:20:47 -070037 'QType': 'QTYPE_QSTRING',
Kevin Wolf69dd62d2013-07-08 16:14:21 +020038}
39
Markus Armbrusterbc52d032017-03-15 13:56:51 +010040# Are documentation comments required?
41doc_required = False
42
Eric Blake10d4d992015-05-04 09:05:23 -060043# Whitelist of commands allowed to return a non-dictionary
Markus Armbruster1554a8f2017-03-15 13:56:54 +010044returns_whitelist = []
Eric Blake10d4d992015-05-04 09:05:23 -060045
Eric Blake893e1f22015-12-01 22:20:57 -070046# Whitelist of entities allowed to violate case conventions
Markus Armbruster2cfbae32017-03-15 13:56:55 +010047name_case_whitelist = []
Eric Blake893e1f22015-12-01 22:20:57 -070048
Eric Blake4dc2e692015-05-04 09:05:17 -060049enum_types = []
50struct_types = []
51union_types = []
52events = []
53all_names = {}
54
Markus Armbruster00e4b282015-06-10 10:04:36 +020055#
56# Parsing the schema into expressions
57#
58
Eric Blake437db252015-09-29 16:21:02 -060059
Lluís Vilanovaa719a272014-05-07 20:46:15 +020060def error_path(parent):
61 res = ""
62 while parent:
63 res = ("In file included from %s:%d:\n" % (parent['file'],
64 parent['line'])) + res
65 parent = parent['parent']
66 return res
67
Eric Blake437db252015-09-29 16:21:02 -060068
Marc-André Lureau4148c292017-01-13 15:41:25 +010069class QAPIError(Exception):
70 def __init__(self, fname, line, col, incl_info, msg):
Eric Blake59b00542015-09-29 16:21:01 -060071 Exception.__init__(self)
Marc-André Lureau4148c292017-01-13 15:41:25 +010072 self.fname = fname
73 self.line = line
74 self.col = col
75 self.info = incl_info
Markus Armbruster2caba362013-07-27 17:41:56 +020076 self.msg = msg
Marc-André Lureau4148c292017-01-13 15:41:25 +010077
78 def __str__(self):
79 loc = "%s:%d" % (self.fname, self.line)
80 if self.col is not None:
81 loc += ":%s" % self.col
82 return error_path(self.info) + "%s: %s" % (loc, self.msg)
83
84
85class QAPIParseError(QAPIError):
86 def __init__(self, parser, msg):
87 col = 1
88 for ch in parser.src[parser.line_pos:parser.pos]:
Wenchao Xia515b9432014-03-04 18:44:33 -080089 if ch == '\t':
Marc-André Lureau4148c292017-01-13 15:41:25 +010090 col = (col + 7) % 8 + 1
Markus Armbruster2caba362013-07-27 17:41:56 +020091 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +010092 col += 1
93 QAPIError.__init__(self, parser.fname, parser.line, col,
94 parser.incl_info, msg)
Markus Armbruster2caba362013-07-27 17:41:56 +020095
Eric Blake437db252015-09-29 16:21:02 -060096
Marc-André Lureau4148c292017-01-13 15:41:25 +010097class QAPISemError(QAPIError):
98 def __init__(self, info, msg):
99 QAPIError.__init__(self, info['file'], info['line'], None,
100 info['parent'], msg)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800101
Eric Blake437db252015-09-29 16:21:02 -0600102
Marc-André Lureau3313b612017-01-13 15:41:29 +0100103class QAPIDoc(object):
104 class Section(object):
105 def __init__(self, name=None):
106 # optional section name (argument/member or section name)
107 self.name = name
108 # the list of lines for this section
109 self.content = []
Markus Armbrusterb116fd82017-03-15 13:57:00 +0100110 self.optional = False
Marc-André Lureau3313b612017-01-13 15:41:29 +0100111
112 def append(self, line):
113 self.content.append(line)
114
115 def __repr__(self):
116 return "\n".join(self.content).strip()
117
118 class ArgSection(Section):
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100119 def __init__(self, name):
120 QAPIDoc.Section.__init__(self, name)
121 self.member = None
122
123 def connect(self, member):
124 self.member = member
Marc-André Lureau3313b612017-01-13 15:41:29 +0100125
126 def __init__(self, parser, info):
127 # self.parser is used to report errors with QAPIParseError. The
128 # resulting error position depends on the state of the parser.
129 # It happens to be the beginning of the comment. More or less
130 # servicable, but action at a distance.
131 self.parser = parser
132 self.info = info
133 self.symbol = None
134 self.body = QAPIDoc.Section()
135 # dict mapping parameter name to ArgSection
136 self.args = OrderedDict()
137 # a list of Section
138 self.sections = []
139 # the current section
140 self.section = self.body
141 # associated expression (to be set by expression parser)
142 self.expr = None
143
144 def has_section(self, name):
145 """Return True if we have a section with this name."""
146 for i in self.sections:
147 if i.name == name:
148 return True
149 return False
150
151 def append(self, line):
152 """Parse a comment line and add it to the documentation."""
153 line = line[1:]
154 if not line:
155 self._append_freeform(line)
156 return
157
158 if line[0] != ' ':
159 raise QAPIParseError(self.parser, "Missing space after #")
160 line = line[1:]
161
162 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
163 # recognized, and get silently treated as ordinary text
164 if self.symbol:
165 self._append_symbol_line(line)
166 elif not self.body.content and line.startswith("@"):
167 if not line.endswith(":"):
168 raise QAPIParseError(self.parser, "Line should end with :")
169 self.symbol = line[1:-1]
170 # FIXME invalid names other than the empty string aren't flagged
171 if not self.symbol:
172 raise QAPIParseError(self.parser, "Invalid name")
173 else:
174 self._append_freeform(line)
175
176 def _append_symbol_line(self, line):
177 name = line.split(' ', 1)[0]
178
179 if name.startswith("@") and name.endswith(":"):
180 line = line[len(name)+1:]
181 self._start_args_section(name[1:-1])
182 elif name in ("Returns:", "Since:",
183 # those are often singular or plural
184 "Note:", "Notes:",
185 "Example:", "Examples:",
186 "TODO:"):
187 line = line[len(name)+1:]
188 self._start_section(name[:-1])
189
190 self._append_freeform(line)
191
192 def _start_args_section(self, name):
193 # FIXME invalid names other than the empty string aren't flagged
194 if not name:
195 raise QAPIParseError(self.parser, "Invalid parameter name")
196 if name in self.args:
197 raise QAPIParseError(self.parser,
198 "'%s' parameter name duplicated" % name)
199 if self.sections:
200 raise QAPIParseError(self.parser,
201 "'@%s:' can't follow '%s' section"
202 % (name, self.sections[0].name))
203 self.section = QAPIDoc.ArgSection(name)
204 self.args[name] = self.section
205
206 def _start_section(self, name=""):
207 if name in ("Returns", "Since") and self.has_section(name):
208 raise QAPIParseError(self.parser,
209 "Duplicated '%s' section" % name)
210 self.section = QAPIDoc.Section(name)
211 self.sections.append(self.section)
212
213 def _append_freeform(self, line):
214 in_arg = isinstance(self.section, QAPIDoc.ArgSection)
215 if (in_arg and self.section.content
216 and not self.section.content[-1]
217 and line and not line[0].isspace()):
218 self._start_section()
219 if (in_arg or not self.section.name
220 or not self.section.name.startswith("Example")):
221 line = line.strip()
222 self.section.append(line)
223
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100224 def connect_member(self, member):
225 if member.name not in self.args:
226 # Undocumented TODO outlaw
Markus Armbruster860e8772017-03-15 13:57:04 +0100227 self.args[member.name] = QAPIDoc.ArgSection(member.name)
228 self.args[member.name].connect(member)
Markus Armbruster069fb5b2017-03-15 13:57:03 +0100229
Marc-André Lureau3313b612017-01-13 15:41:29 +0100230
Markus Armbrustera4bcb202015-09-16 13:06:04 +0200231class QAPISchemaParser(object):
Michael Roth0f923be2011-07-19 14:50:39 -0500232
Eric Blake437db252015-09-29 16:21:02 -0600233 def __init__(self, fp, previously_included=[], incl_info=None):
Markus Armbruster54414042015-06-09 16:22:45 +0200234 abs_fname = os.path.abspath(fp.name)
Markus Armbruster8608d252015-06-09 18:32:29 +0200235 fname = fp.name
Markus Armbruster54414042015-06-09 16:22:45 +0200236 self.fname = fname
Markus Armbruster54414042015-06-09 16:22:45 +0200237 previously_included.append(abs_fname)
238 self.incl_info = incl_info
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200239 self.src = fp.read()
240 if self.src == '' or self.src[-1] != '\n':
241 self.src += '\n'
242 self.cursor = 0
Wenchao Xia515b9432014-03-04 18:44:33 -0800243 self.line = 1
244 self.line_pos = 0
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200245 self.exprs = []
Marc-André Lureau3313b612017-01-13 15:41:29 +0100246 self.docs = []
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200247 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500248
Eric Blake437db252015-09-29 16:21:02 -0600249 while self.tok is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100250 info = {'file': fname, 'line': self.line,
251 'parent': self.incl_info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100252 if self.tok == '#':
253 doc = self.get_doc(info)
254 self.docs.append(doc)
255 continue
256
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200257 expr = self.get_expr(False)
Markus Armbrustere04dea82017-03-15 13:56:50 +0100258 if 'include' in expr:
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200259 if len(expr) != 1:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100260 raise QAPISemError(info, "Invalid 'include' directive")
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200261 include = expr["include"]
262 if not isinstance(include, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100263 raise QAPISemError(info,
264 "Value of 'include' must be a string")
Markus Armbrustere04dea82017-03-15 13:56:50 +0100265 self._include(include, info, os.path.dirname(abs_fname),
266 previously_included)
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100267 elif "pragma" in expr:
268 if len(expr) != 1:
269 raise QAPISemError(info, "Invalid 'pragma' directive")
270 pragma = expr['pragma']
271 if not isinstance(pragma, dict):
272 raise QAPISemError(
273 info, "Value of 'pragma' must be a dictionary")
274 for name, value in pragma.iteritems():
275 self._pragma(name, value, info)
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200276 else:
277 expr_elem = {'expr': expr,
Marc-André Lureau4148c292017-01-13 15:41:25 +0100278 'info': info}
Marc-André Lureau3313b612017-01-13 15:41:29 +0100279 if (self.docs
280 and self.docs[-1].info['file'] == fname
281 and not self.docs[-1].expr):
282 self.docs[-1].expr = expr
283 expr_elem['doc'] = self.docs[-1]
284
Lluís Vilanovaa719a272014-05-07 20:46:15 +0200285 self.exprs.append(expr_elem)
Michael Roth0f923be2011-07-19 14:50:39 -0500286
Markus Armbrustere04dea82017-03-15 13:56:50 +0100287 def _include(self, include, info, base_dir, previously_included):
288 incl_abs_fname = os.path.join(base_dir, include)
289 # catch inclusion cycle
290 inf = info
291 while inf:
292 if incl_abs_fname == os.path.abspath(inf['file']):
293 raise QAPISemError(info, "Inclusion loop for %s" % include)
294 inf = inf['parent']
295
296 # skip multiple include of the same file
297 if incl_abs_fname in previously_included:
298 return
299 try:
300 fobj = open(incl_abs_fname, 'r')
301 except IOError as e:
302 raise QAPISemError(info, '%s: %s' % (e.strerror, include))
303 exprs_include = QAPISchemaParser(fobj, previously_included, info)
304 self.exprs.extend(exprs_include.exprs)
305 self.docs.extend(exprs_include.docs)
306
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100307 def _pragma(self, name, value, info):
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100308 global doc_required, returns_whitelist, name_case_whitelist
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100309 if name == 'doc-required':
310 if not isinstance(value, bool):
311 raise QAPISemError(info,
312 "Pragma 'doc-required' must be boolean")
313 doc_required = value
Markus Armbruster1554a8f2017-03-15 13:56:54 +0100314 elif name == 'returns-whitelist':
315 if (not isinstance(value, list)
316 or any([not isinstance(elt, str) for elt in value])):
317 raise QAPISemError(info,
318 "Pragma returns-whitelist must be"
319 " a list of strings")
320 returns_whitelist = value
Markus Armbruster2cfbae32017-03-15 13:56:55 +0100321 elif name == 'name-case-whitelist':
322 if (not isinstance(value, list)
323 or any([not isinstance(elt, str) for elt in value])):
324 raise QAPISemError(info,
325 "Pragma name-case-whitelist must be"
326 " a list of strings")
327 name_case_whitelist = value
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100328 else:
329 raise QAPISemError(info, "Unknown pragma '%s'" % name)
330
Marc-André Lureau3313b612017-01-13 15:41:29 +0100331 def accept(self, skip_comment=True):
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200332 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200333 self.tok = self.src[self.cursor]
Markus Armbruster2caba362013-07-27 17:41:56 +0200334 self.pos = self.cursor
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200335 self.cursor += 1
336 self.val = None
Michael Roth0f923be2011-07-19 14:50:39 -0500337
Markus Armbrusterf1a145e2013-07-27 17:42:01 +0200338 if self.tok == '#':
Marc-André Lureau3313b612017-01-13 15:41:29 +0100339 if self.src[self.cursor] == '#':
340 # Start of doc comment
341 skip_comment = False
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200342 self.cursor = self.src.find('\n', self.cursor)
Marc-André Lureau3313b612017-01-13 15:41:29 +0100343 if not skip_comment:
344 self.val = self.src[self.pos:self.cursor]
345 return
Eric Blake8712fa52015-10-26 16:34:41 -0600346 elif self.tok in "{}:,[]":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200347 return
348 elif self.tok == "'":
349 string = ''
350 esc = False
351 while True:
352 ch = self.src[self.cursor]
353 self.cursor += 1
354 if ch == '\n':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100355 raise QAPIParseError(self, 'Missing terminating "\'"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200356 if esc:
Eric Blakea7f59662015-05-04 09:05:36 -0600357 if ch == 'b':
358 string += '\b'
359 elif ch == 'f':
360 string += '\f'
361 elif ch == 'n':
362 string += '\n'
363 elif ch == 'r':
364 string += '\r'
365 elif ch == 't':
366 string += '\t'
367 elif ch == 'u':
368 value = 0
Eric Blake437db252015-09-29 16:21:02 -0600369 for _ in range(0, 4):
Eric Blakea7f59662015-05-04 09:05:36 -0600370 ch = self.src[self.cursor]
371 self.cursor += 1
372 if ch not in "0123456789abcdefABCDEF":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100373 raise QAPIParseError(self,
374 '\\u escape needs 4 '
375 'hex digits')
Eric Blakea7f59662015-05-04 09:05:36 -0600376 value = (value << 4) + int(ch, 16)
377 # If Python 2 and 3 didn't disagree so much on
378 # how to handle Unicode, then we could allow
379 # Unicode string defaults. But most of QAPI is
380 # ASCII-only, so we aren't losing much for now.
381 if not value or value > 0x7f:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100382 raise QAPIParseError(self,
383 'For now, \\u escape '
384 'only supports non-zero '
385 'values up to \\u007f')
Eric Blakea7f59662015-05-04 09:05:36 -0600386 string += chr(value)
387 elif ch in "\\/'\"":
388 string += ch
389 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100390 raise QAPIParseError(self,
391 "Unknown escape \\%s" % ch)
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200392 esc = False
393 elif ch == "\\":
394 esc = True
395 elif ch == "'":
396 self.val = string
397 return
398 else:
399 string += ch
Markus Armbrustere565d932015-06-10 08:24:58 +0200400 elif self.src.startswith("true", self.pos):
401 self.val = True
402 self.cursor += 3
403 return
404 elif self.src.startswith("false", self.pos):
405 self.val = False
406 self.cursor += 4
407 return
408 elif self.src.startswith("null", self.pos):
409 self.val = None
410 self.cursor += 3
411 return
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200412 elif self.tok == '\n':
413 if self.cursor == len(self.src):
414 self.tok = None
415 return
Wenchao Xia515b9432014-03-04 18:44:33 -0800416 self.line += 1
417 self.line_pos = self.cursor
Markus Armbruster9213aa52013-07-27 17:41:57 +0200418 elif not self.tok.isspace():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100419 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
Michael Roth0f923be2011-07-19 14:50:39 -0500420
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200421 def get_members(self):
422 expr = OrderedDict()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200423 if self.tok == '}':
424 self.accept()
425 return expr
426 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100427 raise QAPIParseError(self, 'Expected string or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200428 while True:
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200429 key = self.val
430 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200431 if self.tok != ':':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100432 raise QAPIParseError(self, 'Expected ":"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200433 self.accept()
Wenchao Xia4b359912014-03-04 18:44:32 -0800434 if key in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100435 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200436 expr[key] = self.get_expr(True)
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200437 if self.tok == '}':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200438 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200439 return expr
440 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100441 raise QAPIParseError(self, 'Expected "," or "}"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200442 self.accept()
443 if self.tok != "'":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100444 raise QAPIParseError(self, 'Expected string')
Michael Roth0f923be2011-07-19 14:50:39 -0500445
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200446 def get_values(self):
447 expr = []
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200448 if self.tok == ']':
449 self.accept()
450 return expr
Eric Blake437db252015-09-29 16:21:02 -0600451 if self.tok not in "{['tfn":
Marc-André Lureau4148c292017-01-13 15:41:25 +0100452 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
453 'boolean or "null"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200454 while True:
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200455 expr.append(self.get_expr(True))
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200456 if self.tok == ']':
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200457 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200458 return expr
459 if self.tok != ',':
Marc-André Lureau4148c292017-01-13 15:41:25 +0100460 raise QAPIParseError(self, 'Expected "," or "]"')
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200461 self.accept()
Michael Roth0f923be2011-07-19 14:50:39 -0500462
Markus Armbruster5f3cd2b2013-07-27 17:41:59 +0200463 def get_expr(self, nested):
464 if self.tok != '{' and not nested:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100465 raise QAPIParseError(self, 'Expected "{"')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200466 if self.tok == '{':
467 self.accept()
468 expr = self.get_members()
469 elif self.tok == '[':
470 self.accept()
471 expr = self.get_values()
Fam Zhenge53188a2015-05-04 09:05:18 -0600472 elif self.tok in "'tfn":
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200473 expr = self.val
474 self.accept()
Markus Armbruster6974ccd2013-07-27 17:41:58 +0200475 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100476 raise QAPIParseError(self, 'Expected "{", "[" or string')
Markus Armbrusterc7a3f252013-07-27 17:41:55 +0200477 return expr
Kevin Wolfbd9927f2013-07-01 16:31:50 +0200478
Marc-André Lureau3313b612017-01-13 15:41:29 +0100479 def get_doc(self, info):
480 if self.val != '##':
481 raise QAPIParseError(self, "Junk after '##' at start of "
482 "documentation comment")
483
484 doc = QAPIDoc(self, info)
485 self.accept(False)
486 while self.tok == '#':
487 if self.val.startswith('##'):
488 # End of doc comment
489 if self.val != '##':
490 raise QAPIParseError(self, "Junk after '##' at end of "
491 "documentation comment")
492 self.accept()
493 return doc
494 else:
495 doc.append(self.val)
496 self.accept(False)
497
498 raise QAPIParseError(self, "Documentation comment must end with '##'")
499
500
Markus Armbruster00e4b282015-06-10 10:04:36 +0200501#
502# Semantic analysis of schema expressions
Markus Armbrusterac882192015-09-16 13:06:05 +0200503# TODO fold into QAPISchema
504# TODO catching name collisions in generated code would be nice
Markus Armbruster00e4b282015-06-10 10:04:36 +0200505#
506
Eric Blake437db252015-09-29 16:21:02 -0600507
Eric Blake14f00c62016-03-03 09:16:43 -0700508def find_base_members(base):
Eric Blakeac4338f2016-03-17 16:48:39 -0600509 if isinstance(base, dict):
510 return base
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800511 base_struct_define = find_struct(base)
512 if not base_struct_define:
513 return None
514 return base_struct_define['data']
515
Eric Blake437db252015-09-29 16:21:02 -0600516
Eric Blake811d04f2015-05-04 09:05:10 -0600517# Return the qtype of an alternate branch, or None on error.
518def find_alternate_member_qtype(qapi_type):
Eric Blake437db252015-09-29 16:21:02 -0600519 if qapi_type in builtin_types:
Eric Blake44bd1272015-05-04 09:05:08 -0600520 return builtin_types[qapi_type]
521 elif find_struct(qapi_type):
522 return "QTYPE_QDICT"
523 elif find_enum(qapi_type):
524 return "QTYPE_QSTRING"
Eric Blake811d04f2015-05-04 09:05:10 -0600525 elif find_union(qapi_type):
526 return "QTYPE_QDICT"
Eric Blake44bd1272015-05-04 09:05:08 -0600527 return None
528
Eric Blake437db252015-09-29 16:21:02 -0600529
Wenchao Xiabceae762014-03-06 17:08:56 -0800530# Return the discriminator enum define if discriminator is specified as an
531# enum type, otherwise return None.
532def discriminator_find_enum_define(expr):
533 base = expr.get('base')
534 discriminator = expr.get('discriminator')
535
536 if not (discriminator and base):
537 return None
538
Eric Blake14f00c62016-03-03 09:16:43 -0700539 base_members = find_base_members(base)
540 if not base_members:
Wenchao Xiabceae762014-03-06 17:08:56 -0800541 return None
542
Eric Blake14f00c62016-03-03 09:16:43 -0700543 discriminator_type = base_members.get(discriminator)
Wenchao Xiabceae762014-03-06 17:08:56 -0800544 if not discriminator_type:
545 return None
546
547 return find_enum(discriminator_type)
548
Eric Blake437db252015-09-29 16:21:02 -0600549
Eric Blake59a92fe2015-11-18 01:52:56 -0700550# Names must be letters, numbers, -, and _. They must start with letter,
551# except for downstream extensions which must start with __RFQDN_.
552# Dots are only valid in the downstream extension prefix.
553valid_name = re.compile('^(__[a-zA-Z0-9.-]+_)?'
554 '[a-zA-Z][a-zA-Z0-9_-]*$')
Eric Blake437db252015-09-29 16:21:02 -0600555
556
Marc-André Lureau4148c292017-01-13 15:41:25 +0100557def check_name(info, source, name, allow_optional=False,
Eric Blake437db252015-09-29 16:21:02 -0600558 enum_member=False):
Eric Blakec9e0a792015-05-04 09:05:22 -0600559 global valid_name
560 membername = name
561
562 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100563 raise QAPISemError(info, "%s requires a string name" % source)
Eric Blakec9e0a792015-05-04 09:05:22 -0600564 if name.startswith('*'):
565 membername = name[1:]
566 if not allow_optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100567 raise QAPISemError(info, "%s does not allow optional name '%s'"
568 % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600569 # Enum members can start with a digit, because the generated C
570 # code always prefixes it with the enum name
Eric Blake59a92fe2015-11-18 01:52:56 -0700571 if enum_member and membername[0].isdigit():
572 membername = 'D' + membername
Eric Blake75996972016-03-17 16:48:29 -0600573 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
574 # and 'q_obj_*' implicit type names.
Eric Blake9fb081e2015-10-26 16:34:44 -0600575 if not valid_name.match(membername) or \
576 c_name(membername, False).startswith('q_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100577 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
Eric Blakec9e0a792015-05-04 09:05:22 -0600578
Eric Blake437db252015-09-29 16:21:02 -0600579
580def add_name(name, info, meta, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200581 global all_names
582 check_name(info, "'%s'" % meta, name)
Markus Armbrusterd90675f2015-07-31 11:33:52 +0200583 # FIXME should reject names that differ only in '_' vs. '.'
584 # vs. '-', because they're liable to clash in generated C.
Markus Armbruster00e4b282015-06-10 10:04:36 +0200585 if name in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100586 raise QAPISemError(info, "%s '%s' is already defined"
587 % (all_names[name], name))
Eric Blake255960d2015-10-26 16:34:43 -0600588 if not implicit and (name.endswith('Kind') or name.endswith('List')):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100589 raise QAPISemError(info, "%s '%s' should not end in '%s'"
590 % (meta, name, name[-4:]))
Markus Armbruster00e4b282015-06-10 10:04:36 +0200591 all_names[name] = meta
592
Eric Blake437db252015-09-29 16:21:02 -0600593
Markus Armbruster00e4b282015-06-10 10:04:36 +0200594def add_struct(definition, info):
595 global struct_types
596 name = definition['struct']
597 add_name(name, info, 'struct')
598 struct_types.append(definition)
599
Eric Blake437db252015-09-29 16:21:02 -0600600
Markus Armbruster00e4b282015-06-10 10:04:36 +0200601def find_struct(name):
602 global struct_types
603 for struct in struct_types:
604 if struct['struct'] == name:
605 return struct
606 return None
607
Eric Blake437db252015-09-29 16:21:02 -0600608
Markus Armbruster00e4b282015-06-10 10:04:36 +0200609def add_union(definition, info):
610 global union_types
611 name = definition['union']
612 add_name(name, info, 'union')
613 union_types.append(definition)
614
Eric Blake437db252015-09-29 16:21:02 -0600615
Markus Armbruster00e4b282015-06-10 10:04:36 +0200616def find_union(name):
617 global union_types
618 for union in union_types:
619 if union['union'] == name:
620 return union
621 return None
622
Eric Blake437db252015-09-29 16:21:02 -0600623
624def add_enum(name, info, enum_values=None, implicit=False):
Markus Armbruster00e4b282015-06-10 10:04:36 +0200625 global enum_types
626 add_name(name, info, 'enum', implicit)
627 enum_types.append({"enum_name": name, "enum_values": enum_values})
628
Eric Blake437db252015-09-29 16:21:02 -0600629
Markus Armbruster00e4b282015-06-10 10:04:36 +0200630def find_enum(name):
631 global enum_types
632 for enum in enum_types:
633 if enum['enum_name'] == name:
634 return enum
635 return None
636
Markus Armbruster00e4b282015-06-10 10:04:36 +0200637
Eric Blake437db252015-09-29 16:21:02 -0600638def is_enum(name):
639 return find_enum(name) is not None
640
641
Marc-André Lureau4148c292017-01-13 15:41:25 +0100642def check_type(info, source, value, allow_array=False,
Eric Blake437db252015-09-29 16:21:02 -0600643 allow_dict=False, allow_optional=False,
644 allow_metas=[]):
Eric Blakedd883c62015-05-04 09:05:21 -0600645 global all_names
Eric Blakedd883c62015-05-04 09:05:21 -0600646
647 if value is None:
648 return
649
Eric Blakedd883c62015-05-04 09:05:21 -0600650 # Check if array type for value is okay
651 if isinstance(value, list):
652 if not allow_array:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100653 raise QAPISemError(info, "%s cannot be an array" % source)
Eric Blakedd883c62015-05-04 09:05:21 -0600654 if len(value) != 1 or not isinstance(value[0], str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100655 raise QAPISemError(info,
656 "%s: array type must contain single type name" %
657 source)
Eric Blakedd883c62015-05-04 09:05:21 -0600658 value = value[0]
Eric Blakedd883c62015-05-04 09:05:21 -0600659
660 # Check if type name for value is okay
661 if isinstance(value, str):
Eric Blake437db252015-09-29 16:21:02 -0600662 if value not in all_names:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100663 raise QAPISemError(info, "%s uses unknown type '%s'"
664 % (source, value))
Eric Blakedd883c62015-05-04 09:05:21 -0600665 if not all_names[value] in allow_metas:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100666 raise QAPISemError(info, "%s cannot use %s type '%s'" %
667 (source, all_names[value], value))
Eric Blakedd883c62015-05-04 09:05:21 -0600668 return
669
Eric Blakedd883c62015-05-04 09:05:21 -0600670 if not allow_dict:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100671 raise QAPISemError(info, "%s should be a type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200672
673 if not isinstance(value, OrderedDict):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100674 raise QAPISemError(info,
675 "%s should be a dictionary or type name" % source)
Markus Armbrusterc6b71e52015-08-31 17:28:52 +0200676
677 # value is a dictionary, check that each member is okay
Eric Blakedd883c62015-05-04 09:05:21 -0600678 for (key, arg) in value.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100679 check_name(info, "Member of %s" % source, key,
Eric Blakec9e0a792015-05-04 09:05:22 -0600680 allow_optional=allow_optional)
Eric Blake5e59baf2015-10-26 16:35:02 -0600681 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100682 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
683 % (source, key))
Eric Blake6b5abc72015-05-04 09:05:33 -0600684 # Todo: allow dictionaries to represent default values of
685 # an optional argument.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100686 check_type(info, "Member '%s' of %s" % (key, source), arg,
Markus Armbruster2d212912015-09-16 13:06:27 +0200687 allow_array=True,
Eric Blakedd883c62015-05-04 09:05:21 -0600688 allow_metas=['built-in', 'union', 'alternate', 'struct',
Eric Blake6b5abc72015-05-04 09:05:33 -0600689 'enum'])
Eric Blakedd883c62015-05-04 09:05:21 -0600690
Eric Blake437db252015-09-29 16:21:02 -0600691
Marc-André Lureau4148c292017-01-13 15:41:25 +0100692def check_command(expr, info):
Eric Blakedd883c62015-05-04 09:05:21 -0600693 name = expr['command']
Eric Blakec8184082016-07-13 21:50:20 -0600694 boxed = expr.get('boxed', False)
Eric Blake2cbf0992015-05-04 09:05:24 -0600695
Eric Blakec8184082016-07-13 21:50:20 -0600696 args_meta = ['struct']
697 if boxed:
698 args_meta += ['union', 'alternate']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100699 check_type(info, "'data' for command '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600700 expr.get('data'), allow_dict=not boxed, allow_optional=True,
701 allow_metas=args_meta)
Eric Blake10d4d992015-05-04 09:05:23 -0600702 returns_meta = ['union', 'struct']
703 if name in returns_whitelist:
704 returns_meta += ['built-in', 'alternate', 'enum']
Marc-André Lureau4148c292017-01-13 15:41:25 +0100705 check_type(info, "'returns' for command '%s'" % name,
Markus Armbruster9b090d42015-07-31 17:59:38 +0200706 expr.get('returns'), allow_array=True,
Markus Armbruster2d212912015-09-16 13:06:27 +0200707 allow_optional=True, allow_metas=returns_meta)
Eric Blakedd883c62015-05-04 09:05:21 -0600708
Eric Blake437db252015-09-29 16:21:02 -0600709
Marc-André Lureau4148c292017-01-13 15:41:25 +0100710def check_event(expr, info):
Eric Blake4dc2e692015-05-04 09:05:17 -0600711 global events
712 name = expr['event']
Eric Blakec8184082016-07-13 21:50:20 -0600713 boxed = expr.get('boxed', False)
Eric Blake4dc2e692015-05-04 09:05:17 -0600714
Eric Blakec8184082016-07-13 21:50:20 -0600715 meta = ['struct']
716 if boxed:
717 meta += ['union', 'alternate']
Eric Blake4dc2e692015-05-04 09:05:17 -0600718 events.append(name)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100719 check_type(info, "'data' for event '%s'" % name,
Eric Blakec8184082016-07-13 21:50:20 -0600720 expr.get('data'), allow_dict=not boxed, allow_optional=True,
721 allow_metas=meta)
Wenchao Xia21cd70d2014-06-18 08:43:28 +0200722
Eric Blake437db252015-09-29 16:21:02 -0600723
Marc-André Lureau4148c292017-01-13 15:41:25 +0100724def check_union(expr, info):
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800725 name = expr['union']
726 base = expr.get('base')
727 discriminator = expr.get('discriminator')
728 members = expr['data']
729
Eric Blake811d04f2015-05-04 09:05:10 -0600730 # Two types of unions, determined by discriminator.
Eric Blake811d04f2015-05-04 09:05:10 -0600731
732 # With no discriminator it is a simple union.
733 if discriminator is None:
Eric Blake44bd1272015-05-04 09:05:08 -0600734 enum_define = None
Eric Blake437db252015-09-29 16:21:02 -0600735 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
Eric Blake44bd1272015-05-04 09:05:08 -0600736 if base is not None:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100737 raise QAPISemError(info, "Simple union '%s' must not have a base" %
738 name)
Eric Blake44bd1272015-05-04 09:05:08 -0600739
740 # Else, it's a flat union.
741 else:
Eric Blakeac4338f2016-03-17 16:48:39 -0600742 # The object must have a string or dictionary 'base'.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100743 check_type(info, "'base' for union '%s'" % name,
Eric Blakeac4338f2016-03-17 16:48:39 -0600744 base, allow_dict=True, allow_optional=True,
745 allow_metas=['struct'])
Eric Blake376863e2015-09-29 16:21:07 -0600746 if not base:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100747 raise QAPISemError(info, "Flat union '%s' must have a base"
748 % name)
Eric Blake14f00c62016-03-03 09:16:43 -0700749 base_members = find_base_members(base)
Markus Armbruster48153742017-03-15 13:56:58 +0100750 assert base_members is not None
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800751
Eric Blakec9e0a792015-05-04 09:05:22 -0600752 # The value of member 'discriminator' must name a non-optional
Eric Blakefd41dd42015-05-04 09:05:25 -0600753 # member of the base struct.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100754 check_name(info, "Discriminator of flat union '%s'" % name,
Eric Blakec9e0a792015-05-04 09:05:22 -0600755 discriminator)
Eric Blake14f00c62016-03-03 09:16:43 -0700756 discriminator_type = base_members.get(discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800757 if not discriminator_type:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100758 raise QAPISemError(info,
759 "Discriminator '%s' is not a member of base "
760 "struct '%s'"
761 % (discriminator, base))
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800762 enum_define = find_enum(discriminator_type)
Eric Blake437db252015-09-29 16:21:02 -0600763 allow_metas = ['struct']
Wenchao Xia52230702014-03-04 18:44:39 -0800764 # Do not allow string discriminator
765 if not enum_define:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100766 raise QAPISemError(info,
767 "Discriminator '%s' must be of enumeration "
768 "type" % discriminator)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800769
Eric Blake02a57ae2016-02-17 23:48:16 -0700770 # Check every branch; don't allow an empty union
771 if len(members) == 0:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100772 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800773 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100774 check_name(info, "Member of union '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600775
Eric Blake01cfbaa2015-12-01 22:20:58 -0700776 # Each value must name a known type
Marc-André Lureau4148c292017-01-13 15:41:25 +0100777 check_type(info, "Member '%s' of union '%s'" % (key, name),
Markus Armbrusterf9a14272015-06-10 13:07:43 +0200778 value, allow_array=not base, allow_metas=allow_metas)
Eric Blakedd883c62015-05-04 09:05:21 -0600779
Eric Blake44bd1272015-05-04 09:05:08 -0600780 # If the discriminator names an enum type, then all members
Eric Blake61a94662015-11-18 01:52:49 -0700781 # of 'data' must also be members of the enum type.
Eric Blake44bd1272015-05-04 09:05:08 -0600782 if enum_define:
Eric Blake437db252015-09-29 16:21:02 -0600783 if key not in enum_define['enum_values']:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100784 raise QAPISemError(info,
785 "Discriminator value '%s' is not found in "
786 "enum '%s'"
787 % (key, enum_define["enum_name"]))
Eric Blake44bd1272015-05-04 09:05:08 -0600788
Eric Blaked0b18232016-07-13 21:50:13 -0600789 # If discriminator is user-defined, ensure all values are covered
790 if enum_define:
791 for value in enum_define['enum_values']:
792 if value not in members.keys():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100793 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
794 % (name, value))
Eric Blaked0b18232016-07-13 21:50:13 -0600795
Eric Blake437db252015-09-29 16:21:02 -0600796
Marc-André Lureau4148c292017-01-13 15:41:25 +0100797def check_alternate(expr, info):
Eric Blakeab916fa2015-05-04 09:05:13 -0600798 name = expr['alternate']
Eric Blake811d04f2015-05-04 09:05:10 -0600799 members = expr['data']
Eric Blake811d04f2015-05-04 09:05:10 -0600800 types_seen = {}
Eric Blake44bd1272015-05-04 09:05:08 -0600801
Eric Blake02a57ae2016-02-17 23:48:16 -0700802 # Check every branch; require at least two branches
803 if len(members) < 2:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100804 raise QAPISemError(info,
805 "Alternate '%s' should have at least two branches "
806 "in 'data'" % name)
Eric Blake811d04f2015-05-04 09:05:10 -0600807 for (key, value) in members.items():
Marc-André Lureau4148c292017-01-13 15:41:25 +0100808 check_name(info, "Member of alternate '%s'" % name, key)
Eric Blakec9e0a792015-05-04 09:05:22 -0600809
Eric Blake811d04f2015-05-04 09:05:10 -0600810 # Ensure alternates have no type conflicts.
Marc-André Lureau4148c292017-01-13 15:41:25 +0100811 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
Eric Blakedd883c62015-05-04 09:05:21 -0600812 value,
813 allow_metas=['built-in', 'union', 'struct', 'enum'])
Eric Blake811d04f2015-05-04 09:05:10 -0600814 qtype = find_alternate_member_qtype(value)
Eric Blake46534302016-02-17 23:48:17 -0700815 if not qtype:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100816 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
817 "type '%s'" % (name, key, value))
Eric Blake811d04f2015-05-04 09:05:10 -0600818 if qtype in types_seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100819 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
820 "be distinguished from member '%s'"
821 % (name, key, types_seen[qtype]))
Eric Blake811d04f2015-05-04 09:05:10 -0600822 types_seen[qtype] = key
Wenchao Xiab86b05e2014-03-04 18:44:34 -0800823
Eric Blake437db252015-09-29 16:21:02 -0600824
Marc-André Lureau4148c292017-01-13 15:41:25 +0100825def check_enum(expr, info):
Eric Blakecf393592015-05-04 09:05:04 -0600826 name = expr['enum']
827 members = expr.get('data')
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100828 prefix = expr.get('prefix')
Eric Blakecf393592015-05-04 09:05:04 -0600829
830 if not isinstance(members, list):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100831 raise QAPISemError(info,
832 "Enum '%s' requires an array for 'data'" % name)
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100833 if prefix is not None and not isinstance(prefix, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100834 raise QAPISemError(info,
835 "Enum '%s' requires a string for 'prefix'" % name)
Eric Blakecf393592015-05-04 09:05:04 -0600836 for member in members:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100837 check_name(info, "Member of enum '%s'" % name, member,
Eric Blakec9e0a792015-05-04 09:05:22 -0600838 enum_member=True)
Eric Blakecf393592015-05-04 09:05:04 -0600839
Eric Blake437db252015-09-29 16:21:02 -0600840
Marc-André Lureau4148c292017-01-13 15:41:25 +0100841def check_struct(expr, info):
Eric Blakefd41dd42015-05-04 09:05:25 -0600842 name = expr['struct']
Eric Blakedd883c62015-05-04 09:05:21 -0600843 members = expr['data']
844
Marc-André Lureau4148c292017-01-13 15:41:25 +0100845 check_type(info, "'data' for struct '%s'" % name, members,
Eric Blakec9e0a792015-05-04 09:05:22 -0600846 allow_dict=True, allow_optional=True)
Marc-André Lureau4148c292017-01-13 15:41:25 +0100847 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
Eric Blakedd883c62015-05-04 09:05:21 -0600848 allow_metas=['struct'])
849
Eric Blake437db252015-09-29 16:21:02 -0600850
Eric Blake0545f6b2015-05-04 09:05:15 -0600851def check_keys(expr_elem, meta, required, optional=[]):
852 expr = expr_elem['expr']
853 info = expr_elem['info']
854 name = expr[meta]
855 if not isinstance(name, str):
Marc-André Lureau4148c292017-01-13 15:41:25 +0100856 raise QAPISemError(info, "'%s' key must have a string value" % meta)
Eric Blake437db252015-09-29 16:21:02 -0600857 required = required + [meta]
Eric Blake0545f6b2015-05-04 09:05:15 -0600858 for (key, value) in expr.items():
Eric Blake437db252015-09-29 16:21:02 -0600859 if key not in required and key not in optional:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100860 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
861 % (key, meta, name))
Eric Blake437db252015-09-29 16:21:02 -0600862 if (key == 'gen' or key == 'success-response') and value is not False:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100863 raise QAPISemError(info,
864 "'%s' of %s '%s' should only use false value"
865 % (key, meta, name))
Eric Blakec8184082016-07-13 21:50:20 -0600866 if key == 'boxed' and value is not True:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100867 raise QAPISemError(info,
868 "'%s' of %s '%s' should only use true value"
869 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600870 for key in required:
Eric Blake437db252015-09-29 16:21:02 -0600871 if key not in expr:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100872 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
873 % (key, meta, name))
Eric Blake0545f6b2015-05-04 09:05:15 -0600874
Eric Blake437db252015-09-29 16:21:02 -0600875
Markus Armbruster4d076d62015-06-10 08:55:21 +0200876def check_exprs(exprs):
877 global all_names
878
879 # Learn the types and check for valid expression keys
880 for builtin in builtin_types.keys():
881 all_names[builtin] = 'built-in'
882 for expr_elem in exprs:
883 expr = expr_elem['expr']
884 info = expr_elem['info']
Marc-André Lureau3313b612017-01-13 15:41:29 +0100885
Markus Armbrusterbc52d032017-03-15 13:56:51 +0100886 if 'doc' not in expr_elem and doc_required:
Marc-André Lureau3313b612017-01-13 15:41:29 +0100887 raise QAPISemError(info,
888 "Expression missing documentation comment")
889
Eric Blake437db252015-09-29 16:21:02 -0600890 if 'enum' in expr:
Daniel P. Berrange351d36e2015-08-26 14:21:20 +0100891 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200892 add_enum(expr['enum'], info, expr['data'])
Eric Blake437db252015-09-29 16:21:02 -0600893 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200894 check_keys(expr_elem, 'union', ['data'],
895 ['base', 'discriminator'])
896 add_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600897 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200898 check_keys(expr_elem, 'alternate', ['data'])
899 add_name(expr['alternate'], info, 'alternate')
Eric Blake437db252015-09-29 16:21:02 -0600900 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200901 check_keys(expr_elem, 'struct', ['data'], ['base'])
902 add_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600903 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200904 check_keys(expr_elem, 'command', [],
Eric Blakec8184082016-07-13 21:50:20 -0600905 ['data', 'returns', 'gen', 'success-response', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200906 add_name(expr['command'], info, 'command')
Eric Blake437db252015-09-29 16:21:02 -0600907 elif 'event' in expr:
Eric Blakec8184082016-07-13 21:50:20 -0600908 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Markus Armbruster4d076d62015-06-10 08:55:21 +0200909 add_name(expr['event'], info, 'event')
910 else:
Marc-André Lureau4148c292017-01-13 15:41:25 +0100911 raise QAPISemError(expr_elem['info'],
912 "Expression is missing metatype")
Markus Armbruster4d076d62015-06-10 08:55:21 +0200913
914 # Try again for hidden UnionKind enum
915 for expr_elem in exprs:
916 expr = expr_elem['expr']
Eric Blake437db252015-09-29 16:21:02 -0600917 if 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200918 if not discriminator_find_enum_define(expr):
919 add_enum('%sKind' % expr['union'], expr_elem['info'],
920 implicit=True)
Eric Blake437db252015-09-29 16:21:02 -0600921 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200922 add_enum('%sKind' % expr['alternate'], expr_elem['info'],
923 implicit=True)
924
925 # Validate that exprs make sense
926 for expr_elem in exprs:
927 expr = expr_elem['expr']
928 info = expr_elem['info']
929
Eric Blake437db252015-09-29 16:21:02 -0600930 if 'enum' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200931 check_enum(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600932 elif 'union' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200933 check_union(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600934 elif 'alternate' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200935 check_alternate(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600936 elif 'struct' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200937 check_struct(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600938 elif 'command' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200939 check_command(expr, info)
Eric Blake437db252015-09-29 16:21:02 -0600940 elif 'event' in expr:
Markus Armbruster4d076d62015-06-10 08:55:21 +0200941 check_event(expr, info)
942 else:
943 assert False, 'unexpected meta type'
944
Markus Armbrusterac882192015-09-16 13:06:05 +0200945 return exprs
Eric Blake0545f6b2015-05-04 09:05:15 -0600946
Markus Armbrusterac882192015-09-16 13:06:05 +0200947
Marc-André Lureau3313b612017-01-13 15:41:29 +0100948def check_freeform_doc(doc):
949 if doc.symbol:
950 raise QAPISemError(doc.info,
951 "Documention for '%s' is not followed"
952 " by the definition" % doc.symbol)
953
954 body = str(doc.body)
955 if re.search(r'@\S+:', body, re.MULTILINE):
956 raise QAPISemError(doc.info,
957 "Free-form documentation block must not contain"
958 " @NAME: sections")
959
960
961def check_definition_doc(doc, expr, info):
962 for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
963 if i in expr:
964 meta = i
965 break
966
967 name = expr[meta]
968 if doc.symbol != name:
969 raise QAPISemError(info, "Definition of '%s' follows documentation"
970 " for '%s'" % (name, doc.symbol))
971 if doc.has_section('Returns') and 'command' not in expr:
972 raise QAPISemError(info, "'Returns:' is only valid for commands")
973
974 if meta == 'union':
975 args = expr.get('base', [])
976 else:
977 args = expr.get('data', [])
978 if isinstance(args, str):
979 return
980 if isinstance(args, dict):
981 args = args.keys()
982 assert isinstance(args, list)
983
984 if (meta == 'alternate'
985 or (meta == 'union' and not expr.get('discriminator'))):
986 args.append('type')
987
988 for arg in args:
989 if arg[0] == '*':
990 opt = True
991 desc = doc.args.get(arg[1:])
992 else:
993 opt = False
994 desc = doc.args.get(arg)
995 if not desc:
996 continue
Markus Armbrusterb116fd82017-03-15 13:57:00 +0100997 desc.optional = opt
Marc-André Lureau3313b612017-01-13 15:41:29 +0100998 desc_opt = "#optional" in str(desc)
999 if desc_opt and not opt:
1000 raise QAPISemError(info, "Description has #optional, "
1001 "but the declaration doesn't")
1002 if not desc_opt and opt:
Marc-André Lureau3313b612017-01-13 15:41:29 +01001003 # TODO either fix the schema and make this an error,
1004 # or drop #optional entirely
Markus Armbrusterb116fd82017-03-15 13:57:00 +01001005 pass
Marc-André Lureau3313b612017-01-13 15:41:29 +01001006
1007 doc_args = set(doc.args.keys())
1008 args = set([name.strip('*') for name in args])
1009 if not doc_args.issubset(args):
1010 raise QAPISemError(info, "The following documented members are not in "
1011 "the declaration: %s" % ", ".join(doc_args - args))
1012
1013
1014def check_docs(docs):
1015 for doc in docs:
1016 for section in doc.args.values() + doc.sections:
1017 content = str(section)
1018 if not content or content.isspace():
1019 raise QAPISemError(doc.info,
1020 "Empty doc section '%s'" % section.name)
1021
1022 if not doc.expr:
1023 check_freeform_doc(doc)
1024 else:
1025 check_definition_doc(doc, doc.expr, doc.info)
1026
1027 return docs
1028
1029
Markus Armbrusterac882192015-09-16 13:06:05 +02001030#
1031# Schema compiler frontend
1032#
1033
1034class QAPISchemaEntity(object):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001035 def __init__(self, name, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001036 assert isinstance(name, str)
1037 self.name = name
Eric Blake99df5282015-10-12 22:22:32 -06001038 # For explicitly defined entities, info points to the (explicit)
1039 # definition. For builtins (and their arrays), info is None.
1040 # For implicitly defined entities, info points to a place that
1041 # triggered the implicit definition (there may be more than one
1042 # such place).
Markus Armbrusterac882192015-09-16 13:06:05 +02001043 self.info = info
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001044 self.doc = doc
Markus Armbrusterac882192015-09-16 13:06:05 +02001045
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001046 def c_name(self):
1047 return c_name(self.name)
1048
Markus Armbrusterac882192015-09-16 13:06:05 +02001049 def check(self, schema):
1050 pass
1051
Eric Blake49823c42015-10-12 22:22:27 -06001052 def is_implicit(self):
1053 return not self.info
1054
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001055 def visit(self, visitor):
1056 pass
1057
1058
1059class QAPISchemaVisitor(object):
1060 def visit_begin(self, schema):
1061 pass
1062
1063 def visit_end(self):
1064 pass
1065
Eric Blake25a0d9c2015-10-12 22:22:21 -06001066 def visit_needed(self, entity):
1067 # Default to visiting everything
1068 return True
1069
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001070 def visit_builtin_type(self, name, info, json_type):
1071 pass
1072
1073 def visit_enum_type(self, name, info, values, prefix):
1074 pass
1075
1076 def visit_array_type(self, name, info, element_type):
1077 pass
1078
1079 def visit_object_type(self, name, info, base, members, variants):
1080 pass
1081
Markus Armbruster39a18152015-09-16 13:06:28 +02001082 def visit_object_type_flat(self, name, info, members, variants):
1083 pass
1084
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001085 def visit_alternate_type(self, name, info, variants):
1086 pass
1087
1088 def visit_command(self, name, info, arg_type, ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001089 gen, success_response, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001090 pass
1091
Eric Blake48825ca2016-07-13 21:50:19 -06001092 def visit_event(self, name, info, arg_type, boxed):
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001093 pass
1094
Markus Armbrusterac882192015-09-16 13:06:05 +02001095
1096class QAPISchemaType(QAPISchemaEntity):
Eric Blake4040d992016-03-17 16:48:28 -06001097 # Return the C type for common use.
1098 # For the types we commonly box, this is a pointer type.
1099 def c_type(self):
1100 pass
1101
1102 # Return the C type to be used in a parameter list.
1103 def c_param_type(self):
1104 return self.c_type()
1105
1106 # Return the C type to be used where we suppress boxing.
1107 def c_unboxed_type(self):
1108 return self.c_type()
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001109
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001110 def json_type(self):
1111 pass
1112
1113 def alternate_qtype(self):
1114 json2qtype = {
1115 'string': 'QTYPE_QSTRING',
1116 'number': 'QTYPE_QFLOAT',
1117 'int': 'QTYPE_QINT',
1118 'boolean': 'QTYPE_QBOOL',
1119 'object': 'QTYPE_QDICT'
1120 }
1121 return json2qtype.get(self.json_type())
Markus Armbrusterac882192015-09-16 13:06:05 +02001122
1123
1124class QAPISchemaBuiltinType(QAPISchemaType):
Eric Blake861877a2016-03-17 16:48:36 -06001125 def __init__(self, name, json_type, c_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001126 QAPISchemaType.__init__(self, name, None, None)
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001127 assert not c_type or isinstance(c_type, str)
1128 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1129 'value')
1130 self._json_type_name = json_type
1131 self._c_type_name = c_type
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001132
1133 def c_name(self):
1134 return self.name
1135
Eric Blake4040d992016-03-17 16:48:28 -06001136 def c_type(self):
1137 return self._c_type_name
1138
1139 def c_param_type(self):
1140 if self.name == 'str':
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001141 return 'const ' + self._c_type_name
1142 return self._c_type_name
1143
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001144 def json_type(self):
1145 return self._json_type_name
Markus Armbrusterac882192015-09-16 13:06:05 +02001146
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001147 def visit(self, visitor):
1148 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1149
Markus Armbrusterac882192015-09-16 13:06:05 +02001150
1151class QAPISchemaEnumType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001152 def __init__(self, name, info, doc, values, prefix):
1153 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001154 for v in values:
Eric Blake93bda4d2015-12-01 22:20:55 -07001155 assert isinstance(v, QAPISchemaMember)
1156 v.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001157 assert prefix is None or isinstance(prefix, str)
1158 self.values = values
1159 self.prefix = prefix
1160
1161 def check(self, schema):
Eric Blake93bda4d2015-12-01 22:20:55 -07001162 seen = {}
1163 for v in self.values:
1164 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001165 if self.doc:
1166 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001167
Eric Blake99df5282015-10-12 22:22:32 -06001168 def is_implicit(self):
Markus Armbruster46362112017-03-15 13:57:02 +01001169 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1170 return self.name.endswith('Kind') or self.name == 'QType'
Eric Blake99df5282015-10-12 22:22:32 -06001171
Eric Blake4040d992016-03-17 16:48:28 -06001172 def c_type(self):
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001173 return c_name(self.name)
1174
Eric Blake93bda4d2015-12-01 22:20:55 -07001175 def member_names(self):
1176 return [v.name for v in self.values]
1177
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001178 def json_type(self):
1179 return 'string'
1180
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001181 def visit(self, visitor):
1182 visitor.visit_enum_type(self.name, self.info,
Eric Blake93bda4d2015-12-01 22:20:55 -07001183 self.member_names(), self.prefix)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001184
Markus Armbrusterac882192015-09-16 13:06:05 +02001185
1186class QAPISchemaArrayType(QAPISchemaType):
1187 def __init__(self, name, info, element_type):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001188 QAPISchemaType.__init__(self, name, info, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001189 assert isinstance(element_type, str)
1190 self._element_type_name = element_type
1191 self.element_type = None
1192
1193 def check(self, schema):
1194 self.element_type = schema.lookup_type(self._element_type_name)
1195 assert self.element_type
1196
Eric Blake99df5282015-10-12 22:22:32 -06001197 def is_implicit(self):
1198 return True
1199
Eric Blake4040d992016-03-17 16:48:28 -06001200 def c_type(self):
1201 return c_name(self.name) + pointer_suffix
1202
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001203 def json_type(self):
1204 return 'array'
1205
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001206 def visit(self, visitor):
1207 visitor.visit_array_type(self.name, self.info, self.element_type)
1208
Markus Armbrusterac882192015-09-16 13:06:05 +02001209
1210class QAPISchemaObjectType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001211 def __init__(self, name, info, doc, base, local_members, variants):
Eric Blakeda34a9b2015-11-18 01:52:36 -07001212 # struct has local_members, optional base, and no variants
1213 # flat union has base, variants, and no local_members
1214 # simple union has local_members, variants, and no base
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001215 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001216 assert base is None or isinstance(base, str)
1217 for m in local_members:
1218 assert isinstance(m, QAPISchemaObjectTypeMember)
Eric Blake88d4ef82015-11-18 01:52:50 -07001219 m.set_owner(name)
1220 if variants is not None:
1221 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1222 variants.set_owner(name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001223 self._base_name = base
1224 self.base = None
1225 self.local_members = local_members
1226 self.variants = variants
1227 self.members = None
1228
1229 def check(self, schema):
Eric Blakebac54292015-12-01 22:20:59 -07001230 if self.members is False: # check for cycles
Marc-André Lureau4148c292017-01-13 15:41:25 +01001231 raise QAPISemError(self.info,
1232 "Object %s contains itself" % self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001233 if self.members:
1234 return
1235 self.members = False # mark as being checked
Markus Armbruster23a4b2c2015-11-18 01:52:43 -07001236 seen = OrderedDict()
Markus Armbrusterac882192015-09-16 13:06:05 +02001237 if self._base_name:
1238 self.base = schema.lookup_type(self._base_name)
1239 assert isinstance(self.base, QAPISchemaObjectType)
Markus Armbrusterac882192015-09-16 13:06:05 +02001240 self.base.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001241 self.base.check_clash(schema, self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001242 for m in self.local_members:
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001243 m.check(schema)
Eric Blake27b60ab2015-11-18 01:52:51 -07001244 m.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001245 if self.doc:
1246 self.doc.connect_member(m)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001247 self.members = seen.values()
Markus Armbrusterac882192015-09-16 13:06:05 +02001248 if self.variants:
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001249 self.variants.check(schema, seen)
Markus Armbruster14ff8462015-11-18 01:52:45 -07001250 assert self.variants.tag_member in self.members
Eric Blake27b60ab2015-11-18 01:52:51 -07001251 self.variants.check_clash(schema, self.info, seen)
Markus Armbrusterac882192015-09-16 13:06:05 +02001252
Eric Blake14f00c62016-03-03 09:16:43 -07001253 # Check that the members of this type do not cause duplicate JSON members,
Eric Blake27b60ab2015-11-18 01:52:51 -07001254 # and update seen to track the members seen so far. Report any errors
1255 # on behalf of info, which is not necessarily self.info
1256 def check_clash(self, schema, info, seen):
Eric Blakec2183d22015-11-18 01:52:47 -07001257 assert not self.variants # not implemented
1258 for m in self.members:
Eric Blake27b60ab2015-11-18 01:52:51 -07001259 m.check_clash(info, seen)
Eric Blakec2183d22015-11-18 01:52:47 -07001260
Eric Blake99df5282015-10-12 22:22:32 -06001261 def is_implicit(self):
Eric Blake75996972016-03-17 16:48:29 -06001262 # See QAPISchema._make_implicit_object_type(), as well as
1263 # _def_predefineds()
1264 return self.name.startswith('q_')
Eric Blake99df5282015-10-12 22:22:32 -06001265
Eric Blakeb6167702016-07-13 21:50:16 -06001266 def is_empty(self):
1267 assert self.members is not None
1268 return not self.members and not self.variants
1269
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001270 def c_name(self):
Eric Blakecd50a252016-07-13 21:50:14 -06001271 assert self.name != 'q_empty'
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001272 return QAPISchemaType.c_name(self)
1273
Eric Blake4040d992016-03-17 16:48:28 -06001274 def c_type(self):
Eric Blake49823c42015-10-12 22:22:27 -06001275 assert not self.is_implicit()
Eric Blakebecceed2016-02-17 23:48:26 -07001276 return c_name(self.name) + pointer_suffix
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001277
Eric Blake4040d992016-03-17 16:48:28 -06001278 def c_unboxed_type(self):
Eric Blake4040d992016-03-17 16:48:28 -06001279 return c_name(self.name)
1280
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001281 def json_type(self):
1282 return 'object'
1283
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001284 def visit(self, visitor):
1285 visitor.visit_object_type(self.name, self.info,
1286 self.base, self.local_members, self.variants)
Markus Armbruster39a18152015-09-16 13:06:28 +02001287 visitor.visit_object_type_flat(self.name, self.info,
1288 self.members, self.variants)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001289
Markus Armbrusterac882192015-09-16 13:06:05 +02001290
Eric Blaked44f9ac2015-12-01 22:20:54 -07001291class QAPISchemaMember(object):
Eric Blake88d4ef82015-11-18 01:52:50 -07001292 role = 'member'
1293
Eric Blaked44f9ac2015-12-01 22:20:54 -07001294 def __init__(self, name):
Markus Armbrusterac882192015-09-16 13:06:05 +02001295 assert isinstance(name, str)
Markus Armbrusterac882192015-09-16 13:06:05 +02001296 self.name = name
Eric Blake88d4ef82015-11-18 01:52:50 -07001297 self.owner = None
1298
1299 def set_owner(self, name):
1300 assert not self.owner
1301 self.owner = name
Markus Armbrusterac882192015-09-16 13:06:05 +02001302
Eric Blake27b60ab2015-11-18 01:52:51 -07001303 def check_clash(self, info, seen):
1304 cname = c_name(self.name)
Markus Armbruster2cfbae32017-03-15 13:56:55 +01001305 if cname.lower() != cname and self.owner not in name_case_whitelist:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001306 raise QAPISemError(info,
1307 "%s should not use uppercase" % self.describe())
Eric Blake27b60ab2015-11-18 01:52:51 -07001308 if cname in seen:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001309 raise QAPISemError(info, "%s collides with %s" %
1310 (self.describe(), seen[cname].describe()))
Eric Blake27b60ab2015-11-18 01:52:51 -07001311 seen[cname] = self
Markus Armbruster577de122015-11-18 01:52:44 -07001312
Eric Blake88d4ef82015-11-18 01:52:50 -07001313 def _pretty_owner(self):
1314 owner = self.owner
Eric Blake75996972016-03-17 16:48:29 -06001315 if owner.startswith('q_obj_'):
Eric Blake88d4ef82015-11-18 01:52:50 -07001316 # See QAPISchema._make_implicit_object_type() - reverse the
1317 # mapping there to create a nice human-readable description
Eric Blake75996972016-03-17 16:48:29 -06001318 owner = owner[6:]
Eric Blake88d4ef82015-11-18 01:52:50 -07001319 if owner.endswith('-arg'):
1320 return '(parameter of %s)' % owner[:-4]
Eric Blakeac4338f2016-03-17 16:48:39 -06001321 elif owner.endswith('-base'):
1322 return '(base of %s)' % owner[:-5]
Eric Blake88d4ef82015-11-18 01:52:50 -07001323 else:
1324 assert owner.endswith('-wrapper')
1325 # Unreachable and not implemented
1326 assert False
Eric Blake93bda4d2015-12-01 22:20:55 -07001327 if owner.endswith('Kind'):
1328 # See QAPISchema._make_implicit_enum_type()
1329 return '(branch of %s)' % owner[:-4]
Eric Blake88d4ef82015-11-18 01:52:50 -07001330 return '(%s of %s)' % (self.role, owner)
1331
1332 def describe(self):
1333 return "'%s' %s" % (self.name, self._pretty_owner())
1334
Markus Armbrusterac882192015-09-16 13:06:05 +02001335
Eric Blaked44f9ac2015-12-01 22:20:54 -07001336class QAPISchemaObjectTypeMember(QAPISchemaMember):
1337 def __init__(self, name, typ, optional):
1338 QAPISchemaMember.__init__(self, name)
1339 assert isinstance(typ, str)
1340 assert isinstance(optional, bool)
1341 self._type_name = typ
1342 self.type = None
1343 self.optional = optional
1344
1345 def check(self, schema):
1346 assert self.owner
1347 self.type = schema.lookup_type(self._type_name)
1348 assert self.type
1349
1350
Markus Armbrusterac882192015-09-16 13:06:05 +02001351class QAPISchemaObjectTypeVariants(object):
Eric Blake46292ba2015-10-12 22:22:29 -06001352 def __init__(self, tag_name, tag_member, variants):
1353 # Flat unions pass tag_name but not tag_member.
1354 # Simple unions and alternates pass tag_member but not tag_name.
1355 # After check(), tag_member is always set, and tag_name remains
1356 # a reliable witness of being used by a flat union.
1357 assert bool(tag_member) != bool(tag_name)
1358 assert (isinstance(tag_name, str) or
1359 isinstance(tag_member, QAPISchemaObjectTypeMember))
Eric Blake02a57ae2016-02-17 23:48:16 -07001360 assert len(variants) > 0
Markus Armbrusterac882192015-09-16 13:06:05 +02001361 for v in variants:
1362 assert isinstance(v, QAPISchemaObjectTypeVariant)
Eric Blakeda9cb192016-07-13 21:50:15 -06001363 self._tag_name = tag_name
Eric Blake46292ba2015-10-12 22:22:29 -06001364 self.tag_member = tag_member
Markus Armbrusterac882192015-09-16 13:06:05 +02001365 self.variants = variants
1366
Eric Blake88d4ef82015-11-18 01:52:50 -07001367 def set_owner(self, name):
1368 for v in self.variants:
1369 v.set_owner(name)
1370
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001371 def check(self, schema, seen):
Markus Armbruster14ff8462015-11-18 01:52:45 -07001372 if not self.tag_member: # flat union
Eric Blakeda9cb192016-07-13 21:50:15 -06001373 self.tag_member = seen[c_name(self._tag_name)]
1374 assert self._tag_name == self.tag_member.name
Markus Armbrusterac882192015-09-16 13:06:05 +02001375 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1376 for v in self.variants:
Eric Blake10565ca2015-11-18 01:52:48 -07001377 v.check(schema)
Eric Blake0426d532015-12-01 22:20:48 -07001378 # Union names must match enum values; alternate names are
1379 # checked separately. Use 'seen' to tell the two apart.
1380 if seen:
Eric Blake93bda4d2015-12-01 22:20:55 -07001381 assert v.name in self.tag_member.type.member_names()
Eric Blake0426d532015-12-01 22:20:48 -07001382 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001383 v.type.check(schema)
1384
Eric Blake27b60ab2015-11-18 01:52:51 -07001385 def check_clash(self, schema, info, seen):
Eric Blakeb807a1e2015-11-18 01:52:46 -07001386 for v in self.variants:
1387 # Reset seen map for each variant, since qapi names from one
1388 # branch do not affect another branch
Eric Blakeb807a1e2015-11-18 01:52:46 -07001389 assert isinstance(v.type, QAPISchemaObjectType)
Eric Blake27b60ab2015-11-18 01:52:51 -07001390 v.type.check_clash(schema, info, dict(seen))
Markus Armbrusterac882192015-09-16 13:06:05 +02001391
Eric Blake437db252015-09-29 16:21:02 -06001392
Markus Armbrusterac882192015-09-16 13:06:05 +02001393class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
Eric Blake88d4ef82015-11-18 01:52:50 -07001394 role = 'branch'
1395
Markus Armbrusterac882192015-09-16 13:06:05 +02001396 def __init__(self, name, typ):
1397 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1398
Markus Armbrusterac882192015-09-16 13:06:05 +02001399
1400class QAPISchemaAlternateType(QAPISchemaType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001401 def __init__(self, name, info, doc, variants):
1402 QAPISchemaType.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001403 assert isinstance(variants, QAPISchemaObjectTypeVariants)
Eric Blakeda9cb192016-07-13 21:50:15 -06001404 assert variants.tag_member
Eric Blake88d4ef82015-11-18 01:52:50 -07001405 variants.set_owner(name)
1406 variants.tag_member.set_owner(self.name)
Markus Armbrusterac882192015-09-16 13:06:05 +02001407 self.variants = variants
1408
1409 def check(self, schema):
Markus Armbrustere564e2d2015-11-18 01:52:40 -07001410 self.variants.tag_member.check(schema)
Eric Blakeb807a1e2015-11-18 01:52:46 -07001411 # Not calling self.variants.check_clash(), because there's nothing
1412 # to clash with
Markus Armbrustercdc5fa32015-11-18 01:52:41 -07001413 self.variants.check(schema, {})
Eric Blake0426d532015-12-01 22:20:48 -07001414 # Alternate branch names have no relation to the tag enum values;
1415 # so we have to check for potential name collisions ourselves.
1416 seen = {}
1417 for v in self.variants.variants:
1418 v.check_clash(self.info, seen)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001419 if self.doc:
1420 self.doc.connect_member(v)
Markus Armbrusterac882192015-09-16 13:06:05 +02001421
Eric Blake4040d992016-03-17 16:48:28 -06001422 def c_type(self):
1423 return c_name(self.name) + pointer_suffix
1424
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001425 def json_type(self):
1426 return 'value'
1427
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001428 def visit(self, visitor):
1429 visitor.visit_alternate_type(self.name, self.info, self.variants)
1430
Eric Blakec8184082016-07-13 21:50:20 -06001431 def is_empty(self):
1432 return False
1433
Markus Armbrusterac882192015-09-16 13:06:05 +02001434
1435class QAPISchemaCommand(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001436 def __init__(self, name, info, doc, arg_type, ret_type,
1437 gen, success_response, boxed):
1438 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001439 assert not arg_type or isinstance(arg_type, str)
1440 assert not ret_type or isinstance(ret_type, str)
1441 self._arg_type_name = arg_type
1442 self.arg_type = None
1443 self._ret_type_name = ret_type
1444 self.ret_type = None
1445 self.gen = gen
1446 self.success_response = success_response
Eric Blake48825ca2016-07-13 21:50:19 -06001447 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001448
1449 def check(self, schema):
1450 if self._arg_type_name:
1451 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001452 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1453 isinstance(self.arg_type, QAPISchemaAlternateType))
1454 self.arg_type.check(schema)
1455 if self.boxed:
1456 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001457 raise QAPISemError(self.info,
1458 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001459 else:
1460 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1461 assert not self.arg_type.variants
1462 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001463 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001464 if self._ret_type_name:
1465 self.ret_type = schema.lookup_type(self._ret_type_name)
1466 assert isinstance(self.ret_type, QAPISchemaType)
1467
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001468 def visit(self, visitor):
1469 visitor.visit_command(self.name, self.info,
1470 self.arg_type, self.ret_type,
Eric Blake48825ca2016-07-13 21:50:19 -06001471 self.gen, self.success_response, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001472
Markus Armbrusterac882192015-09-16 13:06:05 +02001473
1474class QAPISchemaEvent(QAPISchemaEntity):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001475 def __init__(self, name, info, doc, arg_type, boxed):
1476 QAPISchemaEntity.__init__(self, name, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001477 assert not arg_type or isinstance(arg_type, str)
1478 self._arg_type_name = arg_type
1479 self.arg_type = None
Eric Blake48825ca2016-07-13 21:50:19 -06001480 self.boxed = boxed
Markus Armbrusterac882192015-09-16 13:06:05 +02001481
1482 def check(self, schema):
1483 if self._arg_type_name:
1484 self.arg_type = schema.lookup_type(self._arg_type_name)
Eric Blakec8184082016-07-13 21:50:20 -06001485 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1486 isinstance(self.arg_type, QAPISchemaAlternateType))
1487 self.arg_type.check(schema)
1488 if self.boxed:
1489 if self.arg_type.is_empty():
Marc-André Lureau4148c292017-01-13 15:41:25 +01001490 raise QAPISemError(self.info,
1491 "Cannot use 'boxed' with empty type")
Eric Blakec8184082016-07-13 21:50:20 -06001492 else:
1493 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1494 assert not self.arg_type.variants
1495 elif self.boxed:
Marc-André Lureau4148c292017-01-13 15:41:25 +01001496 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
Markus Armbrusterac882192015-09-16 13:06:05 +02001497
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001498 def visit(self, visitor):
Eric Blake48825ca2016-07-13 21:50:19 -06001499 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001500
Markus Armbrusterac882192015-09-16 13:06:05 +02001501
1502class QAPISchema(object):
1503 def __init__(self, fname):
1504 try:
Marc-André Lureau3313b612017-01-13 15:41:29 +01001505 parser = QAPISchemaParser(open(fname, "r"))
1506 self.exprs = check_exprs(parser.exprs)
1507 self.docs = check_docs(parser.docs)
Eric Blake7618b912015-10-12 22:22:22 -06001508 self._entity_dict = {}
Eric Blake99df5282015-10-12 22:22:32 -06001509 self._predefining = True
Eric Blake7618b912015-10-12 22:22:22 -06001510 self._def_predefineds()
Eric Blake99df5282015-10-12 22:22:32 -06001511 self._predefining = False
Eric Blake7618b912015-10-12 22:22:22 -06001512 self._def_exprs()
1513 self.check()
Marc-André Lureau4148c292017-01-13 15:41:25 +01001514 except QAPIError as err:
Markus Armbrusterac882192015-09-16 13:06:05 +02001515 print >>sys.stderr, err
1516 exit(1)
Markus Armbrusterac882192015-09-16 13:06:05 +02001517
Markus Armbrusterac882192015-09-16 13:06:05 +02001518 def _def_entity(self, ent):
Eric Blake99df5282015-10-12 22:22:32 -06001519 # Only the predefined types are allowed to not have info
1520 assert ent.info or self._predefining
Markus Armbrusterac882192015-09-16 13:06:05 +02001521 assert ent.name not in self._entity_dict
1522 self._entity_dict[ent.name] = ent
1523
1524 def lookup_entity(self, name, typ=None):
1525 ent = self._entity_dict.get(name)
1526 if typ and not isinstance(ent, typ):
1527 return None
1528 return ent
1529
1530 def lookup_type(self, name):
1531 return self.lookup_entity(name, QAPISchemaType)
1532
Eric Blake861877a2016-03-17 16:48:36 -06001533 def _def_builtin_type(self, name, json_type, c_type):
1534 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
Eric Blake9f08c8e2015-10-12 22:22:28 -06001535 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1536 # qapi-types.h from a single .c, all arrays of builtins must be
1537 # declared in the first file whether or not they are used. Nicer
1538 # would be to use lazy instantiation, while figuring out how to
1539 # avoid compilation issues with multiple qapi-types.h.
Eric Blake99df5282015-10-12 22:22:32 -06001540 self._make_array_type(name, None)
Markus Armbrusterac882192015-09-16 13:06:05 +02001541
1542 def _def_predefineds(self):
Eric Blake861877a2016-03-17 16:48:36 -06001543 for t in [('str', 'string', 'char' + pointer_suffix),
1544 ('number', 'number', 'double'),
1545 ('int', 'int', 'int64_t'),
1546 ('int8', 'int', 'int8_t'),
1547 ('int16', 'int', 'int16_t'),
1548 ('int32', 'int', 'int32_t'),
1549 ('int64', 'int', 'int64_t'),
1550 ('uint8', 'int', 'uint8_t'),
1551 ('uint16', 'int', 'uint16_t'),
1552 ('uint32', 'int', 'uint32_t'),
1553 ('uint64', 'int', 'uint64_t'),
1554 ('size', 'int', 'uint64_t'),
1555 ('bool', 'boolean', 'bool'),
1556 ('any', 'value', 'QObject' + pointer_suffix)]:
Markus Armbrusterf51d8c32015-09-16 13:06:06 +02001557 self._def_builtin_type(*t)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001558 self.the_empty_object_type = QAPISchemaObjectType(
1559 'q_empty', None, None, None, [], None)
Markus Armbruster39a18152015-09-16 13:06:28 +02001560 self._def_entity(self.the_empty_object_type)
Eric Blake93bda4d2015-12-01 22:20:55 -07001561 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1562 'qstring', 'qdict', 'qlist',
1563 'qfloat', 'qbool'])
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001564 self._def_entity(QAPISchemaEnumType('QType', None, None,
1565 qtype_values, 'QTYPE'))
Markus Armbrusterac882192015-09-16 13:06:05 +02001566
Eric Blake93bda4d2015-12-01 22:20:55 -07001567 def _make_enum_members(self, values):
1568 return [QAPISchemaMember(v) for v in values]
1569
Eric Blake99df5282015-10-12 22:22:32 -06001570 def _make_implicit_enum_type(self, name, info, values):
Eric Blake93bda4d2015-12-01 22:20:55 -07001571 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake49823c42015-10-12 22:22:27 -06001572 name = name + 'Kind' # Use namespace reserved by add_name()
Eric Blake93bda4d2015-12-01 22:20:55 -07001573 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001574 name, info, None, self._make_enum_members(values), None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001575 return name
1576
Eric Blake99df5282015-10-12 22:22:32 -06001577 def _make_array_type(self, element_type, info):
Eric Blake255960d2015-10-26 16:34:43 -06001578 name = element_type + 'List' # Use namespace reserved by add_name()
Markus Armbrusterac882192015-09-16 13:06:05 +02001579 if not self.lookup_type(name):
Eric Blake99df5282015-10-12 22:22:32 -06001580 self._def_entity(QAPISchemaArrayType(name, info, element_type))
Markus Armbrusterac882192015-09-16 13:06:05 +02001581 return name
1582
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001583 def _make_implicit_object_type(self, name, info, doc, role, members):
Markus Armbrusterac882192015-09-16 13:06:05 +02001584 if not members:
1585 return None
Eric Blake88d4ef82015-11-18 01:52:50 -07001586 # See also QAPISchemaObjectTypeMember._pretty_owner()
Eric Blake75996972016-03-17 16:48:29 -06001587 name = 'q_obj_%s-%s' % (name, role)
Markus Armbrusterac882192015-09-16 13:06:05 +02001588 if not self.lookup_entity(name, QAPISchemaObjectType):
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001589 self._def_entity(QAPISchemaObjectType(name, info, doc, None,
Markus Armbrusterac882192015-09-16 13:06:05 +02001590 members, None))
1591 return name
1592
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001593 def _def_enum_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001594 name = expr['enum']
1595 data = expr['data']
1596 prefix = expr.get('prefix')
Eric Blake93bda4d2015-12-01 22:20:55 -07001597 self._def_entity(QAPISchemaEnumType(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001598 name, info, doc, self._make_enum_members(data), prefix))
Markus Armbrusterac882192015-09-16 13:06:05 +02001599
Eric Blake99df5282015-10-12 22:22:32 -06001600 def _make_member(self, name, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001601 optional = False
1602 if name.startswith('*'):
1603 name = name[1:]
1604 optional = True
1605 if isinstance(typ, list):
1606 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001607 typ = self._make_array_type(typ[0], info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001608 return QAPISchemaObjectTypeMember(name, typ, optional)
1609
Eric Blake99df5282015-10-12 22:22:32 -06001610 def _make_members(self, data, info):
1611 return [self._make_member(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001612 for (key, value) in data.iteritems()]
1613
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001614 def _def_struct_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001615 name = expr['struct']
1616 base = expr.get('base')
1617 data = expr['data']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001618 self._def_entity(QAPISchemaObjectType(name, info, doc, base,
Eric Blake99df5282015-10-12 22:22:32 -06001619 self._make_members(data, info),
Markus Armbrusterac882192015-09-16 13:06:05 +02001620 None))
Markus Armbrusterac882192015-09-16 13:06:05 +02001621
1622 def _make_variant(self, case, typ):
1623 return QAPISchemaObjectTypeVariant(case, typ)
1624
Eric Blake99df5282015-10-12 22:22:32 -06001625 def _make_simple_variant(self, case, typ, info):
Markus Armbrusterac882192015-09-16 13:06:05 +02001626 if isinstance(typ, list):
1627 assert len(typ) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001628 typ = self._make_array_type(typ[0], info)
1629 typ = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001630 typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
Markus Armbrusterac882192015-09-16 13:06:05 +02001631 return QAPISchemaObjectTypeVariant(case, typ)
1632
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001633 def _def_union_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001634 name = expr['union']
1635 data = expr['data']
1636 base = expr.get('base')
1637 tag_name = expr.get('discriminator')
Eric Blake46292ba2015-10-12 22:22:29 -06001638 tag_member = None
Eric Blakeac4338f2016-03-17 16:48:39 -06001639 if isinstance(base, dict):
1640 base = (self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001641 name, info, doc, 'base', self._make_members(base, info)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001642 if tag_name:
1643 variants = [self._make_variant(key, value)
1644 for (key, value) in data.iteritems()]
Eric Blakeda34a9b2015-11-18 01:52:36 -07001645 members = []
Markus Armbrusterac882192015-09-16 13:06:05 +02001646 else:
Eric Blake99df5282015-10-12 22:22:32 -06001647 variants = [self._make_simple_variant(key, value, info)
Markus Armbrusterac882192015-09-16 13:06:05 +02001648 for (key, value) in data.iteritems()]
Eric Blake9d3f3492015-12-01 22:20:50 -07001649 typ = self._make_implicit_enum_type(name, info,
1650 [v.name for v in variants])
1651 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
Eric Blakeda34a9b2015-11-18 01:52:36 -07001652 members = [tag_member]
Markus Armbrusterac882192015-09-16 13:06:05 +02001653 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001654 QAPISchemaObjectType(name, info, doc, base, members,
Markus Armbrusterac882192015-09-16 13:06:05 +02001655 QAPISchemaObjectTypeVariants(tag_name,
Eric Blake46292ba2015-10-12 22:22:29 -06001656 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001657 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001658
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001659 def _def_alternate_type(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001660 name = expr['alternate']
1661 data = expr['data']
1662 variants = [self._make_variant(key, value)
1663 for (key, value) in data.iteritems()]
Eric Blake0426d532015-12-01 22:20:48 -07001664 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001665 self._def_entity(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001666 QAPISchemaAlternateType(name, info, doc,
Markus Armbrusterac882192015-09-16 13:06:05 +02001667 QAPISchemaObjectTypeVariants(None,
Eric Blake46292ba2015-10-12 22:22:29 -06001668 tag_member,
Markus Armbrusterac882192015-09-16 13:06:05 +02001669 variants)))
Markus Armbrusterac882192015-09-16 13:06:05 +02001670
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001671 def _def_command(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001672 name = expr['command']
1673 data = expr.get('data')
1674 rets = expr.get('returns')
1675 gen = expr.get('gen', True)
1676 success_response = expr.get('success-response', True)
Eric Blake48825ca2016-07-13 21:50:19 -06001677 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001678 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001679 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001680 name, info, doc, 'arg', self._make_members(data, info))
Markus Armbrusterac882192015-09-16 13:06:05 +02001681 if isinstance(rets, list):
1682 assert len(rets) == 1
Eric Blake99df5282015-10-12 22:22:32 -06001683 rets = self._make_array_type(rets[0], info)
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001684 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1685 gen, success_response, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001686
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001687 def _def_event(self, expr, info, doc):
Markus Armbrusterac882192015-09-16 13:06:05 +02001688 name = expr['event']
1689 data = expr.get('data')
Eric Blake48825ca2016-07-13 21:50:19 -06001690 boxed = expr.get('boxed', False)
Markus Armbrusterac882192015-09-16 13:06:05 +02001691 if isinstance(data, OrderedDict):
Eric Blake99df5282015-10-12 22:22:32 -06001692 data = self._make_implicit_object_type(
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001693 name, info, doc, 'arg', self._make_members(data, info))
1694 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
Markus Armbrusterac882192015-09-16 13:06:05 +02001695
1696 def _def_exprs(self):
1697 for expr_elem in self.exprs:
1698 expr = expr_elem['expr']
1699 info = expr_elem['info']
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001700 doc = expr_elem.get('doc')
Markus Armbrusterac882192015-09-16 13:06:05 +02001701 if 'enum' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001702 self._def_enum_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001703 elif 'struct' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001704 self._def_struct_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001705 elif 'union' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001706 self._def_union_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001707 elif 'alternate' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001708 self._def_alternate_type(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001709 elif 'command' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001710 self._def_command(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001711 elif 'event' in expr:
Markus Armbruster069fb5b2017-03-15 13:57:03 +01001712 self._def_event(expr, info, doc)
Markus Armbrusterac882192015-09-16 13:06:05 +02001713 else:
1714 assert False
1715
1716 def check(self):
1717 for ent in self._entity_dict.values():
1718 ent.check(self)
1719
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001720 def visit(self, visitor):
Eric Blake25a0d9c2015-10-12 22:22:21 -06001721 visitor.visit_begin(self)
1722 for (name, entity) in sorted(self._entity_dict.items()):
1723 if visitor.visit_needed(entity):
1724 entity.visit(visitor)
Markus Armbruster3f7dc212015-09-16 13:06:07 +02001725 visitor.visit_end()
1726
Markus Armbruster2caba362013-07-27 17:41:56 +02001727
Markus Armbruster00e4b282015-06-10 10:04:36 +02001728#
1729# Code generation helpers
1730#
1731
Michael Roth0f923be2011-07-19 14:50:39 -05001732def camel_case(name):
1733 new_name = ''
1734 first = True
1735 for ch in name:
1736 if ch in ['_', '-']:
1737 first = True
1738 elif first:
1739 new_name += ch.upper()
1740 first = False
1741 else:
1742 new_name += ch.lower()
1743 return new_name
1744
Eric Blake437db252015-09-29 16:21:02 -06001745
Markus Armbruster849bc532015-05-14 06:50:53 -06001746# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1747# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1748# ENUM24_Name -> ENUM24_NAME
1749def camel_to_upper(value):
1750 c_fun_str = c_name(value, False)
1751 if value.isupper():
1752 return c_fun_str
1753
1754 new_name = ''
1755 l = len(c_fun_str)
1756 for i in range(l):
1757 c = c_fun_str[i]
1758 # When c is upper and no "_" appears before, do more checks
1759 if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
Eric Blake437db252015-09-29 16:21:02 -06001760 if i < l - 1 and c_fun_str[i + 1].islower():
1761 new_name += '_'
1762 elif c_fun_str[i - 1].isdigit():
Markus Armbruster849bc532015-05-14 06:50:53 -06001763 new_name += '_'
1764 new_name += c
1765 return new_name.lstrip('_').upper()
1766
Eric Blake437db252015-09-29 16:21:02 -06001767
Daniel P. Berrange351d36e2015-08-26 14:21:20 +01001768def c_enum_const(type_name, const_name, prefix=None):
1769 if prefix is not None:
1770 type_name = prefix
Eric Blaked20a5802015-11-18 01:53:01 -07001771 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
Markus Armbruster849bc532015-05-14 06:50:53 -06001772
Eric Blake18df5152015-05-14 06:50:48 -06001773c_name_trans = string.maketrans('.-', '__')
Markus Armbruster47299262015-05-14 06:50:47 -06001774
Eric Blake437db252015-09-29 16:21:02 -06001775
Eric Blakec6405b52015-05-14 06:50:55 -06001776# Map @name to a valid C identifier.
1777# If @protect, avoid returning certain ticklish identifiers (like
1778# C keywords) by prepending "q_".
1779#
1780# Used for converting 'name' from a 'name':'type' qapi definition
1781# into a generated struct member, as well as converting type names
1782# into substrings of a generated C function name.
1783# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1784# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
Eric Blake18df5152015-05-14 06:50:48 -06001785def c_name(name, protect=True):
Blue Swirl427a1a22012-07-30 15:46:55 +00001786 # ANSI X3J11/88-090, 3.1.1
1787 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
Eric Blake437db252015-09-29 16:21:02 -06001788 'default', 'do', 'double', 'else', 'enum', 'extern',
1789 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1790 'return', 'short', 'signed', 'sizeof', 'static',
1791 'struct', 'switch', 'typedef', 'union', 'unsigned',
1792 'void', 'volatile', 'while'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001793 # ISO/IEC 9899:1999, 6.4.1
1794 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1795 # ISO/IEC 9899:2011, 6.4.1
Eric Blake437db252015-09-29 16:21:02 -06001796 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1797 '_Noreturn', '_Static_assert', '_Thread_local'])
Blue Swirl427a1a22012-07-30 15:46:55 +00001798 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1799 # excluding _.*
1800 gcc_words = set(['asm', 'typeof'])
Tomoki Sekiyama6f880092013-08-07 11:39:43 -04001801 # C++ ISO/IEC 14882:2003 2.11
1802 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1803 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1804 'namespace', 'new', 'operator', 'private', 'protected',
1805 'public', 'reinterpret_cast', 'static_cast', 'template',
1806 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1807 'using', 'virtual', 'wchar_t',
1808 # alternative representations
1809 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1810 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
Paolo Bonzini10577252012-09-19 16:31:07 +02001811 # namespace pollution:
Eric Blake86ae1912016-02-02 07:51:41 -07001812 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
Eric Blakec43567c2015-11-18 01:52:52 -07001813 name = name.translate(c_name_trans)
Eric Blake437db252015-09-29 16:21:02 -06001814 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1815 | cpp_words | polluted_words):
Blue Swirl427a1a22012-07-30 15:46:55 +00001816 return "q_" + name
Eric Blakec43567c2015-11-18 01:52:52 -07001817 return name
Michael Roth0f923be2011-07-19 14:50:39 -05001818
Amos Kong05dfb262014-06-10 19:25:53 +08001819eatspace = '\033EATSPACE.'
Eric Blaked5573442015-05-14 06:50:54 -06001820pointer_suffix = ' *' + eatspace
Amos Kong05dfb262014-06-10 19:25:53 +08001821
Eric Blake437db252015-09-29 16:21:02 -06001822
Michael Roth0f923be2011-07-19 14:50:39 -05001823def genindent(count):
1824 ret = ""
Eric Blake437db252015-09-29 16:21:02 -06001825 for _ in range(count):
Michael Roth0f923be2011-07-19 14:50:39 -05001826 ret += " "
1827 return ret
1828
1829indent_level = 0
1830
Eric Blake437db252015-09-29 16:21:02 -06001831
Michael Roth0f923be2011-07-19 14:50:39 -05001832def push_indent(indent_amount=4):
1833 global indent_level
1834 indent_level += indent_amount
1835
Eric Blake437db252015-09-29 16:21:02 -06001836
Michael Roth0f923be2011-07-19 14:50:39 -05001837def pop_indent(indent_amount=4):
1838 global indent_level
1839 indent_level -= indent_amount
1840
Eric Blake437db252015-09-29 16:21:02 -06001841
Markus Armbruster77e703b2015-06-24 19:27:32 +02001842# Generate @code with @kwds interpolated.
1843# Obey indent_level, and strip eatspace.
Michael Roth0f923be2011-07-19 14:50:39 -05001844def cgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001845 raw = code % kwds
1846 if indent_level:
1847 indent = genindent(indent_level)
Markus Armbruster2752e5b2015-09-07 17:45:55 +02001848 # re.subn() lacks flags support before Python 2.7, use re.compile()
1849 raw = re.subn(re.compile("^.", re.MULTILINE),
1850 indent + r'\g<0>', raw)
Markus Armbruster77e703b2015-06-24 19:27:32 +02001851 raw = raw[0]
1852 return re.sub(re.escape(eatspace) + ' *', '', raw)
Michael Roth0f923be2011-07-19 14:50:39 -05001853
Eric Blake437db252015-09-29 16:21:02 -06001854
Michael Roth0f923be2011-07-19 14:50:39 -05001855def mcgen(code, **kwds):
Markus Armbruster77e703b2015-06-24 19:27:32 +02001856 if code[0] == '\n':
1857 code = code[1:]
1858 return cgen(code, **kwds)
Michael Roth0f923be2011-07-19 14:50:39 -05001859
Michael Roth0f923be2011-07-19 14:50:39 -05001860
1861def guardname(filename):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02001862 return c_name(filename, protect=False).upper()
Michael Rothc0afa9c2013-05-10 17:46:00 -05001863
Eric Blake437db252015-09-29 16:21:02 -06001864
Michael Rothc0afa9c2013-05-10 17:46:00 -05001865def guardstart(name):
1866 return mcgen('''
1867
1868#ifndef %(name)s
1869#define %(name)s
1870
1871''',
1872 name=guardname(name))
1873
Eric Blake437db252015-09-29 16:21:02 -06001874
Michael Rothc0afa9c2013-05-10 17:46:00 -05001875def guardend(name):
1876 return mcgen('''
1877
1878#endif /* %(name)s */
1879
1880''',
1881 name=guardname(name))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001882
Eric Blake437db252015-09-29 16:21:02 -06001883
Markus Armbrustere98859a2015-09-16 13:06:16 +02001884def gen_enum_lookup(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001885 ret = mcgen('''
1886
Markus Armbrustere98859a2015-09-16 13:06:16 +02001887const char *const %(c_name)s_lookup[] = {
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001888''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001889 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001890 for value in values:
1891 index = c_enum_const(name, value, prefix)
1892 ret += mcgen('''
1893 [%(index)s] = "%(value)s",
1894''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001895 index=index, value=value)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001896
Eric Blake7fb1cf12015-11-18 01:52:57 -07001897 max_index = c_enum_const(name, '_MAX', prefix)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001898 ret += mcgen('''
1899 [%(max_index)s] = NULL,
1900};
1901''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001902 max_index=max_index)
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001903 return ret
1904
Eric Blake437db252015-09-29 16:21:02 -06001905
Markus Armbrustere98859a2015-09-16 13:06:16 +02001906def gen_enum(name, values, prefix=None):
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001907 # append automatically generated _MAX value
Eric Blake7fb1cf12015-11-18 01:52:57 -07001908 enum_values = values + ['_MAX']
Markus Armbrustere98859a2015-09-16 13:06:16 +02001909
1910 ret = mcgen('''
1911
1912typedef enum %(c_name)s {
1913''',
1914 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001915
1916 i = 0
1917 for value in enum_values:
Markus Armbrustere98859a2015-09-16 13:06:16 +02001918 ret += mcgen('''
1919 %(c_enum)s = %(i)d,
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001920''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001921 c_enum=c_enum_const(name, value, prefix),
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001922 i=i)
1923 i += 1
1924
Markus Armbrustere98859a2015-09-16 13:06:16 +02001925 ret += mcgen('''
1926} %(c_name)s;
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001927''',
Markus Armbrustere98859a2015-09-16 13:06:16 +02001928 c_name=c_name(name))
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001929
Markus Armbrustere98859a2015-09-16 13:06:16 +02001930 ret += mcgen('''
1931
1932extern const char *const %(c_name)s_lookup[];
1933''',
1934 c_name=c_name(name))
1935 return ret
Markus Armbrusterefd2eaa2015-09-16 13:06:12 +02001936
Eric Blake437db252015-09-29 16:21:02 -06001937
Eric Blake48825ca2016-07-13 21:50:19 -06001938def gen_params(arg_type, boxed, extra):
Markus Armbruster03b43672015-09-16 13:06:20 +02001939 if not arg_type:
Eric Blakec8184082016-07-13 21:50:20 -06001940 assert not boxed
Markus Armbruster03b43672015-09-16 13:06:20 +02001941 return extra
Markus Armbruster03b43672015-09-16 13:06:20 +02001942 ret = ''
1943 sep = ''
Eric Blake48825ca2016-07-13 21:50:19 -06001944 if boxed:
Eric Blakec8184082016-07-13 21:50:20 -06001945 ret += '%s arg' % arg_type.c_param_type()
1946 sep = ', '
Eric Blake48825ca2016-07-13 21:50:19 -06001947 else:
1948 assert not arg_type.variants
1949 for memb in arg_type.members:
1950 ret += sep
1951 sep = ', '
1952 if memb.optional:
1953 ret += 'bool has_%s, ' % c_name(memb.name)
1954 ret += '%s %s' % (memb.type.c_param_type(),
1955 c_name(memb.name))
Markus Armbruster03b43672015-09-16 13:06:20 +02001956 if extra:
1957 ret += sep + extra
1958 return ret
1959
Eric Blake1f353342015-09-29 16:21:13 -06001960
Markus Armbruster00e4b282015-06-10 10:04:36 +02001961#
1962# Common command line parsing
1963#
1964
Eric Blake437db252015-09-29 16:21:02 -06001965
1966def parse_command_line(extra_options="", extra_long_options=[]):
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001967
1968 try:
1969 opts, args = getopt.gnu_getopt(sys.argv[1:],
Markus Armbruster16d80f62015-04-02 13:32:16 +02001970 "chp:o:" + extra_options,
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001971 ["source", "header", "prefix=",
Markus Armbruster16d80f62015-04-02 13:32:16 +02001972 "output-dir="] + extra_long_options)
Markus Armbruster291928a2015-12-18 08:52:41 +01001973 except getopt.GetoptError as err:
Markus Armbrusterb4540962015-04-02 13:17:34 +02001974 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001975 sys.exit(1)
1976
1977 output_dir = ""
1978 prefix = ""
1979 do_c = False
1980 do_h = False
1981 extra_opts = []
1982
1983 for oa in opts:
1984 o, a = oa
1985 if o in ("-p", "--prefix"):
Markus Armbruster1cf47a12015-07-01 13:13:54 +02001986 match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1987 if match.end() != len(a):
1988 print >>sys.stderr, \
1989 "%s: 'funny character '%s' in argument of --prefix" \
1990 % (sys.argv[0], a[match.end()])
1991 sys.exit(1)
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001992 prefix = a
Markus Armbruster2114f5a2015-04-02 13:12:21 +02001993 elif o in ("-o", "--output-dir"):
1994 output_dir = a + "/"
1995 elif o in ("-c", "--source"):
1996 do_c = True
1997 elif o in ("-h", "--header"):
1998 do_h = True
1999 else:
2000 extra_opts.append(oa)
2001
2002 if not do_c and not do_h:
2003 do_c = True
2004 do_h = True
2005
Markus Armbruster16d80f62015-04-02 13:32:16 +02002006 if len(args) != 1:
2007 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002008 sys.exit(1)
Markus Armbruster54414042015-06-09 16:22:45 +02002009 fname = args[0]
Markus Armbrusterb4540962015-04-02 13:17:34 +02002010
Markus Armbruster54414042015-06-09 16:22:45 +02002011 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002012
Markus Armbruster00e4b282015-06-10 10:04:36 +02002013#
2014# Generate output files with boilerplate
2015#
2016
Eric Blake437db252015-09-29 16:21:02 -06002017
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002018def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
2019 c_comment, h_comment):
Markus Armbruster00dfc3b2015-06-27 07:27:21 +02002020 guard = guardname(prefix + h_file)
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002021 c_file = output_dir + prefix + c_file
2022 h_file = output_dir + prefix + h_file
2023
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002024 if output_dir:
2025 try:
2026 os.makedirs(output_dir)
Markus Armbruster291928a2015-12-18 08:52:41 +01002027 except os.error as e:
Markus Armbrusterc4f498f2015-09-03 10:24:25 +02002028 if e.errno != errno.EEXIST:
2029 raise
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002030
2031 def maybe_open(really, name, opt):
2032 if really:
2033 return open(name, opt)
2034 else:
2035 import StringIO
2036 return StringIO.StringIO()
2037
2038 fdef = maybe_open(do_c, c_file, 'w')
2039 fdecl = maybe_open(do_h, h_file, 'w')
2040
2041 fdef.write(mcgen('''
2042/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2043%(comment)s
2044''',
Eric Blake437db252015-09-29 16:21:02 -06002045 comment=c_comment))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002046
2047 fdecl.write(mcgen('''
2048/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2049%(comment)s
2050#ifndef %(guard)s
2051#define %(guard)s
2052
2053''',
Eric Blake437db252015-09-29 16:21:02 -06002054 comment=h_comment, guard=guard))
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002055
2056 return (fdef, fdecl)
2057
Eric Blake437db252015-09-29 16:21:02 -06002058
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002059def close_output(fdef, fdecl):
2060 fdecl.write('''
2061#endif
2062''')
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002063 fdecl.close()
Markus Armbruster12f8e1b2015-04-02 14:46:39 +02002064 fdef.close()