[Concept,1/4] qconfig: Add helper functions for #include defconfig sync

Message ID 20260407122656.3462730-2-sjg@u-boot.org
State New
Headers
Series qconfig: Fix #include defconfig sync producing too-large overlays |

Commit Message

Simon Glass April 7, 2026, 12:26 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

Add three helper functions in preparation for fixing the #include
defconfig sync:

- _get_defconfig_entries(): parses a preprocessed defconfig to get a
  dict of config entries by name, for textual comparison against
  include file content

- _build_include_defconfig(): assembles defconfig content from include
  lines and an overlay delta

- _format_sym_value(): formats a kconfiglib symbol's current value as
  a defconfig line, handling bool/tristate/string/int/hex types

No functional change.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 tools/qconfig.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)
  

Patch

diff --git a/tools/qconfig.py b/tools/qconfig.py
index 86b2bed9283..8976b863dab 100755
--- a/tools/qconfig.py
+++ b/tools/qconfig.py
@@ -423,6 +423,43 @@  def _get_min_config_lines(kconf, fname):
     return lines
 
 
+def _config_name(line):
+    """Extract config name (e.g. 'CONFIG_ARM') from a defconfig line
+
+    Args:
+        line (str): A defconfig line
+
+    Returns:
+        str or None: The config name, or None if not a config line
+    """
+    stripped = line.strip()
+    if stripped.startswith('CONFIG_'):
+        return stripped.split('=', 1)[0]
+    if stripped.startswith('# CONFIG_'):
+        return stripped.split()[1]
+    return None
+
+
+def _get_defconfig_entries(fname):
+    """Parse a preprocessed defconfig file to get config entries by name
+
+    Args:
+        fname (str): Path to the preprocessed defconfig file
+
+    Returns:
+        dict: Mapping of config name (e.g. 'CONFIG_ARM') to the full line
+            including the value (e.g. 'CONFIG_ARM=y')
+    """
+    entries = {}
+    with open(fname, encoding='utf-8') as inf:
+        for line in inf:
+            line = line.strip()
+            name = _config_name(line)
+            if name:
+                entries[name] = line
+    return entries
+
+
 def _sync_plain_defconfig(kconf, orig, dry_run):
     """Sync a plain defconfig (no #include)
 
@@ -450,6 +487,53 @@  def _sync_plain_defconfig(kconf, orig, dry_run):
     return updated
 
 
+def _build_include_defconfig(include_lines, delta, sep):
+    """Build defconfig content from include lines and overlay delta
+
+    Assembles a defconfig that uses #include directives by concatenating
+    the original #include lines with the overlay delta (the CONFIG lines
+    that are needed on top of what the includes provide). The separator
+    preserves the blank-line convention from the original file.
+
+    Args:
+        include_lines (list of bytes): The #include lines
+        delta (list of str): Sorted overlay config lines
+        sep (bytes): Separator between includes and delta (b'\\n' or b'')
+
+    Returns:
+        bytes: The defconfig content
+    """
+    out = b''
+    for line in include_lines:
+        out += line
+    if delta:
+        out += sep
+    for line in delta:
+        out += line.encode() if isinstance(line, str) else line
+    return out
+
+
+def _format_sym_value(sym, value=None):
+    """Format a symbol value as a defconfig line
+
+    Args:
+        sym (kconfiglib.Symbol): The symbol
+        value (str or None): Value to format; uses sym.str_value if None
+
+    Returns:
+        str: The defconfig line
+    """
+    if value is None:
+        value = sym.str_value
+    if sym.orig_type in (kconfiglib.BOOL, kconfiglib.TRISTATE):
+        if value == 'n':
+            return f'# CONFIG_{sym.name} is not set'
+        return f'CONFIG_{sym.name}={value}'
+    if sym.orig_type == kconfiglib.STRING:
+        return f'CONFIG_{sym.name}="{kconfiglib.escape(value)}"'
+    return f'CONFIG_{sym.name}={value}'
+
+
 def _sync_include_defconfig(kconf, srcdir, orig, dry_run):
     """Sync a defconfig that uses #include directives