source: kernel-config/kernel-config.py@ 4e37f01

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 4e37f01 was 4e37f01, checked in by Xi Ruoyao <xry111@…>, 14 months ago

kernel-config: Do not include kernel version in every generated file

This is stupid and it will cause meaningless diffs in version control
(like this commit does :( ).

Remove the kernel version from the generated XML files. Add
kernel.version file into git to track the kernel version.

  • Property mode set to 100755
File size: 7.2 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 return d
58
59def parse_config(buf):
60 global ind0, ind1, stack, menu_id
61 is_menu = buf[0].startswith('menu')
62 key = buf[0].split()[1].strip()
63
64 deps = ['menu'] + cur_if()
65 title = None
66 klass = None
67 for line in buf[1:]:
68 line = line.strip()
69 if line.startswith('depends on '):
70 new_deps = line[len('depends on '):].split('&&')
71 deps += [clean_dep(x) for x in new_deps]
72 else:
73 for prefix in ['tristate', 'bool', 'string']:
74 if line.startswith(prefix + ' '):
75 title = line[len(prefix) + 1:]
76 klass = prefix
77 elif line.startswith('def_' + prefix + ' '):
78 klass = prefix
79 elif line.startswith('prompt '):
80 title = line[len('prompt '):]
81
82 pop_stack_while(lambda x: x not in deps)
83
84 if key not in known_config:
85 return []
86 val = known_config[key]
87 comment = None
88 forced = None
89
90 if type(val) == dict:
91 comment = val.get('comment')
92 forced = val.get('forced')
93 val = val['value']
94
95 assert(title and klass)
96 title = title.strip().lstrip('"')
97 title = title[:title.find('"')]
98
99 if klass == 'string':
100 val = '(' + val + ')'
101 else:
102 assert((val == 'X') == bool(cur_menu() & choice_bit))
103 if (val == 'X'):
104 val = '(X)'
105 else:
106 val = list(val)
107 val.sort()
108 for c in val:
109 if c not in 'M* ' or (c == 'M' and klass != 'tristate'):
110 raise Exception('unknown setting %s for %s' % (c, key))
111 bracket = None
112 if klass == 'tristate' and forced != '*' :
113 bracket = '{}' if forced else '<>'
114 else:
115 bracket = '--' if forced else '[]'
116
117 val = bracket[0] + '/'.join(val) + bracket[1]
118
119 arrow = ' --->' if is_menu else ''
120 r = [(ind0, val, ind1, title, arrow, key, cur_menu(), comment)]
121 menu_id += is_menu
122 stack_ent = (key, 2, 0, menu_id) if is_menu else (key, 0, 2, cur_menu())
123 ind0 += stack_ent[1]
124 ind1 += stack_ent[2]
125 stack += [stack_ent]
126 return r
127
128def parse_choice(buf):
129 global ind0, ind1, stack, menu_id
130 assert(buf[0] == 'choice\n')
131 title = ''
132 for line in buf:
133 line = line.strip()
134 if line.startswith('prompt '):
135 title = line[len('prompt '):].strip().strip('"')
136 r = [(ind0, "", ind1, title, ' --->', '', cur_menu(), None)]
137 menu_id += 1
138 stack += [('menu', 2, 0, menu_id | choice_bit)]
139 ind0 += 2
140 return r
141
142def load_kconfig(file):
143 global ind0, ind1, stack, path, menu_id, if_stack
144 r = []
145 config_buf = []
146 with open(path + file) as f:
147 for line in f:
148 if len(config_buf):
149 if not (line.startswith('\t') or line.startswith(' ')):
150 if config_buf[0] == 'choice\n':
151 r += parse_choice(config_buf)
152 else:
153 r += parse_config(config_buf)
154 config_buf = []
155 else:
156 config_buf += [line]
157 continue
158 if line.startswith('source') or line.startswith('\tsource'):
159 sub = expand_var(line.strip().split()[1].strip('"'))
160 r += load_kconfig(sub)
161 elif line.startswith('config') or line.startswith('menuconfig'):
162 config_buf = [line]
163 elif line.startswith('choice'):
164 config_buf = [line]
165 elif line.startswith("menu"):
166 title = expand_var(line[4:].strip().strip('"'))
167 r += [(ind0, "", ind1, title, ' --->', '', cur_menu(), None)]
168 menu_id += 1
169 stack += [('menu', 2, 0, menu_id)]
170 ind0 += 2
171 elif line.startswith('endmenu') or line.startswith('endchoice'):
172 pop_stack_while(lambda x: x != 'menu')
173 pop_stack(lambda x: x == 'menu')
174 if r[-1][1] == "":
175 # prune empty menu
176 r = r[:-1]
177 elif line.startswith('if '):
178 line = line[3:]
179 top = cur_if()
180 top += [x.strip() for x in line.split("&&")]
181 if_stack += [top]
182 elif line.startswith('endif'):
183 if_stack = if_stack[:-1]
184 return r
185
186known_config = {}
187
188from sys import argv
189import tomllib
190
191path = argv[1]
192if path[-1] != '/':
193 path += '/'
194with open(argv[2], 'rb') as f:
195 known_config = tomllib.load(f)
196
197r = load_kconfig("Kconfig")
198
199# Now we are going to pretty-print r
200
201## Calculate the maximum value length for each menu
202max_val_len = {}
203for _, val, _, _, _, _, menu, _ in r:
204 x = max_val_len[menu] if menu in max_val_len else 0
205 max_val_len[menu] = max(x, len(val))
206
207## Output
208
209max_line = 80
210buf = []
211
212done = [x[5] for x in r]
213for i in known_config:
214 if i not in done:
215 raise Exception("%s seems not exist" % i)
216
217for i0, val, i1, title, arrow, key, menu, comment in r:
218 if val:
219 val += (max_val_len[menu] - len(val)) * ' '
220 line = i0 * ' ' + val + (i1 + bool(val)) * ' '
221
222 rem = max_line - len(line) - len(arrow)
223
224 if len(title) > rem:
225 title = title[:rem - 3] + '...'
226
227 line += title + arrow
228 rem = max_line - len(line)
229
230 if key:
231 key = ' [' + key + ']'
232
233 if len(key) <= rem:
234 line += (rem - len(key)) * ' ' + key
235 else:
236 key = '... ' + key
237 line += '\n' + ' ' * (max_line - len(key)) + key
238 if comment:
239 line = ' ' * i0 + '# ' + comment + ':\n' + line
240 buf += [line.replace('<', '&lt;').replace('>', '&gt;').rstrip()]
241
242import kernel_version
243kver = kernel_version.kernel_version(path)
244
245print('''<?xml version="1.0" encoding="ISO-8859-1"?>
246<!DOCTYPE note PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
247 "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">''')
248print('<!-- Automatically generated by kernel-config.py')
249print(' DO NOT EDIT! -->')
250print('<screen><literal>' + '\n'.join(buf) + '</literal></screen>')
Note: See TracBrowser for help on using the repository browser.