minikconf: modernize handling of include chain
Use a dataclass, and store it in the parser to avoid having to save and restore it. Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Paolo Bonzini committed
Apr 30, 2026 at 11:03 UTC
1e20bf8061958f6031707739d957468eb4a1af8f
1 file changed
+40
-30
scripts/minikconf.py
+40
-30
@@ -11,15 +11,36 @@
11
# or, at your option, any later version. See the COPYING file in
12
# the top-level directory.
13
14
+from __future__ import annotations
15
+
16
import os
17
import random
18
import re
19
import sys
20
+from dataclasses import dataclass
21
22
__all__ = [ 'KconfigDataError', 'KconfigParserError',
23
'KconfigData', 'KconfigParser' ,
24
'defconfig', 'allyesconfig', 'allnoconfig', 'randconfig' ]
25
26
+@dataclass
27
+class IncludeInfo:
28
+ file: str
29
+ line: int
30
+ parent: IncludeInfo | None
31
+
32
+ def __iter__(self):
33
+ inf = self
34
+ while inf is not None:
35
+ yield "%s:%d" % (inf.file, inf.line)
36
+ inf = inf.parent
37
+
38
+ def error_path(self):
39
+ res = ""
40
+ for loc in self:
41
+ res = "In file included from %s:\n" % loc + res
42
+ return res
43
+
44
def debug_print(*args):
45
#print('# ' + (' '.join(str(x) for x in args)))
46
pass
@@ -202,7 +223,6 @@ class KconfigData:
223
def __init__(self, value_mangler=defconfig):
224
self.value_mangler = value_mangler
225
self.previously_included = []
205
- self.incl_info = None
226
self.defined_vars = set()
227
self.referenced_vars = dict()
228
self.clauses = list()
@@ -342,13 +362,12 @@ class KconfigParserError(Exception):
362
class KconfigParser:
363
364
@classmethod
345
- def parse(cls, fp, data):
346
- cls(data).parse_file(fp)
365
+ def parse(cls, fp, data, incl_info=None):
366
+ cls(fp, data, incl_info).parse_config()
367
348
- def __init__(self, data):
368
+ def __init__(self, fp, data, incl_info):
369
self.data = data
350
-
351
- def parse_file(self, fp):
370
+ self.incl_info = incl_info
371
self.abs_fname = os.path.abspath(fp.name)
372
self.fname = fp.name
373
self.data.previously_included.append(self.abs_fname)
@@ -360,19 +379,9 @@ class KconfigParser:
379
self.line = 1
380
self.line_pos = 0
381
self.get_token()
363
- self.parse_config()
382
383
# file management -----
384
367
- def error_path(self):
368
- inf = self.data.incl_info
369
- res = ""
370
- while inf:
371
- res = ("In file included from %s:%d:\n" % (inf['file'],
372
- inf['line'])) + res
373
- inf = inf['parent']
374
- return res
375
-
385
def location(self):
386
col = 1
387
for ch in self.src[self.line_pos:self.pos]:
@@ -380,33 +389,34 @@ class KconfigParser:
389
col += 8 - ((col - 1) % 8)
390
else:
391
col += 1
383
- return '%s%s:%d:%d' %(self.error_path(), self.fname, self.line, col)
392
+ inf = self.incl_info
393
+ incl_chain = inf.error_path() if inf is not None else ""
394
+ return '%s%s:%d:%d' % (incl_chain, self.fname, self.line, col)
395
396
def do_include(self, include):
397
incl_abs_fname = os.path.join(os.path.dirname(self.abs_fname),
398
include)
399
# catch inclusion cycle
389
- inf = self.data.incl_info
400
+ inf = self.incl_info
401
while inf:
391
- if incl_abs_fname == os.path.abspath(inf['file']):
402
+ if incl_abs_fname == os.path.abspath(inf.file):
403
raise KconfigParserError(self, "Inclusion loop for %s"
404
% include)
394
- inf = inf['parent']
405
+ inf = inf.parent
406
407
# skip multiple include of the same file
408
if incl_abs_fname in self.data.previously_included:
409
return
410
try:
400
- fp = open(incl_abs_fname, 'rt', encoding='utf-8')
401
- except IOError as e:
402
- raise KconfigParserError(self,
403
- '%s: %s' % (e.strerror, include))
404
-
405
- inf = self.data.incl_info
406
- self.data.incl_info = { 'file': self.fname, 'line': self.line,
407
- 'parent': inf }
408
- KconfigParser(self.data).parse_file(fp)
409
- self.data.incl_info = inf
411
+ try:
412
+ fp = open(incl_abs_fname, 'rt', encoding='utf-8')
413
+ except IOError as e:
414
+ raise KconfigParserError(self, '%s: %s' % (e.strerror, include))
415
+
416
+ inner = IncludeInfo(file=self.fname, line=self.line, parent=self.incl_info)
417
+ type(self).parse(fp, self.data, inner)
418
+ finally:
419
+ fp.close()
420
421
# recursive descent parser -----
422