Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20191220' into...
[qemu/ar7.git] / scripts / qapi / gen.py
blob112b6d94c5df93f54125fe8d0b3b155a9e6fd8c5
1 # -*- coding: utf-8 -*-
3 # QAPI code generation
5 # Copyright (c) 2018-2019 Red Hat Inc.
7 # Authors:
8 # Markus Armbruster <armbru@redhat.com>
9 # Marc-André Lureau <marcandre.lureau@redhat.com>
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
15 import errno
16 import os
17 import re
18 import sys
19 from contextlib import contextmanager
21 from qapi.common import *
22 from qapi.schema import QAPISchemaVisitor
25 class QAPIGen(object):
27 def __init__(self, fname):
28 self.fname = fname
29 self._preamble = ''
30 self._body = ''
32 def preamble_add(self, text):
33 self._preamble += text
35 def add(self, text):
36 self._body += text
38 def get_content(self):
39 return self._top() + self._preamble + self._body + self._bottom()
41 def _top(self):
42 return ''
44 def _bottom(self):
45 return ''
47 def write(self, output_dir):
48 pathname = os.path.join(output_dir, self.fname)
49 dir = os.path.dirname(pathname)
50 if dir:
51 try:
52 os.makedirs(dir)
53 except os.error as e:
54 if e.errno != errno.EEXIST:
55 raise
56 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
57 if sys.version_info[0] >= 3:
58 f = open(fd, 'r+', encoding='utf-8')
59 else:
60 f = os.fdopen(fd, 'r+')
61 text = self.get_content()
62 oldtext = f.read(len(text) + 1)
63 if text != oldtext:
64 f.seek(0)
65 f.truncate(0)
66 f.write(text)
67 f.close()
70 def _wrap_ifcond(ifcond, before, after):
71 if before == after:
72 return after # suppress empty #if ... #endif
74 assert after.startswith(before)
75 out = before
76 added = after[len(before):]
77 if added[0] == '\n':
78 out += '\n'
79 added = added[1:]
80 out += gen_if(ifcond)
81 out += added
82 out += gen_endif(ifcond)
83 return out
86 class QAPIGenCCode(QAPIGen):
88 def __init__(self, fname):
89 QAPIGen.__init__(self, fname)
90 self._start_if = None
92 def start_if(self, ifcond):
93 assert self._start_if is None
94 self._start_if = (ifcond, self._body, self._preamble)
96 def end_if(self):
97 assert self._start_if
98 self._wrap_ifcond()
99 self._start_if = None
101 def _wrap_ifcond(self):
102 self._body = _wrap_ifcond(self._start_if[0],
103 self._start_if[1], self._body)
104 self._preamble = _wrap_ifcond(self._start_if[0],
105 self._start_if[2], self._preamble)
107 def get_content(self):
108 assert self._start_if is None
109 return QAPIGen.get_content(self)
112 class QAPIGenC(QAPIGenCCode):
114 def __init__(self, fname, blurb, pydoc):
115 QAPIGenCCode.__init__(self, fname)
116 self._blurb = blurb
117 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
118 re.MULTILINE))
120 def _top(self):
121 return mcgen('''
122 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
125 %(blurb)s
127 * %(copyright)s
129 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
130 * See the COPYING.LIB file in the top-level directory.
133 ''',
134 blurb=self._blurb, copyright=self._copyright)
136 def _bottom(self):
137 return mcgen('''
139 /* Dummy declaration to prevent empty .o file */
140 char qapi_dummy_%(name)s;
141 ''',
142 name=c_fname(self.fname))
145 class QAPIGenH(QAPIGenC):
147 def _top(self):
148 return QAPIGenC._top(self) + guardstart(self.fname)
150 def _bottom(self):
151 return guardend(self.fname)
154 @contextmanager
155 def ifcontext(ifcond, *args):
156 """A 'with' statement context manager to wrap with start_if()/end_if()
158 *args: any number of QAPIGenCCode
160 Example::
162 with ifcontext(ifcond, self._genh, self._genc):
163 modify self._genh and self._genc ...
165 Is equivalent to calling::
167 self._genh.start_if(ifcond)
168 self._genc.start_if(ifcond)
169 modify self._genh and self._genc ...
170 self._genh.end_if()
171 self._genc.end_if()
173 for arg in args:
174 arg.start_if(ifcond)
175 yield
176 for arg in args:
177 arg.end_if()
180 class QAPIGenDoc(QAPIGen):
182 def _top(self):
183 return (QAPIGen._top(self)
184 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
187 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
189 def __init__(self, prefix, what, blurb, pydoc):
190 self._prefix = prefix
191 self._what = what
192 self._genc = QAPIGenC(self._prefix + self._what + '.c',
193 blurb, pydoc)
194 self._genh = QAPIGenH(self._prefix + self._what + '.h',
195 blurb, pydoc)
197 def write(self, output_dir):
198 self._genc.write(output_dir)
199 self._genh.write(output_dir)
202 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
204 def __init__(self, prefix, what, blurb, pydoc):
205 self._prefix = prefix
206 self._what = what
207 self._blurb = blurb
208 self._pydoc = pydoc
209 self._genc = None
210 self._genh = None
211 self._module = {}
212 self._main_module = None
214 @staticmethod
215 def _is_user_module(name):
216 return name and not name.startswith('./')
218 @staticmethod
219 def _is_builtin_module(name):
220 return not name
222 def _module_dirname(self, what, name):
223 if self._is_user_module(name):
224 return os.path.dirname(name)
225 return ''
227 def _module_basename(self, what, name):
228 ret = '' if self._is_builtin_module(name) else self._prefix
229 if self._is_user_module(name):
230 basename = os.path.basename(name)
231 ret += what
232 if name != self._main_module:
233 ret += '-' + os.path.splitext(basename)[0]
234 else:
235 name = name[2:] if name else 'builtin'
236 ret += re.sub(r'-', '-' + name + '-', what)
237 return ret
239 def _module_filename(self, what, name):
240 return os.path.join(self._module_dirname(what, name),
241 self._module_basename(what, name))
243 def _add_module(self, name, blurb):
244 basename = self._module_filename(self._what, name)
245 genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
246 genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
247 self._module[name] = (genc, genh)
248 self._set_module(name)
250 def _add_user_module(self, name, blurb):
251 assert self._is_user_module(name)
252 if self._main_module is None:
253 self._main_module = name
254 self._add_module(name, blurb)
256 def _add_system_module(self, name, blurb):
257 self._add_module(name and './' + name, blurb)
259 def _set_module(self, name):
260 self._genc, self._genh = self._module[name]
262 def write(self, output_dir, opt_builtins=False):
263 for name in self._module:
264 if self._is_builtin_module(name) and not opt_builtins:
265 continue
266 (genc, genh) = self._module[name]
267 genc.write(output_dir)
268 genh.write(output_dir)
270 def _begin_user_module(self, name):
271 pass
273 def visit_module(self, name):
274 if name in self._module:
275 self._set_module(name)
276 elif self._is_builtin_module(name):
277 # The built-in module has not been created. No code may
278 # be generated.
279 self._genc = None
280 self._genh = None
281 else:
282 self._add_user_module(name, self._blurb)
283 self._begin_user_module(name)
285 def visit_include(self, name, info):
286 relname = os.path.relpath(self._module_filename(self._what, name),
287 os.path.dirname(self._genh.fname))
288 self._genh.preamble_add(mcgen('''
289 #include "%(relname)s.h"
290 ''',
291 relname=relname))