]> Git Repo - qemu.git/blob - scripts/qapi/gen.py
Merge remote-tracking branch 'remotes/huth-gitlab/tags/pull-request-2020-01-17' into...
[qemu.git] / scripts / qapi / gen.py
1 # -*- coding: utf-8 -*-
2 #
3 # QAPI code generation
4 #
5 # Copyright (c) 2018-2019 Red Hat Inc.
6 #
7 # Authors:
8 #  Markus Armbruster <[email protected]>
9 #  Marc-AndrĂ© Lureau <[email protected]>
10 #
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
13
14
15 import errno
16 import os
17 import re
18 import sys
19 from contextlib import contextmanager
20
21 from qapi.common import *
22 from qapi.schema import QAPISchemaVisitor
23
24
25 class QAPIGen(object):
26
27     def __init__(self, fname):
28         self.fname = fname
29         self._preamble = ''
30         self._body = ''
31
32     def preamble_add(self, text):
33         self._preamble += text
34
35     def add(self, text):
36         self._body += text
37
38     def get_content(self):
39         return self._top() + self._preamble + self._body + self._bottom()
40
41     def _top(self):
42         return ''
43
44     def _bottom(self):
45         return ''
46
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()
68
69
70 def _wrap_ifcond(ifcond, before, after):
71     if before == after:
72         return after   # suppress empty #if ... #endif
73
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
84
85
86 class QAPIGenCCode(QAPIGen):
87
88     def __init__(self, fname):
89         QAPIGen.__init__(self, fname)
90         self._start_if = None
91
92     def start_if(self, ifcond):
93         assert self._start_if is None
94         self._start_if = (ifcond, self._body, self._preamble)
95
96     def end_if(self):
97         assert self._start_if
98         self._wrap_ifcond()
99         self._start_if = None
100
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)
106
107     def get_content(self):
108         assert self._start_if is None
109         return QAPIGen.get_content(self)
110
111
112 class QAPIGenC(QAPIGenCCode):
113
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))
119
120     def _top(self):
121         return mcgen('''
122 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
123
124 /*
125 %(blurb)s
126  *
127  * %(copyright)s
128  *
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.
131  */
132
133 ''',
134                      blurb=self._blurb, copyright=self._copyright)
135
136     def _bottom(self):
137         return mcgen('''
138
139 /* Dummy declaration to prevent empty .o file */
140 char qapi_dummy_%(name)s;
141 ''',
142                      name=c_fname(self.fname))
143
144
145 class QAPIGenH(QAPIGenC):
146
147     def _top(self):
148         return QAPIGenC._top(self) + guardstart(self.fname)
149
150     def _bottom(self):
151         return guardend(self.fname)
152
153
154 @contextmanager
155 def ifcontext(ifcond, *args):
156     """A 'with' statement context manager to wrap with start_if()/end_if()
157
158     *args: any number of QAPIGenCCode
159
160     Example::
161
162         with ifcontext(ifcond, self._genh, self._genc):
163             modify self._genh and self._genc ...
164
165     Is equivalent to calling::
166
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()
172     """
173     for arg in args:
174         arg.start_if(ifcond)
175     yield
176     for arg in args:
177         arg.end_if()
178
179
180 class QAPIGenDoc(QAPIGen):
181
182     def _top(self):
183         return (QAPIGen._top(self)
184                 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
185
186
187 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
188
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)
196
197     def write(self, output_dir):
198         self._genc.write(output_dir)
199         self._genh.write(output_dir)
200
201
202 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
203
204     def __init__(self, prefix, what, user_blurb, builtin_blurb, pydoc):
205         self._prefix = prefix
206         self._what = what
207         self._user_blurb = user_blurb
208         self._builtin_blurb = builtin_blurb
209         self._pydoc = pydoc
210         self._genc = None
211         self._genh = None
212         self._module = {}
213         self._main_module = None
214
215     @staticmethod
216     def _is_user_module(name):
217         return name and not name.startswith('./')
218
219     @staticmethod
220     def _is_builtin_module(name):
221         return not name
222
223     def _module_dirname(self, what, name):
224         if self._is_user_module(name):
225             return os.path.dirname(name)
226         return ''
227
228     def _module_basename(self, what, name):
229         ret = '' if self._is_builtin_module(name) else self._prefix
230         if self._is_user_module(name):
231             basename = os.path.basename(name)
232             ret += what
233             if name != self._main_module:
234                 ret += '-' + os.path.splitext(basename)[0]
235         else:
236             name = name[2:] if name else 'builtin'
237             ret += re.sub(r'-', '-' + name + '-', what)
238         return ret
239
240     def _module_filename(self, what, name):
241         return os.path.join(self._module_dirname(what, name),
242                             self._module_basename(what, name))
243
244     def _add_module(self, name, blurb):
245         basename = self._module_filename(self._what, name)
246         genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
247         genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
248         self._module[name] = (genc, genh)
249         self._genc, self._genh = self._module[name]
250
251     def _add_user_module(self, name, blurb):
252         assert self._is_user_module(name)
253         if self._main_module is None:
254             self._main_module = name
255         self._add_module(name, blurb)
256
257     def _add_system_module(self, name, blurb):
258         self._add_module(name and './' + name, blurb)
259
260     def write(self, output_dir, opt_builtins=False):
261         for name in self._module:
262             if self._is_builtin_module(name) and not opt_builtins:
263                 continue
264             (genc, genh) = self._module[name]
265             genc.write(output_dir)
266             genh.write(output_dir)
267
268     def _begin_user_module(self, name):
269         pass
270
271     def visit_module(self, name):
272         if name is None:
273             if self._builtin_blurb:
274                 self._add_system_module(None, self._builtin_blurb)
275                 self._begin_system_module(name)
276             else:
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._user_blurb)
283             self._begin_user_module(name)
284
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))
This page took 0.039306 seconds and 4 git commands to generate.