1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
#!/usr/bin/env python3
import argparse
import os
import sys
from typing import Optional
def print_array(name: str, values: list[str]) -> None:
if len(values) == 0:
return
list = ", ".join(values)
print(f" .{name} = ((const char*[]){{ {list}, NULL }}),")
def parse_line(line: str) -> tuple[str, str]:
kind = ""
data = ""
get_kind = False
get_data = False
for item in line.split():
if item == "MODINFO_START":
get_kind = True
continue
if item.startswith("MODINFO_END"):
get_data = False
continue
if get_kind:
kind = item
get_kind = False
get_data = True
continue
if get_data:
data += " " + item
continue
return (kind, data)
def parse_modinfo(name: str, lines: list[str], enabled: set[str]) -> Optional[dict]:
"""Parse a modinfo file and return module metadata, or None if disabled."""
arch = ""
objs = []
deps = []
opts = []
for line in lines:
if "MODINFO_START" in line:
(kind, data) = parse_line(line)
if kind == 'obj':
objs.append(data)
elif kind == 'dep':
deps.append(data)
elif kind == 'opts':
opts.append(data)
elif kind == 'arch':
arch = data
elif kind == 'kconfig':
# don't add a module which dependency is not enabled
# in kconfig
if data.strip() not in enabled:
return None
else:
print("unknown:", kind)
exit(1)
return {
'name': name,
'arch': arch,
'objs': objs,
'deps': deps,
'opts': opts,
'dep_names': {dep.strip('" ') for dep in deps}
}
def generate(modinfo: str, mod: Optional[dict],
skip_reason: Optional[str]) -> None:
"""Generate C code for a module."""
print(f" /* {modinfo} */")
if mod is None:
if skip_reason == "missing_deps":
print(" /* module has missing dependencies. */")
else:
print(" /* module isn't enabled in Kconfig. */")
print("/* },{ */")
return
print(f' .name = "{mod["name"]}",')
if mod['arch'] != "":
print(f" .arch = {mod['arch']},")
print_array("objs", mod['objs'])
print_array("deps", mod['deps'])
print_array("opts", mod['opts'])
print("},{")
def print_pre() -> None:
print("/* generated by scripts/modinfo-generate.py */")
print("#include \"qemu/osdep.h\"")
print("#include \"qemu/module.h\"")
print("const QemuModinfo qemu_modinfo[] = {{")
def print_post() -> None:
print(" /* end of list */")
print("}};")
def main() -> None:
parser = argparse.ArgumentParser(
description='Generate C code for QEMU module info'
)
parser.add_argument('--devices',
help='path to config-device.mak')
parser.add_argument('--skip-missing-deps', action='store_true',
help='warn if a dependency is missing and continue')
parser.add_argument('modinfo', nargs='+',
help='modinfo files to process')
args = parser.parse_args()
# get all devices enabled in kconfig, from *-config-device.mak
enabled = set()
if args.devices:
with open(args.devices) as file:
for line in file.readlines():
config = line.split('=')
if config[1].rstrip() == 'y':
enabled.add(config[0][7:]) # remove CONFIG_
# all_modules: modinfo path -> (basename, parsed module or None, skip_reason)
all_modules = {}
for modinfo in args.modinfo:
with open(modinfo) as f:
lines = f.readlines()
(basename, _) = os.path.splitext(modinfo)
mod = parse_modinfo(basename, lines, enabled)
skip_reason = "kconfig" if mod is None else None
all_modules[modinfo] = (basename, mod, skip_reason)
# Collect all available module names
available = {basename for basename, mod, _ in all_modules.values()
if mod is not None}
# Collect all dependencies
all_deps = set()
for basename, mod, _ in all_modules.values():
if mod is not None:
all_deps.update(mod['dep_names'])
# Check for missing dependencies
missing = all_deps.difference(available)
for dep in missing:
print(f"Dependency {dep} cannot be satisfied", file=sys.stderr)
if missing and not args.skip_missing_deps:
exit(1)
# When skipping missing deps, iteratively remove modules with
# unsatisfiable dependencies
if args.skip_missing_deps and missing:
changed = True
while changed:
changed = False
for modinfo, (basename, mod, skip_reason) in list(all_modules.items()):
if mod is None:
continue
if not mod['dep_names'].issubset(available):
available.discard(basename)
all_modules[modinfo] = (basename, None, "missing_deps")
changed = True
# generate output
print_pre()
for modinfo in args.modinfo:
(basename, mod, skip_reason) = all_modules[modinfo]
generate(modinfo, mod, skip_reason)
print_post()
if __name__ == "__main__":
main()
|