]> Git Repo - qemu.git/blob - scripts/qapi/gen.py
qapi: modify docstrings to be sphinx-compatible
[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 from contextlib import contextmanager
19
20 from qapi.common import *
21 from qapi.schema import QAPISchemaVisitor
22
23
24 class QAPIGen:
25
26     def __init__(self, fname):
27         self.fname = fname
28         self._preamble = ''
29         self._body = ''
30
31     def preamble_add(self, text):
32         self._preamble += text
33
34     def add(self, text):
35         self._body += text
36
37     def get_content(self):
38         return self._top() + self._preamble + self._body + self._bottom()
39
40     def _top(self):
41         return ''
42
43     def _bottom(self):
44         return ''
45
46     def write(self, output_dir):
47         # Include paths starting with ../ are used to reuse modules of the main
48         # schema in specialised schemas. Don't overwrite the files that are
49         # already generated for the main schema.
50         if self.fname.startswith('../'):
51             return
52         pathname = os.path.join(output_dir, self.fname)
53         odir = os.path.dirname(pathname)
54         if odir:
55             try:
56                 os.makedirs(odir)
57             except os.error as e:
58                 if e.errno != errno.EEXIST:
59                     raise
60         fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
61         f = open(fd, 'r+', encoding='utf-8')
62         text = self.get_content()
63         oldtext = f.read(len(text) + 1)
64         if text != oldtext:
65             f.seek(0)
66             f.truncate(0)
67             f.write(text)
68         f.close()
69
70
71 def _wrap_ifcond(ifcond, before, after):
72     if before == after:
73         return after   # suppress empty #if ... #endif
74
75     assert after.startswith(before)
76     out = before
77     added = after[len(before):]
78     if added[0] == '\n':
79         out += '\n'
80         added = added[1:]
81     out += gen_if(ifcond)
82     out += added
83     out += gen_endif(ifcond)
84     return out
85
86
87 class QAPIGenCCode(QAPIGen):
88
89     def __init__(self, fname):
90         super().__init__(fname)
91         self._start_if = None
92
93     def start_if(self, ifcond):
94         assert self._start_if is None
95         self._start_if = (ifcond, self._body, self._preamble)
96
97     def end_if(self):
98         assert self._start_if
99         self._wrap_ifcond()
100         self._start_if = None
101
102     def _wrap_ifcond(self):
103         self._body = _wrap_ifcond(self._start_if[0],
104                                   self._start_if[1], self._body)
105         self._preamble = _wrap_ifcond(self._start_if[0],
106                                       self._start_if[2], self._preamble)
107
108     def get_content(self):
109         assert self._start_if is None
110         return super().get_content()
111
112
113 class QAPIGenC(QAPIGenCCode):
114
115     def __init__(self, fname, blurb, pydoc):
116         super().__init__(fname)
117         self._blurb = blurb
118         self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
119                                                   re.MULTILINE))
120
121     def _top(self):
122         return mcgen('''
123 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
124
125 /*
126 %(blurb)s
127  *
128  * %(copyright)s
129  *
130  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
131  * See the COPYING.LIB file in the top-level directory.
132  */
133
134 ''',
135                      blurb=self._blurb, copyright=self._copyright)
136
137     def _bottom(self):
138         return mcgen('''
139
140 /* Dummy declaration to prevent empty .o file */
141 char qapi_dummy_%(name)s;
142 ''',
143                      name=c_fname(self.fname))
144
145
146 class QAPIGenH(QAPIGenC):
147
148     def _top(self):
149         return super()._top() + guardstart(self.fname)
150
151     def _bottom(self):
152         return guardend(self.fname)
153
154
155 @contextmanager
156 def ifcontext(ifcond, *args):
157     """
158     A with-statement context manager that wraps with `start_if()` / `end_if()`.
159
160     :param ifcond: A list of conditionals, passed to `start_if()`.
161     :param args: any number of `QAPIGenCCode`.
162
163     Example::
164
165         with ifcontext(ifcond, self._genh, self._genc):
166             modify self._genh and self._genc ...
167
168     Is equivalent to calling::
169
170         self._genh.start_if(ifcond)
171         self._genc.start_if(ifcond)
172         modify self._genh and self._genc ...
173         self._genh.end_if()
174         self._genc.end_if()
175     """
176     for arg in args:
177         arg.start_if(ifcond)
178     yield
179     for arg in args:
180         arg.end_if()
181
182
183 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
184
185     def __init__(self, prefix, what, blurb, pydoc):
186         self._prefix = prefix
187         self._what = what
188         self._genc = QAPIGenC(self._prefix + self._what + '.c',
189                               blurb, pydoc)
190         self._genh = QAPIGenH(self._prefix + self._what + '.h',
191                               blurb, pydoc)
192
193     def write(self, output_dir):
194         self._genc.write(output_dir)
195         self._genh.write(output_dir)
196
197
198 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
199
200     def __init__(self, prefix, what, user_blurb, builtin_blurb, pydoc):
201         self._prefix = prefix
202         self._what = what
203         self._user_blurb = user_blurb
204         self._builtin_blurb = builtin_blurb
205         self._pydoc = pydoc
206         self._genc = None
207         self._genh = None
208         self._module = {}
209         self._main_module = None
210
211     @staticmethod
212     def _is_user_module(name):
213         return name and not name.startswith('./')
214
215     @staticmethod
216     def _is_builtin_module(name):
217         return not name
218
219     def _module_dirname(self, what, name):
220         if self._is_user_module(name):
221             return os.path.dirname(name)
222         return ''
223
224     def _module_basename(self, what, name):
225         ret = '' if self._is_builtin_module(name) else self._prefix
226         if self._is_user_module(name):
227             basename = os.path.basename(name)
228             ret += what
229             if name != self._main_module:
230                 ret += '-' + os.path.splitext(basename)[0]
231         else:
232             name = name[2:] if name else 'builtin'
233             ret += re.sub(r'-', '-' + name + '-', what)
234         return ret
235
236     def _module_filename(self, what, name):
237         return os.path.join(self._module_dirname(what, name),
238                             self._module_basename(what, name))
239
240     def _add_module(self, name, blurb):
241         basename = self._module_filename(self._what, name)
242         genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
243         genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
244         self._module[name] = (genc, genh)
245         self._genc, self._genh = self._module[name]
246
247     def _add_user_module(self, name, blurb):
248         assert self._is_user_module(name)
249         if self._main_module is None:
250             self._main_module = name
251         self._add_module(name, blurb)
252
253     def _add_system_module(self, name, blurb):
254         self._add_module(name and './' + name, blurb)
255
256     def write(self, output_dir, opt_builtins=False):
257         for name in self._module:
258             if self._is_builtin_module(name) and not opt_builtins:
259                 continue
260             (genc, genh) = self._module[name]
261             genc.write(output_dir)
262             genh.write(output_dir)
263
264     def _begin_system_module(self, name):
265         pass
266
267     def _begin_user_module(self, name):
268         pass
269
270     def visit_module(self, name):
271         if name is None:
272             if self._builtin_blurb:
273                 self._add_system_module(None, self._builtin_blurb)
274                 self._begin_system_module(name)
275             else:
276                 # The built-in module has not been created.  No code may
277                 # be generated.
278                 self._genc = None
279                 self._genh = None
280         else:
281             self._add_user_module(name, self._user_blurb)
282             self._begin_user_module(name)
283
284     def visit_include(self, name, info):
285         relname = os.path.relpath(self._module_filename(self._what, name),
286                                   os.path.dirname(self._genh.fname))
287         self._genh.preamble_add(mcgen('''
288 #include "%(relname)s.h"
289 ''',
290                                       relname=relname))
This page took 0.039079 seconds and 4 git commands to generate.