These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / scripts / checkkconfigsymbols.py
index 74086a5..2f4b7ff 100755 (executable)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 
 """Find Kconfig symbols that are referenced but not defined."""
 
-# (c) 2014-2015 Valentin Rothberg <Valentin.Rothberg@lip6.fr>
+# (c) 2014-2015 Valentin Rothberg <valentinrothberg@gmail.com>
 # (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
 #
 # Licensed under the terms of the GNU GPL License version 2
@@ -20,18 +20,20 @@ OPERATORS = r"&|\(|\)|\||\!"
 FEATURE = r"(?:\w*[A-Z0-9]\w*){2,}"
 DEF = r"^\s*(?:menu){,1}config\s+(" + FEATURE + r")\s*"
 EXPR = r"(?:" + OPERATORS + r"|\s|" + FEATURE + r")+"
-STMT = r"^\s*(?:if|select|depends\s+on)\s+" + EXPR
+DEFAULT = r"default\s+.*?(?:if\s.+){,1}"
+STMT = r"^\s*(?:if|select|depends\s+on|(?:" + DEFAULT + r"))\s+" + EXPR
 SOURCE_FEATURE = r"(?:\W|\b)+[D]{,1}CONFIG_(" + FEATURE + r")"
 
 # regex objects
 REGEX_FILE_KCONFIG = re.compile(r".*Kconfig[\.\w+\-]*$")
-REGEX_FEATURE = re.compile(r"(" + FEATURE + r")")
+REGEX_FEATURE = re.compile(r'(?!\B"[^"]*)' + FEATURE + r'(?![^"]*"\B)')
 REGEX_SOURCE_FEATURE = re.compile(SOURCE_FEATURE)
 REGEX_KCONFIG_DEF = re.compile(DEF)
 REGEX_KCONFIG_EXPR = re.compile(EXPR)
 REGEX_KCONFIG_STMT = re.compile(STMT)
 REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$")
 REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$")
+REGEX_NUMERIC = re.compile(r"0[xX][0-9a-fA-F]+|[0-9]+")
 
 
 def parse_options():
@@ -58,6 +60,17 @@ def parse_options():
                            "input format bases on Git log's "
                            "\'commmit1..commit2\'.")
 
+    parser.add_option('-f', '--find', dest='find', action='store_true',
+                      default=False,
+                      help="Find and show commits that may cause symbols to be "
+                           "missing.  Required to run with --diff.")
+
+    parser.add_option('-i', '--ignore', dest='ignore', action='store',
+                      default="",
+                      help="Ignore files matching this pattern.  Note that "
+                           "the pattern needs to be a Python regex.  To "
+                           "ignore defconfigs, specify -i '.*defconfig'.")
+
     parser.add_option('', '--force', dest='force', action='store_true',
                       default=False,
                       help="Reset current Git tree even when it's dirty.")
@@ -80,6 +93,15 @@ def parse_options():
                      "'--force' if you\nwant to ignore this warning and "
                      "continue.")
 
+    if opts.commit:
+        opts.find = False
+
+    if opts.ignore:
+        try:
+            re.match(opts.ignore, "this/is/just/a/test.c")
+        except:
+            sys.exit("Please specify a valid Python regex.")
+
     return opts
 
 
@@ -105,34 +127,54 @@ def main():
 
         # get undefined items before the commit
         execute("git reset --hard %s" % commit_a)
-        undefined_a = check_symbols()
+        undefined_a = check_symbols(opts.ignore)
 
         # get undefined items for the commit
         execute("git reset --hard %s" % commit_b)
-        undefined_b = check_symbols()
+        undefined_b = check_symbols(opts.ignore)
 
         # report cases that are present for the commit but not before
         for feature in sorted(undefined_b):
             # feature has not been undefined before
             if not feature in undefined_a:
                 files = sorted(undefined_b.get(feature))
-                print "%s\t%s" % (feature, ", ".join(files))
+                print "%s\t%s" % (yel(feature), ", ".join(files))
+                if opts.find:
+                    commits = find_commits(feature, opts.diff)
+                    print red(commits)
             # check if there are new files that reference the undefined feature
             else:
                 files = sorted(undefined_b.get(feature) -
                                undefined_a.get(feature))
                 if files:
-                    print "%s\t%s" % (feature, ", ".join(files))
+                    print "%s\t%s" % (yel(feature), ", ".join(files))
+                    if opts.find:
+                        commits = find_commits(feature, opts.diff)
+                        print red(commits)
 
         # reset to head
         execute("git reset --hard %s" % head)
 
     # default to check the entire tree
     else:
-        undefined = check_symbols()
+        undefined = check_symbols(opts.ignore)
         for feature in sorted(undefined):
             files = sorted(undefined.get(feature))
-            print "%s\t%s" % (feature, ", ".join(files))
+            print "%s\t%s" % (yel(feature), ", ".join(files))
+
+
+def yel(string):
+    """
+    Color %string yellow.
+    """
+    return "\033[33m%s\033[0m" % string
+
+
+def red(string):
+    """
+    Color %string red.
+    """
+    return "\033[31m%s\033[0m" % string
 
 
 def execute(cmd):
@@ -144,6 +186,13 @@ def execute(cmd):
     return stdout
 
 
+def find_commits(symbol, diff):
+    """Find commits changing %symbol in the given range of %diff."""
+    commits = execute("git log --pretty=oneline --abbrev-commit -G %s %s"
+                      % (symbol, diff))
+    return commits
+
+
 def tree_is_dirty():
     """Return true if the current working tree is dirty (i.e., if any file has
     been added, deleted, modified, renamed or copied but not committed)."""
@@ -160,9 +209,10 @@ def get_head():
     return stdout.strip('\n')
 
 
-def check_symbols():
+def check_symbols(ignore):
     """Find undefined Kconfig symbols and return a dict with the symbol as key
-    and a list of referencing files as value."""
+    and a list of referencing files as value.  Files matching %ignore are not
+    checked for undefined symbols."""
     source_files = []
     kconfig_files = []
     defined_features = set()
@@ -185,10 +235,17 @@ def check_symbols():
             source_files.append(gitfile)
 
     for sfile in source_files:
+        if ignore and re.match(ignore, sfile):
+            # do not check files matching %ignore
+            continue
         parse_source_file(sfile, referenced_features)
 
     for kfile in kconfig_files:
-        parse_kconfig_file(kfile, defined_features, referenced_features)
+        if ignore and re.match(ignore, kfile):
+            # do not collect references for files matching %ignore
+            parse_kconfig_file(kfile, defined_features, dict())
+        else:
+            parse_kconfig_file(kfile, defined_features, referenced_features)
 
     undefined = {}  # {feature: [files]}
     for feature in sorted(referenced_features):
@@ -259,6 +316,9 @@ def parse_kconfig_file(kfile, defined_features, referenced_features):
                 line = line.strip('\n')
                 features.extend(get_features_in_line(line))
             for feature in set(features):
+                if REGEX_NUMERIC.match(feature):
+                    # ignore numeric values
+                    continue
                 paths = referenced_features.get(feature, set())
                 paths.add(kfile)
                 referenced_features[feature] = paths