Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

checkkconfigsymbols.py: make it Git aware

The script now supports to check a specified commit or a specified range
of commits (i.e., commit1..commit2). Developers and maintainers are
encouraged to use this functionality before sending or merging patches
to avoid potential bugs and to keep the code, documentation, etc. clean.

This patch adds the following options to the script:

-c COMMIT, --commit=COMMIT
Check if the specified commit (hash) introduces
undefined Kconfig symbols.

-d DIFF, --diff=DIFF
Diff undefined symbols between two commits. The input
format bases on Git log's 'commmit1..commit2'.

--force Reset current Git tree even when it's dirty.

Note that the first two options require to 'git reset --hard' the user's
Git tree. This hard reset is necessary to keep the script fast, but it
can lead to the loss of uncommitted data. Hence, the script aborts in
case it is executed in a dirty tree. It won't abort if '--force' is
passed.

If neither -c nor -d is specified, the script defaults to check the
entire local tree (i.e., the previous behavior).

Signed-off-by: Valentin Rothberg <valentinrothberg@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Valentin Rothberg and committed by
Greg Kroah-Hartman
b1a3f243 da2ff527

+132 -6
+132 -6
scripts/checkkconfigsymbols.py
··· 1 1 #!/usr/bin/env python 2 2 3 - """Find Kconfig identifiers that are referenced but not defined.""" 3 + """Find Kconfig symbols that are referenced but not defined.""" 4 4 5 5 # (c) 2014-2015 Valentin Rothberg <Valentin.Rothberg@lip6.fr> 6 6 # (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de> ··· 10 10 11 11 import os 12 12 import re 13 + import sys 13 14 from subprocess import Popen, PIPE, STDOUT 15 + from optparse import OptionParser 14 16 15 17 16 18 # regex expressions ··· 34 32 REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$") 35 33 36 34 35 + def parse_options(): 36 + """The user interface of this module.""" 37 + usage = "%prog [options]\n\n" \ 38 + "Run this tool to detect Kconfig symbols that are referenced but " \ 39 + "not defined in\nKconfig. The output of this tool has the " \ 40 + "format \'Undefined symbol\\tFile list\'\n\n" \ 41 + "If no option is specified, %prog will default to check your\n" \ 42 + "current tree. Please note that specifying commits will " \ 43 + "\'git reset --hard\'\nyour current tree! You may save " \ 44 + "uncommitted changes to avoid losing data." 45 + 46 + parser = OptionParser(usage=usage) 47 + 48 + parser.add_option('-c', '--commit', dest='commit', action='store', 49 + default="", 50 + help="Check if the specified commit (hash) introduces " 51 + "undefined Kconfig symbols.") 52 + 53 + parser.add_option('-d', '--diff', dest='diff', action='store', 54 + default="", 55 + help="Diff undefined symbols between two commits. The " 56 + "input format bases on Git log's " 57 + "\'commmit1..commit2\'.") 58 + 59 + parser.add_option('', '--force', dest='force', action='store_true', 60 + default=False, 61 + help="Reset current Git tree even when it's dirty.") 62 + 63 + (opts, _) = parser.parse_args() 64 + 65 + if opts.commit and opts.diff: 66 + sys.exit("Please specify only one option at once.") 67 + 68 + if opts.diff and not re.match(r"^[\w\-\.]+\.\.[\w\-\.]+$", opts.diff): 69 + sys.exit("Please specify valid input in the following format: " 70 + "\'commmit1..commit2\'") 71 + 72 + if opts.commit or opts.diff: 73 + if not opts.force and tree_is_dirty(): 74 + sys.exit("The current Git tree is dirty (see 'git status'). " 75 + "Running this script may\ndelete important data since it " 76 + "calls 'git reset --hard' for some performance\nreasons. " 77 + " Please run this script in a clean Git tree or pass " 78 + "'--force' if you\nwant to ignore this warning and " 79 + "continue.") 80 + 81 + return opts 82 + 83 + 37 84 def main(): 38 85 """Main function of this module.""" 86 + opts = parse_options() 87 + 88 + if opts.commit or opts.diff: 89 + head = get_head() 90 + 91 + # get commit range 92 + commit_a = None 93 + commit_b = None 94 + if opts.commit: 95 + commit_a = opts.commit + "~" 96 + commit_b = opts.commit 97 + elif opts.diff: 98 + split = opts.diff.split("..") 99 + commit_a = split[0] 100 + commit_b = split[1] 101 + undefined_a = {} 102 + undefined_b = {} 103 + 104 + # get undefined items before the commit 105 + execute("git reset --hard %s" % commit_a) 106 + undefined_a = check_symbols() 107 + 108 + # get undefined items for the commit 109 + execute("git reset --hard %s" % commit_b) 110 + undefined_b = check_symbols() 111 + 112 + # report cases that are present for the commit but not before 113 + for feature in undefined_b: 114 + # feature has not been undefined before 115 + if not feature in undefined_a: 116 + files = undefined_b.get(feature) 117 + print "%s\t%s" % (feature, ", ".join(files)) 118 + # check if there are new files that reference the undefined feature 119 + else: 120 + files = undefined_b.get(feature) - undefined_a.get(feature) 121 + if files: 122 + print "%s\t%s" % (feature, ", ".join(files)) 123 + 124 + # reset to head 125 + execute("git reset --hard %s" % head) 126 + 127 + # default to check the entire tree 128 + else: 129 + undefined = check_symbols() 130 + for feature in undefined: 131 + files = undefined.get(feature) 132 + 133 + 134 + def execute(cmd): 135 + """Execute %cmd and return stdout. Exit in case of error.""" 136 + pop = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=True) 137 + (stdout, _) = pop.communicate() # wait until finished 138 + if pop.returncode != 0: 139 + sys.exit(stdout) 140 + return stdout 141 + 142 + 143 + def tree_is_dirty(): 144 + """Return true if the current working tree is dirty (i.e., if any file has 145 + been added, deleted, modified, renamed or copied but not committed).""" 146 + stdout = execute("git status --porcelain") 147 + for line in stdout: 148 + if re.findall(r"[URMADC]{1}", line[:2]): 149 + return True 150 + return False 151 + 152 + 153 + def get_head(): 154 + """Return commit hash of current HEAD.""" 155 + stdout = execute("git rev-parse HEAD") 156 + return stdout.strip('\n') 157 + 158 + 159 + def check_symbols(): 160 + """Find undefined Kconfig symbols and return a dict with the symbol as key 161 + and a list of referencing files as value.""" 39 162 source_files = [] 40 163 kconfig_files = [] 41 164 defined_features = set() 42 165 referenced_features = dict() # {feature: [files]} 43 166 44 167 # use 'git ls-files' to get the worklist 45 - pop = Popen("git ls-files", stdout=PIPE, stderr=STDOUT, shell=True) 46 - (stdout, _) = pop.communicate() # wait until finished 168 + stdout = execute("git ls-files") 47 169 if len(stdout) > 0 and stdout[-1] == "\n": 48 170 stdout = stdout[:-1] 49 171 ··· 188 62 for kfile in kconfig_files: 189 63 parse_kconfig_file(kfile, defined_features, referenced_features) 190 64 191 - print "Undefined symbol used\tFile list" 65 + undefined = {} # {feature: [files]} 192 66 for feature in sorted(referenced_features): 193 67 # filter some false positives 194 68 if feature == "FOO" or feature == "BAR" or \ ··· 199 73 # avoid false positives for kernel modules 200 74 if feature[:-len("_MODULE")] in defined_features: 201 75 continue 202 - files = referenced_features.get(feature) 203 - print "%s\t%s" % (feature, ", ".join(files)) 76 + undefined[feature] = referenced_features.get(feature) 77 + return undefined 204 78 205 79 206 80 def parse_source_file(sfile, referenced_features):