Skip to content

Commit 1557a7e

Browse files
authored
Merge pull request #17 from ispras/customization
Boilerplate customization Make QDT more friendly for boilerplate customization and add an example. Side changes: - support v4.1.0 ### v2 - fix errors - prettify example of boilerplate customization
2 parents 161c5fc + eff26f0 commit 1557a7e

File tree

5 files changed

+172
-25
lines changed

5 files changed

+172
-25
lines changed

examples/customizing_device.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
from sys import (
2+
path as PYTHONPATH,
3+
)
4+
from os import (
5+
environ,
6+
)
7+
from os.path import (
8+
abspath,
9+
dirname,
10+
)
11+
12+
# Imports below are from QDT.
13+
PYTHONPATH.insert(0, dirname(dirname(abspath(__file__))))
14+
15+
from qemu import (
16+
qvd_load_with_cache,
17+
describable,
18+
SysBusDeviceType,
19+
QProject,
20+
)
21+
from source import (
22+
Function,
23+
Type,
24+
Header,
25+
Call,
26+
)
27+
28+
29+
@describable
30+
class CustomSBDType(SysBusDeviceType):
31+
""" This is an example of system bus device boilerplate extension with
32+
custom functionality.
33+
"""
34+
35+
def fill_source(self):
36+
""" Adds a hook callback in MMIO read handler.
37+
"""
38+
39+
super(CustomSBDType, self).fill_source()
40+
41+
# Now, code of device's module is generated. It's time to patch it!
42+
43+
# Assuming, your code uses functions and types from your header.
44+
custom_h = Header("custom-api.h", is_global = True)
45+
46+
# If your header is inside Qemu source tree and under Git, then you
47+
# likely should write above statement as follows:
48+
#
49+
# custom_h = Header["custom/custom-api.h"]
50+
#
51+
# Because the header is already registered.
52+
53+
# To use custom functions (and other things) you must declare them.
54+
# Note that, macros are added automatically if header is inside Qemu
55+
# source tree.
56+
# Only name is sufficient because no code will be generated for them.
57+
custom_h.add_types([
58+
Function("custom_callback")
59+
])
60+
61+
read_func = self.find_mmio_read_helper(0)
62+
63+
# We are going to pass second and following argument values of the
64+
# read helper to the custom callback.
65+
# Of course, you can made different choice in your code.
66+
args2callback = read_func.args[1:]
67+
68+
# Insert function call statement before 3-nd statement.
69+
# Note, first statements are likely variable declarations.
70+
# This is for prettiness only.
71+
read_func.body.children.insert(2,
72+
Call("custom_callback", *args2callback)
73+
)
74+
75+
def find_mmio_read_helper(self, mmioN):
76+
# Evaluate name of the read helper.
77+
# This code came from superclass `fill_source`.
78+
component = self.get_Ith_mmio_id_component(mmioN)
79+
name = self.qtn.for_id_name + "_" + component + "_read"
80+
81+
# Currently all functions (including `static`) are in global name
82+
# space (it's an implementation limitation yet).
83+
# And they are "types"... Here it's more generic term then in C.
84+
return Type[name]
85+
86+
87+
def main():
88+
p = QProject(
89+
descriptions = [
90+
CustomSBDDescription(# This class is defined by `@describable`
91+
name = "custom_device",
92+
directory = "misc",
93+
mmio_num = 1
94+
),
95+
]
96+
)
97+
98+
# We need information about target Qemu source tree.
99+
# QDT gets it starting from build directory (where `configure` has work).
100+
qemu_build = environ["QEMU_BUILD_DIR"]
101+
102+
qemu_version_description = qvd_load_with_cache(qemu_build,
103+
version = "v4.1.0"
104+
)
105+
# First time the loading may take few minutes because Qemu sources
106+
# are analyzed.
107+
# Then result is cached in a file to be reused.
108+
109+
# Apply Qemu's source code environment.
110+
qemu_version_description.use()
111+
112+
# And finally, generate the boilerplate.
113+
p.gen_all(qemu_src = qemu_version_description.src_path)
114+
115+
116+
if __name__ == "__main__":
117+
exit(main() or 0)

qemu/project.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,6 @@ def register_in_build_system(self, folder, known_targets):
132132
if not isfile(Makefile_obj):
133133
open(Makefile_obj, "w").close()
134134

135-
def make_src_dirs(self, full_path, known_targets):
136-
if not isdir(full_path):
137-
# Provide required directory.
138-
makedirs(full_path)
139-
140-
self.register_in_build_system(full_path, known_targets)
141-
142135
def gen(self, *args, **kw):
143136
"Backward compatibility wrapper for co_gen"
144137
callco(self.co_gen(*args, **kw))

qemu/qom_desc.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ def describable(QOMTemplate):
6060
module.__dict__[desc_name] = desc_class
6161

6262
# export the description class
63-
module.__all__.append(desc_name)
63+
try:
64+
module.__all__.append(desc_name)
65+
except AttributeError:
66+
pass # The module does not define `__all__`
6467

6568
# The template is not actually changed.
6669
return QOMTemplate

qemu/version.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,11 @@ def define_only_qemu_2_6_0_types():
186186
Structure("I2CBus") # the structure is defined in .c file
187187
]).add_reference(osdep_fake_type)
188188

