public inbox for u-boot@lists.denx.de
 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>
Subject: [PATCH v2 001/169] moveconfig: Update to detect / correct missing SPL Kconfigs
Date: Sun,  5 Feb 2023 15:35:47 -0700	[thread overview]
Message-ID: <20230205223836.231657-2-sjg@chromium.org> (raw)
In-Reply-To: <20230205223836.231657-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>
---

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.1.519.gcb327c4b5f-goog


  reply	other threads:[~2023-02-05 22:40 UTC|newest]

Thread overview: 72+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-05 22:35 [PATCH v2 000/169] Kconfig: More cleanup of CONFIG options Simon Glass
2023-02-05 22:35 ` Simon Glass [this message]
2023-02-05 22:35 ` [PATCH v2 002/169] x86: Correct Chrromebook typo Simon Glass
2023-02-05 22:35 ` [PATCH v2 003/169] boot: Add a Kconfig for SPL_FIT_CIPHER Simon Glass
2023-02-05 22:35 ` [PATCH v2 004/169] boot: Add a Kconfig for SPL_BOOTSTD_FULL Simon Glass
2023-02-06 15:14   ` Tom Rini
2023-02-06 17:19     ` Simon Glass
2023-02-06 18:32       ` Tom Rini
2023-02-06 19:05         ` Simon Glass
2023-02-05 22:35 ` [PATCH v2 005/169] boot: Add Kconfigs for BOOTMETH_VBE_REQUEST Simon Glass
2023-02-05 22:35 ` [PATCH v2 006/169] boot: Add a Kconfig for SPL_BOOTDEV_SPI_FLASH Simon Glass
2023-02-05 22:35 ` [PATCH v2 007/169] boot: Add a Kconfig for SPL_BOOTDEV_ETH Simon Glass
2023-02-05 22:35 ` [PATCH v2 008/169] dm: Add a Kconfig for SPL_DEVRES Simon Glass
2023-02-05 22:35 ` [PATCH v2 009/169] dm: Add a Kconfig for SPL_DM_HWSPINLOCK Simon Glass
2023-02-05 22:35 ` [PATCH v2 010/169] boot: Add a Kconfig for SPL_OF_LIVE Simon Glass
2023-02-05 22:35 ` [PATCH v2 011/169] cmd: Add a Kconfig for SPL_CRC32_VERIFY Simon Glass
2023-02-05 22:35 ` [PATCH v2 012/169] console: Add a Kconfig for SPL_CONSOLE_MUX et al Simon Glass
2023-02-05 22:35 ` [PATCH v2 013/169] button: Add a Kconfig for SPL_BUTTON Simon Glass
2023-02-05 22:36 ` [PATCH v2 014/169] lib: Add a Kconfig for SPL_ERRNO_STR Simon Glass
2023-02-05 22:36 ` [PATCH v2 015/169] iommu: Add a Kconfig for SPL_IOMMU Simon Glass
2023-02-05 22:36 ` [PATCH v2 016/169] pinctrl: Add a Kconfig for SPL_PINCTRL_ARMADA_38X Simon Glass
2023-02-05 22:36 ` [PATCH v2 017/169] virtio: Add a Kconfig for SPL_VIRTIO Simon Glass
2023-02-05 22:36 ` [PATCH v2 018/169] lib: Add a Kconfig for SPL_BZIP2 Simon Glass
2023-02-05 22:36 ` [PATCH v2 019/169] acpi: Add a Kconfig for SPL_ACPIGEN Simon Glass
2023-02-05 22:36 ` [PATCH v2 020/169] rockchip: Add a Kconfig for SPL_RESET_ROCKCHIP Simon Glass
2023-02-05 22:36 ` [PATCH v2 021/169] mediatek: Add a Kconfig for SPL_RESET_MEDIATEK Simon Glass
2023-02-05 22:36 ` [PATCH v2 022/169] Correct SPL use of A003399_NOR_WORKAROUND Simon Glass
2023-02-05 22:36 ` [PATCH v2 023/169] Correct SPL use of AHCI Simon Glass
2023-02-05 22:36 ` [PATCH v2 024/169] Correct SPL uses of ALLEYCAT_5 Simon Glass
2023-02-05 22:36 ` [PATCH v2 025/169] Correct SPL uses of ARCH_MVEBU Simon Glass
2023-02-09 17:23   ` Tom Rini
2023-02-10 12:22     ` Stefan Roese
2023-02-05 22:36 ` [PATCH v2 027/169] Correct SPL use of ARCH_ZYNQ Simon Glass
2023-02-05 22:36 ` [PATCH v2 028/169] Correct SPL use of ARM64 Simon Glass
2023-02-05 22:36 ` [PATCH v2 029/169] Correct SPL uses of ARMADA_3700 Simon Glass
2023-02-05 22:36 ` [PATCH v2 030/169] Correct SPL uses of ARMADA_8K Simon Glass
2023-02-05 22:36 ` [PATCH v2 031/169] Correct SPL use of ATMEL_PIO4 Simon Glass
2023-02-09 17:36   ` Tom Rini
2023-02-10  7:25     ` Eugen Hristev
2023-02-10 12:41       ` Tom Rini
2023-02-10 12:47         ` Eugen Hristev
2023-02-05 22:36 ` [PATCH v2 032/169] Correct SPL use of BNXT_ETH Simon Glass
2023-02-05 22:36 ` [PATCH v2 033/169] Correct SPL use of BOOTSTAGE_FDT Simon Glass
2023-02-05 22:36 ` [PATCH v2 034/169] Correct SPL uses of BOOTSTAGE_REPORT Simon Glass
2023-02-05 22:36 ` [PATCH v2 035/169] Correct SPL use of CMD_BCB Simon Glass
2023-02-05 22:36 ` [PATCH v2 037/169] Correct SPL use of CMD_BOOTI Simon Glass
2023-02-05 22:36 ` [PATCH v2 038/169] Correct SPL uses of CMD_BOOTM_PRE_LOAD Simon Glass
2023-02-05 22:36 ` [PATCH v2 039/169] Correct SPL use of CMD_BOOTZ Simon Glass
2023-02-05 22:36 ` [PATCH v2 040/169] Correct SPL uses of CMD_CLK Simon Glass
2023-02-05 22:36 ` [PATCH v2 041/169] Correct SPL uses of CMD_DHCP Simon Glass
2023-02-09 15:55   ` Tom Rini
2023-02-05 22:36 ` [PATCH v2 042/169] Correct SPL uses of CMD_EFICONFIG Simon Glass
2023-02-05 22:36 ` [PATCH v2 043/169] Correct SPL use of CMD_ERASEENV Simon Glass
2023-02-05 22:36 ` [PATCH v2 044/169] Correct SPL uses of CMD_FDT Simon Glass
2023-02-05 22:36 ` [PATCH v2 045/169] Correct SPL use of CMD_FRU Simon Glass
2023-02-05 22:36 ` [PATCH v2 046/169] Correct SPL use of CMD_HASH Simon Glass
2023-02-05 22:36 ` [PATCH v2 047/169] Correct SPL use of CMD_MBR Simon Glass
2023-02-05 22:36 ` [PATCH v2 048/169] Correct SPL uses of CMD_MMC Simon Glass
2023-02-05 22:36 ` [PATCH v2 049/169] Correct SPL use of CMD_NET Simon Glass
2023-02-05 22:36 ` [PATCH v2 050/169] Correct SPL use of CMD_NVEDIT_EFI Simon Glass
2023-02-05 22:36 ` [PATCH v2 051/169] Correct SPL uses of CMD_NVME Simon Glass
2023-02-05 22:36 ` [PATCH v2 052/169] Correct SPL use of CMD_PSTORE Simon Glass
2023-02-05 22:36 ` [PATCH v2 053/169] Correct SPL uses of CMD_PXE Simon Glass
2023-02-05 22:36 ` [PATCH v2 054/169] Correct SPL uses of CMD_SCSI Simon Glass
2023-02-05 22:36 ` [PATCH v2 055/169] Correct SPL uses of CMD_SF Simon Glass
2023-02-05 22:36 ` [PATCH v2 056/169] Correct SPL use of CMD_SHA1SUM Simon Glass
2023-02-05 22:36 ` [PATCH v2 057/169] Correct SPL use of CMD_STBOARD Simon Glass
2023-02-05 22:36 ` [PATCH v2 058/169] Correct SPL use of CMD_STM32PROG Simon Glass
2023-02-05 22:36 ` [PATCH v2 059/169] Correct SPL uses of CMD_TFTPPUT Simon Glass
2023-02-05 22:36 ` [PATCH v2 060/169] Correct SPL uses of CMD_USB Simon Glass
2023-02-05 22:36 ` [PATCH v2 061/169] Correct SPL use of CMD_VIRTIO Simon Glass
2023-02-10 15:17 ` [PATCH v2 000/169] Kconfig: More cleanup of CONFIG options Tom Rini

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=20230205223836.231657-2-sjg@chromium.org \
    --to=sjg@chromium.org \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox