All of lore.kernel.org
 help / color / mirror / Atom feed
From: Simon Glass <sjg@chromium.org>
To: U-Boot Mailing List <u-boot@lists.denx.de>
Cc: Tom Rini <trini@konsulko.com>, Simon Glass <sjg@chromium.org>,
	Alper Nebi Yasak <alpernebiyasak@gmail.com>
Subject: [PATCH v5 04/44] moveconfig: Update to detect / correct missing SPL Kconfigs
Date: Wed, 22 Feb 2023 09:33:45 -0700	[thread overview]
Message-ID: <20230222163425.2043934-5-sjg@chromium.org> (raw)
In-Reply-To: <20230222163425.2043934-1-sjg@chromium.org>

This adds quite a few more features, all designed to detect problems with
Kconfig options and their use. It can find options mentioned in the source
code which are not in the Kconfig and vice versa. It can convert SPL
usages of non-SPL Kconfig options (i.e. changing CONFIG_IS_ENABLED() to
IS_ENABLED() and CONFIG_$(SPL)_FOO to CONFIG_FOO, creating commits
automatically if requested.

These features are only useful for code clean-up, not for normal
development.

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

(no changes since v2)

Changes in v2:
- Rebase to previous series

 tools/moveconfig.py | 599 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 456 insertions(+), 143 deletions(-)

diff --git a/tools/moveconfig.py b/tools/moveconfig.py
index c4d72ede368..b3ac2672737 100755
--- a/tools/moveconfig.py
+++ b/tools/moveconfig.py
@@ -20,6 +20,7 @@ import doctest
 import filecmp
 import fnmatch
 import glob
+import io
 import multiprocessing
 import os
 import queue
@@ -118,6 +119,16 @@ def check_clean_directory():
         if os.path.exists(fname):
             sys.exit("source tree is not clean, please run 'make mrproper'")
 
+def write_commit(msg):
+    """Create a new commit with all modified files
+
+    Args:
+        msg (str): Commit message
+    """
+    subprocess.call(['git', 'add', '-u'])
+    rc = subprocess.call(['git', 'commit', '-s', '-m', msg])
+    return rc == 0
+
 def get_make_cmd():
     """Get the command name of GNU Make.
 
@@ -1606,30 +1617,59 @@ def prefix_config(cfg):
     return op + cfg
 
 
-RE_MK_CONFIGS = re.compile('CONFIG_(\$\(SPL_(?:TPL_)?\))?([A-Za-z0-9_]*)')
-RE_IFDEF = re.compile('(ifdef|ifndef)')
-RE_C_CONFIGS = re.compile('CONFIG_([A-Za-z0-9_]*)')
-RE_CONFIG_IS = re.compile('CONFIG_IS_ENABLED\(([A-Za-z0-9_]*)\)')
+RE_MK_CONFIGS = re.compile(r'CONFIG_(\$\(SPL_(?:TPL_)?\))?([A-Za-z0-9_]*)')
+
+# Makefile ifdefs: this only handles 'else' on its own line, so not
+# 'else ifeq ...', for example
+RE_IF = re.compile(r'^(ifdef|ifndef|endif|ifeq|ifneq|else)([^,]*,([^,]*)\))?.*$')
+
+# Normal CONFIG options in C
+RE_C_CONFIGS = re.compile(r'CONFIG_([A-Za-z0-9_]*)')
+
+# CONFIG_IS_ENABLED() construct
+RE_CONFIG_IS = re.compile(r'CONFIG_IS_ENABLED\(([A-Za-z0-9_]*)\)')
+
+# Preprocessor #if/#ifdef directives, etc.
+RE_IFDEF = re.compile(r'^\s*#\s*(ifdef|ifndef|endif|if|elif|else)\s*(?:#.*)?(.*)$')
 
 class ConfigUse:
-    def __init__(self, cfg, is_spl, fname, rest):
+    """Holds information about a use of a CONFIG option"""
+    def __init__(self, cfg, is_spl, conds, fname, rest, is_mk):
+        """Set up a new use of a CONFIG option
+
+        Args:
+            cfg (str): Config option, without the CONFIG_
+            is_spl (bool): True if it indicates an SPL option, i.e. has a
+                $(SPL_) or similar, False if not
+            conds (list of str): List of conditions for this use, e.g.
+                ['SPL_BUILD']
+            fname (str): Filename contining the use
+            rest (str): Entire line from the file
+            is_mk (bool): True if this is in a Makefile, False if not
+        """
         self.cfg = cfg
         self.is_spl = is_spl
+        self.conds = list(c for c in conds if '(NONE)' not in c)
         self.fname = fname
         self.rest = rest
+        self.is_mk = is_mk
 
     def __hash__(self):
         return hash((self.cfg, self.is_spl))
 
-def scan_makefiles(fnames):
+    def __str__(self):
+        return (f'ConfigUse({self.cfg}, is_spl={self.is_spl}, '
+                f'is_mk={self.is_mk}, fname={self.fname}, rest={self.rest}')
+
+def scan_makefiles(fname_dict):
     """Scan Makefiles looking for Kconfig options
 
     Looks for uses of CONFIG options in Makefiles
 
     Args:
-        fnames (list of tuple):
-            str: Makefile filename where the option was found
-            str: Line of the Makefile
+        fname_dict (dict lines):
+            key: Makefile filename where the option was found
+            value: List of str lines of the Makefile
 
     Returns:
         tuple:
@@ -1646,46 +1686,81 @@ def scan_makefiles(fnames):
     ('$(SPL_)', 'MARY')
     >>> RE_MK_CONFIGS.search('CONFIG_$(SPL_TPL_)MARY').groups()
     ('$(SPL_TPL_)', 'MARY')
+    >>> RE_IF.match('ifdef CONFIG_SPL_BUILD').groups()
+    ('ifdef', None, None)
+    >>> RE_IF.match('endif # CONFIG_SPL_BUILD').groups()
+    ('endif', None, None)
+    >>> RE_IF.match('endif').groups()
+    ('endif', None, None)
+    >>> RE_IF.match('else').groups()
+    ('else', None, None)
     """
     all_uses = collections.defaultdict(list)
     fname_uses = {}
-    for fname, rest in fnames:
-        m_iter = RE_MK_CONFIGS.finditer(rest)
-        found = False
-        for m in m_iter:
-            found = True
-            real_opt = m.group(2)
-            if real_opt == '':
-                continue
-            is_spl = False
-            if m.group(1):
-                is_spl = True
-            use = ConfigUse(real_opt, is_spl, fname, rest)
-            if fname not in fname_uses:
-                fname_uses[fname] = set()
-            fname_uses[fname].add(use)
-            all_uses[use].append(rest)
+    for fname, rest_list in fname_dict.items():
+        conds = []
+        for rest in rest_list:
+            m_iter = RE_MK_CONFIGS.finditer(rest)
+            m_cond = RE_IF.match(rest)
+            #print('line', conds, m_cond and m_cond.group(1) or '.', rest)
+            last_use = None
+            use = None
+            for m in m_iter:
+                real_opt = m.group(2)
+                if real_opt == '':
+                    continue
+                is_spl = False
+                if m.group(1):
+                    is_spl = True
+                use = ConfigUse(real_opt, is_spl, conds, fname, rest, True)
+                if fname not in fname_uses:
+                    fname_uses[fname] = set()
+                fname_uses[fname].add(use)
+                all_uses[use].append(rest)
+                last_use = use
+            if m_cond:
+                cfg = last_use.cfg if last_use else '(NONE)'
+                cond = m_cond.group(1)
+                if cond == 'ifdef':
+                    conds.append(cfg)
+                elif cond == 'ifndef':
+                    conds.append(f'~{cfg}')
+                elif cond == 'ifeq':
+                    #print('todo', fname, m_cond.group(3), fname, rest)
+                    conds.append(f'{m_cond.group(3)}={cfg}')
+                elif cond == 'ifneq':
+                    conds.append(f'{m_cond.group(3)}={cfg}')
+                    #print('todo', m_cond.group(3))
+                elif cond == 'endif':
+                    conds.pop()
+                elif cond == 'else':
+                    cond = conds.pop()
+                    if cond.startswith('~'):
+                        cond = cond[1:]
+                    else:
+                        cond = f'~{cond}'
+                    conds.append(cond)
+                else:
+                    print(f'unknown condition: {rest}')
+        if conds:
+            print(f'leftover {conds}')
     return all_uses, fname_uses
 
-
-def scan_src_files(fnames):
+def scan_src_files(fname_dict, all_uses, fname_uses):
     """Scan source files (other than Makefiles) looking for Kconfig options
 
     Looks for uses of CONFIG options
 
     Args:
-        fnames (list of tuple):
-            str: Makefile filename where the option was found
-            str: Line of the Makefile
-
-    Returns:
-        tuple:
-            dict: all_uses
-                key (ConfigUse): object
-                value (list of str): matching lines
-            dict: Uses by filename
-                key (str): filename
-                value (set of ConfigUse): uses in that filename
+        fname_dict (dict):
+            key (str): Filename
+            value (list of ConfigUse): List of uses in that filename
+        all_uses (dict): to add more uses to:
+            key (ConfigUse): object
+            value (list of str): matching lines
+        fname (dict): to add more uses-by-filename to
+            key (str): filename
+            value (set of ConfigUse): uses in that filename
 
     >>> RE_C_CONFIGS.search('CONFIG_FRED').groups()
     ('FRED',)
@@ -1693,39 +1768,78 @@ def scan_src_files(fnames):
     ('MARY',)
     >>> RE_CONFIG_IS.search('#if CONFIG_IS_ENABLED(OF_PLATDATA)').groups()
     ('OF_PLATDATA',)
+    >>> RE_IFDEF.match('#ifdef CONFIG_SPL_BUILD').groups()
+    ('ifdef', 'CONFIG_SPL_BUILD')
+    >>> RE_IFDEF.match('#endif # CONFIG_SPL_BUILD').groups()
+    ('endif', '')
+    >>> RE_IFDEF.match('#endif').groups()
+    ('endif', '')
+    >>> RE_IFDEF.match('#else').groups()
+    ('else', '')
+    >>> RE_IFDEF.match(' # if defined(CONFIG_CMD_NET) && !defined(CONFIG_DM_ETH)').groups()
+    ('if', 'defined(CONFIG_CMD_NET) && !defined(CONFIG_DM_ETH)')
     """
     def add_uses(m_iter, is_spl):
+        last_use = None
         for m in m_iter:
             found = True
             real_opt = m.group(1)
             if real_opt == '':
                 continue
-            use = ConfigUse(real_opt, is_spl, fname, rest)
+            use = ConfigUse(real_opt, is_spl, conds, fname, rest, False)
             if fname not in fname_uses:
                 fname_uses[fname] = set()
             fname_uses[fname].add(use)
             all_uses[use].append(rest)
-
-    all_uses = collections.defaultdict(list)
-    fname_uses = {}
-    for fname, rest in fnames:
-        m_iter = RE_C_CONFIGS.finditer(rest)
-        add_uses(m_iter, False)
-
-        m_iter2 = RE_CONFIG_IS.finditer(rest)
-        add_uses(m_iter2, True)
-
-    return all_uses, fname_uses
+            last_use = use
+        return last_use
+
+    for fname, rest_list in fname_dict.items():
+        conds = []
+        for rest in rest_list:
+            m_iter = RE_C_CONFIGS.finditer(rest)
+            m_cond = RE_IFDEF.match(rest)
+            last_use1 = add_uses(m_iter, False)
+
+            m_iter2 = RE_CONFIG_IS.finditer(rest)
+            last_use2 = add_uses(m_iter2, True)
+            last_use = last_use1 or last_use2
+            if m_cond:
+                cfg = last_use.cfg if last_use else '(NONE)'
+                cond = m_cond.group(1)
+                #print(fname, rest, cond, conds)
+                if cond == 'ifdef':
+                    conds.append(cfg)
+                elif cond == 'ifndef':
+                    conds.append(f'~{cfg}')
+                elif cond == 'if':
+                    #print('todo', fname, m_cond.group(3), fname, rest)
+                    conds.append(f'{m_cond.group(2)}={cfg}')
+                elif cond == 'endif':
+                    conds.pop()
+                elif cond == 'else':
+                    cond = conds.pop()
+                    if cond.startswith('~'):
+                        cond = cond[1:]
+                    else:
+                        cond = f'~{cond}'
+                    conds.append(cond)
+                elif cond == 'elif':
+                    cond = conds.pop()
+                    conds.append(cfg)
+                else:
+                    print(f'{fname}: unknown condition: {rest}')
 
 
 MODE_NORMAL, MODE_SPL, MODE_PROPER = range(3)
 
-def do_scan_source(path, do_update):
+def do_scan_source(path, do_update, show_conflicts, do_commit):
     """Scan the source tree for Kconfig inconsistencies
 
     Args:
         path (str): Path to source tree
-        do_update (bool) : True to write to scripts/kconf_... files
+        do_update (bool): True to write to scripts/kconf_... files
+        show_conflicts (bool): True to show conflicts
     """
     def is_not_proper(name):
         for prefix in SPL_PREFIXES:
@@ -1754,7 +1868,7 @@ def do_scan_source(path, do_update):
         """
         # Make sure we know about all the options
         not_found = collections.defaultdict(list)
-        for use, rest in all_uses.items():
+        for use, _ in all_uses.items():
             name = use.cfg
             if name in IGNORE_SYMS:
                 continue
@@ -1766,7 +1880,6 @@ def do_scan_source(path, do_update):
                 # If it is an SPL symbol, try prepending all SPL_ prefixes to
                 # find at least one SPL symbol
                 if use.is_spl:
-                    add_to_dict = False
                     for prefix in SPL_PREFIXES:
                         try_name = prefix + name
                         sym = kconf.syms.get(try_name)
@@ -1784,7 +1897,6 @@ def do_scan_source(path, do_update):
                 elif not use.is_spl:
                     check = False
             else: # MODE_NORMAL
-                debug = False
                 sym = kconf.syms.get(name)
                 if not sym:
                     proper_name = is_not_proper(name)
@@ -1819,105 +1931,304 @@ def do_scan_source(path, do_update):
             for i, use in enumerate(uses[name]):
                 print(f'{"   " if i else ""}{use.fname}: {use.rest.strip()}')
 
+    def do_scan():
+        """Scan the source tree
 
-    print('Scanning Kconfig')
-    kconf = KconfigScanner().conf
-    print(f'Scanning source in {path}')
-    args = ['git', 'grep', '-E', r'IS_ENABLED|\bCONFIG']
-    with subprocess.Popen(args, stdout=subprocess.PIPE) as proc:
-        out, err = proc.communicate()
-    lines = out.splitlines()
-    re_fname = re.compile('^([^:]*):(.*)')
-    src_list = []
-    mk_list = []
-    for line in lines:
-        linestr = line.decode('utf-8')
-        m_fname = re_fname.search(linestr)
-        if not m_fname:
-            continue
-        fname, rest = m_fname.groups()
-        dirname, leaf = os.path.split(fname)
-        root, ext = os.path.splitext(leaf)
-        if ext == '.autoconf':
-            pass
-        elif ext in ['.c', '.h', '.S', '.lds', '.dts', '.dtsi', '.asl', '.cfg',
-                     '.env', '.tmpl']:
-            src_list.append([fname, rest])
-        elif 'Makefile' in root or ext == '.mk':
-            mk_list.append([fname, rest])
-        elif ext in ['.yml', '.sh', '.py', '.awk', '.pl', '.rst', '', '.sed']:
-            pass
-        elif 'Kconfig' in root or 'Kbuild' in root:
-            pass
-        elif 'README' in root:
-            pass
-        elif dirname in ['configs']:
-            pass
-        elif dirname.startswith('doc') or dirname.startswith('scripts/kconfig'):
-            pass
+        This runs a 'git grep' and then picks out uses of CONFIG options from
+        the resulting output
+
+        Returns: tuple
+            dict of uses of CONFIG in Makefiles:
+                key (str): Filename
+                value (list of ConfigUse): List of uses in that filename
+            dict of uses of CONFIG in source files:
+                key (str): Filename
+                value (list of ConfigUse): List of uses in that filename
+        """
+        def finish_file(last_fname, rest_list):
+            if is_mkfile:
+                mk_dict[last_fname] = rest_list
+            else:
+                src_dict[last_fname] = rest_list
+            rest_list = []
+
+        print(f'Scanning source in {path}')
+        args = ['git', 'grep', '-E',
+                r'IS_ENABLED|\bCONFIG|CONFIG_SPL_BUILD|if|else|endif']
+        with subprocess.Popen(args, stdout=subprocess.PIPE) as proc:
+            out, _ = proc.communicate()
+        lines = out.splitlines()
+        re_fname = re.compile('^([^:]*):(.*)')
+        src_dict = collections.OrderedDict()
+        mk_dict = collections.OrderedDict()
+        last_fname = None
+        rest_list = []
+        is_mkfile = None
+        for line in lines:
+            linestr = line.decode('utf-8')
+            m_fname = re_fname.search(linestr)
+            if not m_fname:
+                continue
+            fname, rest = m_fname.groups()
+            if fname != last_fname:
+                finish_file(last_fname, rest_list)
+                rest_list = []
+                last_fname = fname
+
+            dirname, leaf = os.path.split(fname)
+            root, ext = os.path.splitext(leaf)
+            #if dirname != '' or root != 'Makefile':
+                #continue
+            if (dirname.startswith('test') or
+                dirname.startswith('lib/efi_selftest')):
+                # Ignore test code since it is mostly only for sandbox
+                pass
+            elif ext == '.autoconf':
+                pass
+            elif ext in ['.c', '.h', '.S', '.lds', '.dts', '.dtsi', '.asl',
+                         '.cfg', '.env', '.tmpl']:
+                rest_list.append(rest)
+                is_mkfile = False
+            elif 'Makefile' in root or ext == '.mk':
+                rest_list.append(rest)
+                is_mkfile = True
+            elif ext in ['.yml', '.sh', '.py', '.awk', '.pl', '.rst', '', '.sed',
+                         '.src', '.inc', '.l', '.i_shipped', '.txt', '.cmd',
+                         '.cfg', '.y', '.cocci', '.ini', '.asn1', '.base',
+                         '.cnf', '.patch', '.mak', '.its', '.svg', '.tcl',
+                         '.css', '.config', '.conf']:
+                pass
+            elif 'Kconfig' in root or 'Kbuild' in root:
+                pass
+            elif 'README' in root:
+                pass
+            elif dirname in ['configs']:
+                pass
+            elif dirname.startswith('doc') or dirname.startswith('scripts/kconfig'):
+                pass
+            else:
+                print(f'Not sure how to handle file {fname}')
+        finish_file(last_fname, rest_list)
+        return mk_dict, src_dict
+
+    def check_mk_missing(all_uses, spl_not_found, proper_not_found):
+        # Make sure we know about all the options in Makefiles
+        print('\nCONFIG options present in Makefiles but not Kconfig:')
+        not_found = check_not_found(all_uses, MODE_NORMAL)
+        show_uses(not_found)
+
+        print('\nCONFIG options present in Makefiles but not Kconfig (SPL):')
+        not_found = check_not_found(all_uses, MODE_SPL)
+        show_uses(not_found)
+        spl_not_found |= set(is_not_proper(key) or key for key in not_found.keys())
+
+        print('\nCONFIG options used as Proper in Makefiles but without a non-SPL_ variant:')
+        not_found = check_not_found(all_uses, MODE_PROPER)
+        show_uses(not_found)
+        proper_not_found |= set(key for key in not_found.keys())
+
+    def check_src_missing(all_uses, spl_not_found, proper_not_found):
+        # Make sure we know about all the options in source files
+        print('\nCONFIG options present in source but not Kconfig:')
+        not_found = check_not_found(all_uses, MODE_NORMAL)
+        show_uses(not_found)
+
+        print('\nCONFIG options present in source but not Kconfig (SPL):')
+        not_found = check_not_found(all_uses, MODE_SPL)
+        show_uses(not_found)
+        spl_not_found |= set(is_not_proper(key) or key
+                             for key in not_found.keys())
+
+        print('\nCONFIG options used as Proper in source but without a non-SPL_ variant:')
+        not_found = check_not_found(all_uses, MODE_PROPER)
+        show_uses(not_found)
+        proper_not_found |= set(key for key in not_found.keys())
+
+    def show_summary(spl_not_found, proper_not_found):
+        print('\nCONFIG options used as SPL but without an SPL_ variant:')
+        for item in sorted(spl_not_found):
+            print(f'   {item}')
+
+        print('\nCONFIG options used as Proper but without a non-SPL_ variant:')
+        for item in sorted(proper_not_found):
+            print(f'   {item}')
+
+    def write_update(spl_not_found, proper_not_found):
+        with open(os.path.join(path, 'scripts', 'conf_nospl'),
+                  encoding='utf-8') as out:
+            print('# These options should not be enabled in SPL builds\n',
+                  file=out)
+            for item in sorted(spl_not_found):
+                print(item, file=out)
+        with open(os.path.join(path, 'scripts', 'conf_noproper'), 'w',
+                  encoding='utf-8') as out:
+            print('# These options should not be enabled in Proper builds\n',
+                  file=out)
+            for item in sorted(proper_not_found):
+                print(item, file=out)
+
+    def check_conflict(kconf, all_uses, show):
+        """Check conflicts between uses of CONFIG options in source
+
+        Sometimes an option is used in an SPL context in once place and not in
+        another. For example if we see CONFIG_SPL_FOO and CONFIG_FOO then we
+        don't know whether FOO should be enabled in SPL if CONFIG_FOO is
+        enabled, or only if CONFIG_SPL_FOO is enabled.,
+
+        This function detects such situations
+
+        Args:
+            kconf (Kconfiglib.Kconfig): Kconfig tree read from source
+            all_uses (dict): dict to add more uses to:
+                key (ConfigUse): object
+                value (list of str): matching lines
+            show (bool): True to show the problems
+
+        Returns:
+            dict of conflicts:
+                key: filename
+                value: list of ConfigUse objects
+        """
+        conflict_dict = collections.defaultdict(list)
+        cfg_dict = collections.defaultdict(list)
+
+        uses = collections.defaultdict(list)
+        for use in all_uses:
+            uses[use.cfg].append(use)
+
+        bad = 0
+        for cfg in sorted(uses):
+            use_list = uses[cfg]
+
+            # Check if
+            #  - there is no SPL_ version of the symbol, and
+            #  - some uses are SPL and some are not
+            is_spl = list(set(u.is_spl for u in use_list))
+            if len(is_spl) > 1 and f'SPL_{cfg}' not in kconf.syms:
+                if show:
+                    print(f'{cfg}: {is_spl}')
+                for use in use_list:
+                    if show:
+                        print(f'   spl={use.is_spl}: {use.conds} {use.fname} {use.rest}')
+                    if use.is_spl:
+                        conflict_dict[use.fname].append(use)
+                        cfg_dict[use.cfg].append(use)
+                bad += 1
+        print(f'total bad: {bad}')
+        return conflict_dict, cfg_dict
+
+    def replace_in_file(fname, use_or_uses):
+        """Replace a CONFIG pattern in a file
+
+        Args:
+            fname (str): Filename to replace in
+            use_or_uses (ConfigUse or list of ConfigUse): Uses to replace
+        """
+        with open(fname, encoding='utf-8') as inf:
+            data = inf.read()
+        out = io.StringIO()
+
+        if isinstance(use_or_uses, list):
+            uses = use_or_uses
         else:
-            print(f'Not sure how to handle file {fname}')
+            uses = [use_or_uses]
 
-    # Scan the Makefiles
-    all_uses, fname_uses = scan_makefiles(mk_list)
+        re_cfgs = []
+        for use in uses:
+            if use.is_mk:
+                expr = r'CONFIG_(?:\$\(SPL_(?:TPL_)?\))%s\b' % use.cfg
+            else:
+                expr = r'CONFIG_IS_ENABLED\(%s\)' % use.cfg
+            re_cfgs.append(re.compile(expr))
+
+        for line in data.splitlines():
+            new_line = line
+            if 'CONFIG_' in line:
+                todo = []
+                for i, re_cfg in enumerate(re_cfgs):
+                    if not re_cfg.search(line):
+                        todo.append(re_cfg)
+                    use = uses[i]
+                    if use.is_mk:
+                        new_line = re_cfg.sub(f'CONFIG_{use.cfg}', new_line)
+                    else:
+                        new = f'IS_ENABLED(CONFIG_{use.cfg})'
+                        new_line = re_cfg.sub(new, new_line)
+                re_cfgs = todo
+            out.write(new_line)
+            out.write('\n')
+        out_data = out.getvalue()
+        if out_data == data:
+            print(f'\n\nfailed with {fname}\n\n')
+        with open(fname, 'w', encoding='utf-8') as outf:
+            outf.write(out_data)
+
+    def update_source(conflicts):
+        """Replace any SPL constructs with plain non-SPL ones
+
+        CONFIG_IS_ENABLED(FOO) becomes IS_ENABLED(CONFIG_FOO)
+        CONFIG$_(SPL_TPL_)FOO becomes CONFIG_FOO
 
-    spl_not_found = set()
-    proper_not_found = set()
+        Args:
+            conflicts (dict): dict of conflicts:
+                key (str): filename
+                value (list of ConfigUse): uses within that file
+        """
+        print(f'Updating {len(conflicts)} files')
+        for fname, uses in conflicts.items():
+            replace_in_file(fname, uses)
 
-    # Make sure we know about all the options
-    print('\nCONFIG options present in Makefiles but not Kconfig:')
-    not_found = check_not_found(all_uses, MODE_NORMAL)
-    show_uses(not_found)
+    def create_commits(cfg_dict):
+        """Create a set of commits to fix SPL conflicts
 
-    print('\nCONFIG options present in Makefiles but not Kconfig (SPL):')
-    not_found = check_not_found(all_uses, MODE_SPL)
-    show_uses(not_found)
-    spl_not_found |= set([is_not_proper(key) or key for key in not_found.keys()])
+        """
+        print(f'Creating {len(cfg_dict)} commits')
+        for cfg, uses in cfg_dict.items():
+            print(f'Processing {cfg}')
+            for use in uses:
+                #print(f'   {use.fname}: {use}')
+                replace_in_file(use.fname, use)
+            plural = 's' if len(uses) > 1 else ''
+            msg = f'Correct SPL use{plural} of {cfg}'
+            msg += f'\n\nThis converts {len(uses)} usage{plural} '
+            msg += 'of this option to the non-SPL form, since there is\n'
+            msg += f'no SPL_{cfg} defined in Kconfig'
+            if not write_commit(msg):
+                print('failed\n', uses)
+                break
 
-    print('\nCONFIG options used as Proper in Makefiles but without a non-SPL_ variant:')
-    not_found = check_not_found(all_uses, MODE_PROPER)
-    show_uses(not_found)
-    proper_not_found |= set([key for key in not_found.keys()])
+    print('Scanning Kconfig')
+    kconf = KconfigScanner().conf
 
-    # Scan the source code
-    all_uses, fname_uses = scan_src_files(src_list)
+    all_uses = collections.defaultdict(list)
+    fname_uses = {}
+    mk_dict, src_dict = do_scan()
+
+    # Scan the Makefiles
+    all_uses, fname_uses = scan_makefiles(mk_dict)
+    scan_src_files(src_dict, all_uses, fname_uses)
 
-    # Make sure we know about all the options
-    print('\nCONFIG options present in source but not Kconfig:')
-    not_found = check_not_found(all_uses, MODE_NORMAL)
-    show_uses(not_found)
+    conflicts, cfg_dict = check_conflict(kconf, all_uses, show_conflicts)
 
-    print('\nCONFIG options present in source but not Kconfig (SPL):')
-    not_found = check_not_found(all_uses, MODE_SPL)
-    show_uses(not_found)
-    spl_not_found |= set([is_not_proper(key) or key for key in not_found.keys()])
+    if do_update:
+        update_source(conflicts)
+    if do_commit:
+        create_commits(cfg_dict)
 
-    print('\nCONFIG options used as Proper in source but without a non-SPL_ variant:')
-    not_found = check_not_found(all_uses, MODE_PROPER)
-    show_uses(not_found)
-    proper_not_found |= set([key for key in not_found.keys()])
+    # Look for CONFIG options that are not found
+    spl_not_found = set()
+    proper_not_found = set()
+    check_mk_missing(all_uses, spl_not_found, proper_not_found)
 
-    print('\nCONFIG options used as SPL but without an SPL_ variant:')
-    for item in sorted(spl_not_found):
-        print(f'   {item}')
+    # Scan the source code
+    scan_src_files(src_dict, all_uses, fname_uses)
+    check_src_missing(all_uses, spl_not_found, proper_not_found)
 
-    print('\nCONFIG options used as Proper but without a non-SPL_ variant:')
-    for item in sorted(proper_not_found):
-        print(f'   {item}')
+    show_summary(spl_not_found, proper_not_found)
 
     # Write out the updated information
     if do_update:
-        with open(os.path.join(path, 'scripts', 'conf_nospl'), 'w') as out:
-            print('# These options should not be enabled in SPL builds\n',
-                  file=out)
-            for item in sorted(spl_not_found):
-                print(item, file=out)
-        with open(os.path.join(path, 'scripts', 'conf_noproper'), 'w') as out:
-            print('# These options should not be enabled in Proper builds\n',
-                  file=out)
-            for item in sorted(proper_not_found):
-                print(item, file=out)
-
+        write_update(spl_not_found, proper_not_found)
 
 def main():
     try:
@@ -1957,9 +2268,11 @@ doc/develop/moveconfig.rst for documentation.'''
     parser.add_argument('-i', '--imply', action='store_true', default=False,
                       help='find options which imply others')
     parser.add_argument('-I', '--imply-flags', type=str, default='',
-                      help="control the -i option ('help' for help")
+                      help="control the -i option ('help' for help)")
     parser.add_argument('-j', '--jobs', type=int, default=cpu_count,
                       help='the number of jobs to run simultaneously')
+    parser.add_argument('-l', '--list-problems', action='store_true',
+                        help='list problems found with --scan-source')
     parser.add_argument('-n', '--dry-run', action='store_true', default=False,
                       help='perform a trial run (show log with no changes)')
     parser.add_argument('-r', '--git-ref', type=str,
@@ -1991,7 +2304,8 @@ doc/develop/moveconfig.rst for documentation.'''
         unittest.main()
 
     if args.scan_source:
-        do_scan_source(os.getcwd(), args.update)
+        do_scan_source(os.getcwd(), args.update, args.list_problems,
+                       args.commit)
         return
 
     if not any((len(configs), args.force_sync, args.build_db, args.imply,
@@ -2049,7 +2363,6 @@ doc/develop/moveconfig.rst for documentation.'''
         cleanup_readme(configs, args)
 
     if args.commit:
-        subprocess.call(['git', 'add', '-u'])
         if configs:
             msg = 'Convert %s %sto Kconfig' % (configs[0],
                     'et al ' if len(configs) > 1 else '')
@@ -2058,7 +2371,7 @@ doc/develop/moveconfig.rst for documentation.'''
         else:
             msg = 'configs: Resync with savedefconfig'
             msg += '\n\nRsync all defconfig files using moveconfig.py'
-        subprocess.call(['git', 'commit', '-s', '-m', msg])
+        write_commit(msg)
 
     if args.build_db:
         with open(CONFIG_DATABASE, 'w', encoding='utf-8') as fd:
-- 
2.39.2.637.g21b0678d19-goog


  parent reply	other threads:[~2023-02-22 16:48 UTC|newest]

Thread overview: 94+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-22 16:33 [PATCH v5 00/44] More tidy-ups of Kconfig options Simon Glass
2023-02-22 16:33 ` [PATCH v5 01/44] mtd: Drop unused kb9202_nand driver Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 02/44] mtd: Drop unused CONFIG_ONENAND_U_BOOT Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 03/44] sh4: Drop unused twl6030 driver Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` Simon Glass [this message]
2023-02-22 16:33 ` [PATCH v5 05/44] bootstd: Disable QFW bootmeth in SPL Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 06/44] Correct SPL uses of ARCH_MVEBU Simon Glass
2023-02-22 16:33 ` [PATCH v5 07/44] Correct SPL uses of DISPLAY_AER_FULL Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 08/44] Correct SPL uses of MULTIPLEXER Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 09/44] Correct SPL use of PG_WCOM_UBOOT_UPDATE_SUPPORTED Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 10/44] Correct SPL uses of PHY_FIXED Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 11/44] boot: Add Kconfigs for BOOTMETH_VBE_REQUEST Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 12/44] Correct SPL use of DM_RNG Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 13/44] lib: Add a Kconfig for SPL_BZIP2 Simon Glass
2023-03-03 23:41   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 14/44] moveconfig: Various minor improvements Simon Glass
2023-02-22 16:33 ` [PATCH v5 15/44] sandbox: Expand size for VPL image Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 16/44] event: Add Kconfig options for SPL Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 17/44] bootstd: Correct 'VPL' typo Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:33 ` [PATCH v5 18/44] env: Avoid checking ENV_IS_IN when env disabled Simon Glass
2023-03-02 17:25   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 19/44] env: Allow VPL environment to be nowhere Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 20/44] lib: Add VPL options for SHA1 and SHA256 Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 21/44] x86: Use string functions for all 32-bit builds Simon Glass
2023-03-02 20:37   ` Tom Rini
2023-03-15 14:05     ` Simon Glass
2023-02-22 16:34 ` [PATCH v5 22/44] lib: Fix build condition for tiny-printf Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 23/44] sandbox: Tidy up RTC options Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 24/44] sandbox: Use the generic VPL option to enable VPL Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 25/44] sandbox: Tidy up I2C options Simon Glass
2023-02-23  5:43   ` Heiko Schocher
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 26/44] fixdep: Add support for VPL Simon Glass
2023-02-22 16:34 ` [PATCH v5 27/44] fixdep: Refactor to make testing easier Simon Glass
2023-02-22 16:34 ` [PATCH v5 28/44] fixdep: Add some tests for parse_config_line() Simon Glass
2023-02-22 16:34 ` [PATCH v5 29/44] test: Add SPL versions of the TEST_KCONFIG options Simon Glass
2023-02-22 16:34 ` [PATCH v5 30/44] lib: Add an SPL config for LIB_UUID Simon Glass
2023-02-23  6:14   ` Heinrich Schuchardt
2023-02-23 23:07     ` Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 31/44] test: Tidy up sandbox handling in test-main Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 32/44] x86: Fix up use of X86_32BIT_INIT and X86_64 options Simon Glass
2023-03-03 14:50   ` Tom Rini
2023-03-06 17:53     ` Simon Glass
2023-02-22 16:34 ` [PATCH v5 33/44] Add VPL options for BLOBLIST Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 34/44] rockchip: Avoid checking environment without ENV_SUPPORT Simon Glass
2023-02-23 16:16   ` Quentin Schulz
2023-02-22 16:34 ` [PATCH v5 35/44] freescale: Drop old pre-DM_ETH code Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 36/44] imx: Use SATA instead of CMD_SATA Simon Glass
2023-02-22 16:43   ` Baruch Siach
2023-02-22 19:16     ` Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 37/44] net: Add an SPL config for atheros Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 38/44] freescale: Fix odd use of ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE Simon Glass
2023-03-02 20:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 39/44] serial: Support ns16550 driver in TPL Simon Glass
2023-03-03 23:42   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 40/44] dm: Add a TPL symbol for simple-bus Simon Glass
2023-03-03 23:43   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 41/44] x86: coral: Add missing TPL options Simon Glass
2023-03-03 23:43   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 42/44] power: wandboard: Add a missing CONFIG Simon Glass
2023-03-03 23:43   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 43/44] venice: Simplify conditions for network init Simon Glass
2023-03-03 23:43   ` Tom Rini
2023-02-22 16:34 ` [PATCH v5 44/44] command: Don't allow commands in SPL Simon Glass
2023-03-02 20:30   ` Tom Rini
2023-03-03 23:43   ` Tom Rini
2023-03-03 23:43 ` [PATCH v5 00/44] More tidy-ups of Kconfig options Tom Rini
2023-03-06 18:20   ` Simon Glass
2023-03-11 15:28     ` Tom Rini
2023-03-11 19:34       ` Simon Glass

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230222163425.2043934-5-sjg@chromium.org \
    --to=sjg@chromium.org \
    --cc=alpernebiyasak@gmail.com \
    --cc=trini@konsulko.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.