source: kernel-config/kernel-config.py@ 7ebdf4e

12.0 12.1 12.2 gimp3 ken/TL2024 ken/tuningfonts lazarus plabs/newcss python3.11 rahul/power-profiles-daemon renodr/vulkan-addition trunk xry111/for-12.3 xry111/llvm18 xry111/spidermonkey128
Last change on this file since 7ebdf4e was 7ebdf4e, checked in by Xi Ruoyao <xry111@…>, 14 months ago

firmware: Use new kernel configuration rendering

  • Property mode set to 100755
File size: 7.3 KB
Line 
1#!/usr/bin/env python3
2
3# SPDX-License-Identifier: MIT
4# Copyright 2023 The LFS Editors
5
6# Stupid script to render "mconf"-style kernel configuration
7# Usage: kernel-config.py [path to kernel tree] [needed config].toml
8# The toml file should be like:
9# for bool and tristate:
10# EXT4="*"
11# DRM="*M"
12# EXPERT=" "
13# DRM_I915="*M"
14# for choice:
15# HIGHMEM64G="X"
16# an entry with comment:
17# DRM_I915 = { value = " *M", comment = "for i915, crocus, or iris" }
18
19choice_bit = 1 << 30
20ind0 = 0
21ind1 = 0
22menu_id = 1
23stack = []
24if_stack = []
25
26expand_var_mp = { 'SRCARCH': 'x86' }
27
28def expand_var(s):
29 for k in expand_var_mp:
30 s = s.replace('$(' + k + ')', expand_var_mp[k])
31 return s
32
33def pop_stack(cond):
34 global ind0, ind1, stack
35 assert(cond(stack[-1][0]))
36 s, i0, i1, _ = stack[-1]
37 stack = stack[:-1]
38 ind0 -= i0
39 ind1 -= i1
40
41def pop_stack_while(cond):
42 while len(stack) and cond(stack[-1][0]):
43 pop_stack(cond)
44
45def cur_menu():
46 global stack
47 return stack[-1][3] if len(stack) else 0
48
49def cur_if():
50 global if_stack
51 return if_stack[-1] if len(if_stack) else []
52
53def clean_dep(d):
54 d = d.strip()
55 if d.endswith('=y') or d.endswith('=M'):
56 d = d[:-2]
57 elif d.endswith(' != ""'):
58 d = d[:-6]
59 return d
60
61def parse_config(buf):
62 global ind0, ind1, stack, menu_id
63 is_menu = buf[0].startswith('menu')
64 key = buf[0].split()[1].strip()
65
66 deps = ['menu'] + cur_if()
67 title = None
68 klass = None
69 for line in buf[1:]:
70 line = line.strip()
71 if line.startswith('depends on '):
72 new_deps = line[len('depends on '):].split('&&')
73 deps += [clean_dep(x) for x in new_deps]
74 else:
75 for prefix in ['tristate', 'bool', 'string']:
76 if line.startswith(prefix + ' '):
77 title = line[len(prefix) + 1:]
78 klass = prefix
79 elif line.startswith('def_' + prefix + ' '):
80 klass = prefix
81 elif line.startswith('prompt '):
82 title = line[len('prompt '):]
83
84 pop_stack_while(lambda x: x not in deps)
85
86 if key not in known_config:
87 return []
88 val = known_config[key]
89 comment = None
90 forced = None
91
92 if type(val) == dict:
93 comment = val.get('comment')
94 forced = val.get('forced')
95 val = val['value']
96
97 assert(title and klass)
98 title = title.strip().lstrip('"')
99 title = title[:title.find('"')]
100
101 if klass == 'string':
102 val = '(' + val + ')'
103 else:
104 assert((val == 'X') == bool(cur_menu() & choice_bit))
105 if (val == 'X'):
106 val = '(X)'
107 else:
108 val = list(val)
109 val.sort()
110 for c in val:
111 if c not in 'M* ' or (c == 'M' and klass != 'tristate'):
112 raise Exception('unknown setting %s for %s' % (c, key))
113 bracket = None
114 if klass == 'tristate' and forced != '*' :
115 bracket = '{}' if forced else '<>'
116 else:
117 bracket = '--' if forced else '[]'
118
119 val = bracket[0] + '/'.join(val) + bracket[1]
120
121 arrow = ' --->' if is_menu else ''
122 r = [(ind0, val, ind1, title, arrow, key, cur_menu(), comment)]
123 menu_id += is_menu
124 stack_ent = (key, 2, 0, menu_id) if is_menu else (key, 0, 2, cur_menu())
125 ind0 += stack_ent[1]
126 ind1 += stack_ent[2]
127 stack += [stack_ent]
128 return r
129
130def parse_choice(buf):
131 global ind0, ind1, stack, menu_id
132 assert(buf[0] == 'choice\n')
133 title = ''
134 for line in buf:
135 line = line.strip()
136 if line.startswith('prompt '):
137 title = line[len('prompt '):].strip().strip('"')
138 r = [(ind0, "", ind1, title, ' --->', '', cur_menu(), None)]
139 menu_id += 1
140 stack += [('menu', 2, 0, menu_id | choice_bit)]
141 ind0 += 2
142 return r
143
144def load_kconfig(file):
145 global ind0, ind1, stack, path, menu_id, if_stack
146 r = []
147 config_buf = []
148 with open(path + file) as f:
149 for line in f:
150 if len(config_buf):
151 if not (line.startswith('\t') or line.startswith(' ')):
152 if config_buf[0] == 'choice\n':
153 r += parse_choice(config_buf)
154 else:
155 r += parse_config(config_buf)
156 config_buf = []
157 else:
158 config_buf += [line]
159 continue
160 if line.startswith('source') or line.startswith('\tsource'):
161 sub = expand_var(line.strip().split()[1].strip('"'))
162 r += load_kconfig(sub)
163 elif line.startswith('config') or line.startswith('menuconfig'):
164 config_buf = [line]
165 elif line.startswith('choice'):
166 config_buf = [line]
167 elif line.startswith("menu"):
168 title = expand_var(line[4:].strip().strip('"'))
169 r += [(ind0, "", ind1, title, ' --->', '', cur_menu(), None)]
170 menu_id += 1
171 stack += [('menu', 2, 0, menu_id)]
172 ind0 += 2
173 elif line.startswith('endmenu') or line.startswith('endchoice'):
174 pop_stack_while(lambda x: x != 'menu')
175 pop_stack(lambda x: x == 'menu')
176 if r[-1][1] == "":
177 # prune empty menu
178 r = r[:-1]
179 elif line.startswith('if '):
180 line = line[3:]
181 top = cur_if()
182 top += [x.strip() for x in line.split("&&")]
183 if_stack += [top]
184 elif line.startswith('endif'):
185 if_stack = if_stack[:-1]
186 return r
187
188known_config = {}
189
190from sys import argv
191import tomllib
192
193path = argv[1]
194if path[-1] != '/':
195 path += '/'
196with open(argv[2], 'rb') as f:
197 known_config = tomllib.load(f)
198
199r = load_kconfig("Kconfig")
200
201# Now we are going to pretty-print r
202
203## Calculate the maximum value length for each menu
204max_val_len = {}
205for _, val, _, _, _, _, menu, _ in r:
206 x = max_val_len[menu] if menu in max_val_len else 0
207 max_val_len[menu] = max(x, len(val))
208
209## Output
210
211max_line = 80
212buf = []
213
214done = [x[5] for x in r]
215for i in known_config:
216 if i not in done:
217 raise Exception("%s seems not exist" % i)
218
219for i0, val, i1, title, arrow, key, menu, comment in r:
220 if val:
221 val += (max_val_len[menu] - len(val)) * ' '
222 line = i0 * ' ' + val + (i1 + bool(val)) * ' '
223
224 rem = max_line - len(line) - len(arrow)
225
226 if len(title) > rem:
227 title = title[:rem - 3] + '...'
228
229 line += title + arrow
230 rem = max_line - len(line)
231
232 if key:
233 key = ' [' + key + ']'
234
235 if len(key) <= rem:
236 line += (rem - len(key)) * ' ' + key
237 else:
238 key = '... ' + key
239 line += '\n' + ' ' * (max_line - len(key)) + key
240 if comment:
241 line = ' ' * i0 + '# ' + comment + ':\n' + line
242 buf += [line.replace('<', '&lt;').replace('>', '&gt;').rstrip()]
243
244import kernel_version
245kver = kernel_version.kernel_version(path)
246
247print('''<?xml version="1.0" encoding="ISO-8859-1"?>
248<!DOCTYPE note PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
249 "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">''')
250print('<!-- Automatically generated by kernel-config.py')
251print(' DO NOT EDIT! -->')
252print('<screen><literal>' + '\n'.join(buf) + '</literal></screen>')
Note: See TracBrowser for help on using the repository browser.