@samitouri / QOSamiQemu / commits / 1e20bf8061

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