123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- # -*- coding: utf-8 -*-
- #
- # QAPI code generation
- #
- # Copyright (c) 2018-2019 Red Hat Inc.
- #
- # Authors:
- # Markus Armbruster <armbru@redhat.com>
- # Marc-André Lureau <marcandre.lureau@redhat.com>
- #
- # This work is licensed under the terms of the GNU GPL, version 2.
- # See the COPYING file in the top-level directory.
- import errno
- import os
- import re
- import sys
- from contextlib import contextmanager
- from qapi.common import *
- from qapi.schema import QAPISchemaVisitor
- class QAPIGen(object):
- def __init__(self, fname):
- self.fname = fname
- self._preamble = ''
- self._body = ''
- def preamble_add(self, text):
- self._preamble += text
- def add(self, text):
- self._body += text
- def get_content(self):
- return self._top() + self._preamble + self._body + self._bottom()
- def _top(self):
- return ''
- def _bottom(self):
- return ''
- def write(self, output_dir):
- pathname = os.path.join(output_dir, self.fname)
- dir = os.path.dirname(pathname)
- if dir:
- try:
- os.makedirs(dir)
- except os.error as e:
- if e.errno != errno.EEXIST:
- raise
- fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
- if sys.version_info[0] >= 3:
- f = open(fd, 'r+', encoding='utf-8')
- else:
- f = os.fdopen(fd, 'r+')
- text = self.get_content()
- oldtext = f.read(len(text) + 1)
- if text != oldtext:
- f.seek(0)
- f.truncate(0)
- f.write(text)
- f.close()
- def _wrap_ifcond(ifcond, before, after):
- if before == after:
- return after # suppress empty #if ... #endif
- assert after.startswith(before)
- out = before
- added = after[len(before):]
- if added[0] == '\n':
- out += '\n'
- added = added[1:]
- out += gen_if(ifcond)
- out += added
- out += gen_endif(ifcond)
- return out
- class QAPIGenCCode(QAPIGen):
- def __init__(self, fname):
- QAPIGen.__init__(self, fname)
- self._start_if = None
- def start_if(self, ifcond):
- assert self._start_if is None
- self._start_if = (ifcond, self._body, self._preamble)
- def end_if(self):
- assert self._start_if
- self._wrap_ifcond()
- self._start_if = None
- def _wrap_ifcond(self):
- self._body = _wrap_ifcond(self._start_if[0],
- self._start_if[1], self._body)
- self._preamble = _wrap_ifcond(self._start_if[0],
- self._start_if[2], self._preamble)
- def get_content(self):
- assert self._start_if is None
- return QAPIGen.get_content(self)
- class QAPIGenC(QAPIGenCCode):
- def __init__(self, fname, blurb, pydoc):
- QAPIGenCCode.__init__(self, fname)
- self._blurb = blurb
- self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
- re.MULTILINE))
- def _top(self):
- return mcgen('''
- /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
- /*
- %(blurb)s
- *
- * %(copyright)s
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING.LIB file in the top-level directory.
- */
- ''',
- blurb=self._blurb, copyright=self._copyright)
- def _bottom(self):
- return mcgen('''
- /* Dummy declaration to prevent empty .o file */
- char qapi_dummy_%(name)s;
- ''',
- name=c_fname(self.fname))
- class QAPIGenH(QAPIGenC):
- def _top(self):
- return QAPIGenC._top(self) + guardstart(self.fname)
- def _bottom(self):
- return guardend(self.fname)
- @contextmanager
- def ifcontext(ifcond, *args):
- """A 'with' statement context manager to wrap with start_if()/end_if()
- *args: any number of QAPIGenCCode
- Example::
- with ifcontext(ifcond, self._genh, self._genc):
- modify self._genh and self._genc ...
- Is equivalent to calling::
- self._genh.start_if(ifcond)
- self._genc.start_if(ifcond)
- modify self._genh and self._genc ...
- self._genh.end_if()
- self._genc.end_if()
- """
- for arg in args:
- arg.start_if(ifcond)
- yield
- for arg in args:
- arg.end_if()
- class QAPIGenDoc(QAPIGen):
- def _top(self):
- return (QAPIGen._top(self)
- + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
- class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
- def __init__(self, prefix, what, blurb, pydoc):
- self._prefix = prefix
- self._what = what
- self._genc = QAPIGenC(self._prefix + self._what + '.c',
- blurb, pydoc)
- self._genh = QAPIGenH(self._prefix + self._what + '.h',
- blurb, pydoc)
- def write(self, output_dir):
- self._genc.write(output_dir)
- self._genh.write(output_dir)
- class QAPISchemaModularCVisitor(QAPISchemaVisitor):
- def __init__(self, prefix, what, blurb, pydoc):
- self._prefix = prefix
- self._what = what
- self._blurb = blurb
- self._pydoc = pydoc
- self._genc = None
- self._genh = None
- self._module = {}
- self._main_module = None
- @staticmethod
- def _is_user_module(name):
- return name and not name.startswith('./')
- @staticmethod
- def _is_builtin_module(name):
- return not name
- def _module_dirname(self, what, name):
- if self._is_user_module(name):
- return os.path.dirname(name)
- return ''
- def _module_basename(self, what, name):
- ret = '' if self._is_builtin_module(name) else self._prefix
- if self._is_user_module(name):
- basename = os.path.basename(name)
- ret += what
- if name != self._main_module:
- ret += '-' + os.path.splitext(basename)[0]
- else:
- name = name[2:] if name else 'builtin'
- ret += re.sub(r'-', '-' + name + '-', what)
- return ret
- def _module_filename(self, what, name):
- return os.path.join(self._module_dirname(what, name),
- self._module_basename(what, name))
- def _add_module(self, name, blurb):
- basename = self._module_filename(self._what, name)
- genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
- genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
- self._module[name] = (genc, genh)
- self._set_module(name)
- def _add_user_module(self, name, blurb):
- assert self._is_user_module(name)
- if self._main_module is None:
- self._main_module = name
- self._add_module(name, blurb)
- def _add_system_module(self, name, blurb):
- self._add_module(name and './' + name, blurb)
- def _set_module(self, name):
- self._genc, self._genh = self._module[name]
- def write(self, output_dir, opt_builtins=False):
- for name in self._module:
- if self._is_builtin_module(name) and not opt_builtins:
- continue
- (genc, genh) = self._module[name]
- genc.write(output_dir)
- genh.write(output_dir)
- def _begin_user_module(self, name):
- pass
- def visit_module(self, name):
- if name in self._module:
- self._set_module(name)
- elif self._is_builtin_module(name):
- # The built-in module has not been created. No code may
- # be generated.
- self._genc = None
- self._genh = None
- else:
- self._add_user_module(name, self._blurb)
- self._begin_user_module(name)
- def visit_include(self, name, info):
- relname = os.path.relpath(self._module_filename(self._what, name),
- os.path.dirname(self._genh.fname))
- self._genh.preamble_add(mcgen('''
- #include "%(relname)s.h"
- ''',
- relname=relname))
|