189-
Header["disas/bfd.h"].add_types([
189+
Header[get_vp("disas header")].add_types([
190190
Type("disassemble_info", False)
191191
]).add_reference(osdep_fake_type)
192192

193-
Header["qemu/fprintf-fn.h"].add_types([
193+
Header[get_vp("fprintf_function definer")].add_types([
194194
Type("fprintf_function", False)
195195
]).add_reference(osdep_fake_type)
196196

@@ -552,13 +552,13 @@ def define_only_qemu_2_6_0_types():
552552
osdep_fake_type
553553
])
554554

555-
Header["disas/bfd.h"].add_types([
555+
Header[get_vp("disas header")].add_types([
556556
Type("bfd_vma", False),
557557
Type("bfd_byte", False),
558558
Type("const bfd_byte", False)
559559
])
560560

561-
Header["disas/bfd.h"].add_types([
561+
Header[get_vp("disas header")].add_types([
562562
Function(
563563
name = name,
564564
ret_type = Type["bfd_vma"],
@@ -777,6 +777,19 @@ def machine_register_2_6(mach):
777777
)
778778

779779
qemu_heuristic_db = {
780+
# Next two commits precede v4.1.0-rc0. They are in one branch.
781+
u'ede9a8a656c992deecce45f8175985dd81cc6be9' : [
782+
QEMUVersionParameterDescription("fprintf_function definer",
783+
new_value = "disas/dis-asm.h",
784+
old_value = "qemu/fprintf-fn.h"
785+
)
786+
],
787+
u'3979fca4b69fc31c372687cd0bb6950592f248bd' : [
788+
QEMUVersionParameterDescription("disas header",
789+
new_value = "disas/dis-asm.h",
790+
old_value = "disas/bfd.h"
791+
)
792+
],
780793
u'b59821a95bd1d7cb4697fd7748725c910582e0e7' : [
781794
QEMUVersionParameterDescription("explicit global memory registration",
782795
new_value = False,

source/model.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,13 @@ def add_type(self, _type):
427427
_type.definer = self
428428
self.types[_type.name] = _type
429429

430+
# Some types (like `Enumeration`) contains types without definer or
431+
# may reference to other just created types.
432+
# They must be added right now (by `TypeFixerVisitor`) to prevent
433+
# capturing by other sources at generation begin.
434+
# Note, replacement of foreign types with `TypeReference` is actually
435+
# not required right now (see `gen_chunks`).
436+
430437
fixer = TypeFixerVisitor(self, _type).visit()
431438

432439
# Auto add references to types this one does depend on
@@ -448,6 +455,8 @@ def gen_chunks(self, inherit_references = False):
448455
if inherit_references:
449456
assert(isinstance(self, Header))
450457

458+
# This header includes other headers to provide types for includers
459+
# of self. This is list of references to those types.
451460
ref_list = []
452461

453462
if isinstance(self, Header):
@@ -456,7 +465,11 @@ def gen_chunks(self, inherit_references = False):
456465
if ref.definer not in user.inclusions:
457466
ref_list.append(TypeReference(ref))
458467

459-
TypeFixerVisitor(self, self.global_variables).visit()
468+
# Finally, we must fix types just before generation because user can
469+
# change already added types.
470+
fixer = TypeFixerVisitor(self, self).visit()
471+
472+
self.add_references(tr.type for tr in fixer.new_type_references)
460473

461474
# fix up types for headers with references
462475
# list of types must be copied because it is changed during each
@@ -576,6 +589,8 @@ def generate(self, inherit_references = False):
576589

577590
return file
578591

592+
__type_references__ = ("types", "global_variables")
593+
579594

580595
class CPP(object):
581596
"This class used as definer for CPPMacro"
@@ -624,6 +639,24 @@ def __getitem__(self, path):
624639
class Header(Source):
625640
reg = {}
626641

642+
def __init__(self, path, is_global = False, protection = True):
643+
"""
644+
:param path: it is used in #include statements, as unique identifier and
645+
somehow during code generation.
646+
:param is_global: inclusions will use <path> instead of "path".
647+
:param protection: wrap content in multiple inclusion protection macro logic.
648+
"""
649+
super(Header, self).__init__(path)
650+
self.is_global = is_global
651+
self.includers = []
652+
self.protection = protection
653+
654+
tpath = path2tuple(path)
655+
if tpath in Header.reg:
656+
raise RuntimeError("Header %s is already registered" % path)
657+
658+
Header.reg[tpath] = self
659+
627660
@staticmethod
628661
def _on_include(includer, inclusion, is_global):
629662
if path2tuple(inclusion) not in Header.reg:
@@ -771,18 +804,6 @@ def lookup(path):
771804
raise RuntimeError("Header with path %s is not registered" % path)
772805
return Header.reg[tpath]
773806

774-
def __init__(self, path, is_global = False, protection = True):
775-
super(Header, self).__init__(path)
776-
self.is_global = is_global
777-
self.includers = []
778-
self.protection = protection
779-
780-
tpath = path2tuple(path)
781-
if tpath in Header.reg:
782-
raise RuntimeError("Header %s is already registered" % path)
783-
784-
Header.reg[tpath] = self
785-
786807
def __str__(self):
787808
if self.is_global:
788809
return "<%s>" % self.path

0 commit comments

Comments
 (0)